diff options
author | Matt Clay <mclay@redhat.com> | 2020-02-27 16:05:47 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-27 16:05:47 -0800 |
commit | f735fd672a783638b8187e61569386ef27410ff7 (patch) | |
tree | dcc4c0d7ce029a970fdce2a0143f05587b033ae6 /test/integration | |
parent | da30e6d2e1efa411b26e6a48e5d7f7cea644bbef (diff) | |
download | ansible-f735fd672a783638b8187e61569386ef27410ff7.tar.gz |
Third batch of incidental integration tests. (#67830)
* Copy in incidental windows tests.
* Update incidental test aliases.
* Add support plugins.
* Update target references.
* Update sanity ignores.
* Update integration-aliases test.
* Add to CI.
Diffstat (limited to 'test/integration')
65 files changed, 4032 insertions, 0 deletions
diff --git a/test/integration/targets/incidental_win_copy/aliases b/test/integration/targets/incidental_win_copy/aliases new file mode 100644 index 0000000000..a5fc90dcf4 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/aliases @@ -0,0 +1,2 @@ +shippable/windows/incidental +windows diff --git a/test/integration/targets/incidental_win_copy/defaults/main.yml b/test/integration/targets/incidental_win_copy/defaults/main.yml new file mode 100644 index 0000000000..5d8a1d2351 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/defaults/main.yml @@ -0,0 +1 @@ +test_win_copy_path: C:\ansible\win_copy .ÅÑŚÌβŁÈ [$!@^&test(;)] diff --git a/test/integration/targets/incidental_win_copy/files-different/vault/folder/nested-vault-file b/test/integration/targets/incidental_win_copy/files-different/vault/folder/nested-vault-file new file mode 100644 index 0000000000..d8d1549874 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files-different/vault/folder/nested-vault-file @@ -0,0 +1,6 @@ +$ANSIBLE_VAULT;1.1;AES256 +65653164323866373138353632323531393664393563633665373635623763353561386431373366 +3232353263363034313136663062623336663463373966320a333763323032646463386432626161 +36386330356637666362396661653935653064623038333031653335626164376465353235303636 +3335616231663838620a303632343938326538656233393562303162343261383465623261646664 +33613932343461626339333832363930303962633364303736376634396364643861 diff --git a/test/integration/targets/incidental_win_copy/files-different/vault/readme.txt b/test/integration/targets/incidental_win_copy/files-different/vault/readme.txt new file mode 100644 index 0000000000..dae883b5ee --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files-different/vault/readme.txt @@ -0,0 +1,5 @@ +This directory contains some files that have been encrypted with ansible-vault. + +This is to test out the decrypt parameter in win_copy. + +The password is: password diff --git a/test/integration/targets/incidental_win_copy/files-different/vault/vault-file b/test/integration/targets/incidental_win_copy/files-different/vault/vault-file new file mode 100644 index 0000000000..2fff7619a7 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files-different/vault/vault-file @@ -0,0 +1,6 @@ +$ANSIBLE_VAULT;1.1;AES256 +30353665333635633433356261616636356130386330363962386533303566313463383734373532 +3933643234323638623939613462346361313431363939370a303532656338353035346661353965 +34656231633238396361393131623834316262306533663838336362366137306562646561383766 +6363373965633337640a373666336461613337346131353564383134326139616561393664663563 +3431 diff --git a/test/integration/targets/incidental_win_copy/files/empty.txt b/test/integration/targets/incidental_win_copy/files/empty.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files/empty.txt diff --git a/test/integration/targets/incidental_win_copy/files/foo.txt b/test/integration/targets/incidental_win_copy/files/foo.txt new file mode 100644 index 0000000000..7c6ded14ec --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files/foo.txt @@ -0,0 +1 @@ +foo.txt diff --git a/test/integration/targets/incidental_win_copy/files/subdir/bar.txt b/test/integration/targets/incidental_win_copy/files/subdir/bar.txt new file mode 100644 index 0000000000..76018072e0 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files/subdir/bar.txt @@ -0,0 +1 @@ +baz diff --git a/test/integration/targets/incidental_win_copy/files/subdir/subdir2/baz.txt b/test/integration/targets/incidental_win_copy/files/subdir/subdir2/baz.txt new file mode 100644 index 0000000000..76018072e0 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files/subdir/subdir2/baz.txt @@ -0,0 +1 @@ +baz diff --git a/test/integration/targets/incidental_win_copy/files/subdir/subdir2/subdir3/subdir4/qux.txt b/test/integration/targets/incidental_win_copy/files/subdir/subdir2/subdir3/subdir4/qux.txt new file mode 100644 index 0000000000..78df5b06bd --- /dev/null +++ b/test/integration/targets/incidental_win_copy/files/subdir/subdir2/subdir3/subdir4/qux.txt @@ -0,0 +1 @@ +qux
\ No newline at end of file diff --git a/test/integration/targets/incidental_win_copy/tasks/main.yml b/test/integration/targets/incidental_win_copy/tasks/main.yml new file mode 100644 index 0000000000..b2ee103fd0 --- /dev/null +++ b/test/integration/targets/incidental_win_copy/tasks/main.yml @@ -0,0 +1,34 @@ +--- +- name: create empty folder + file: + path: '{{role_path}}/files/subdir/empty' + state: directory + delegate_to: localhost + +# removes the cached zip module from the previous task so we can replicate +# the below issue where win_copy would delete DEFAULT_LOCAL_TMP if it +# had permission to +# https://github.com/ansible/ansible/issues/35613 +- name: clear the local ansiballz cache + file: + path: "{{lookup('config', 'DEFAULT_LOCAL_TMP')}}/ansiballz_cache" + state: absent + delegate_to: localhost + +- name: create test folder + win_file: + path: '{{test_win_copy_path}}' + state: directory + +- block: + - name: run tests for local to remote + include_tasks: tests.yml + + - name: run tests for remote to remote + include_tasks: remote_tests.yml + + always: + - name: remove test folder + win_file: + path: '{{test_win_copy_path}}' + state: absent diff --git a/test/integration/targets/incidental_win_copy/tasks/remote_tests.yml b/test/integration/targets/incidental_win_copy/tasks/remote_tests.yml new file mode 100644 index 0000000000..5abb50200b --- /dev/null +++ b/test/integration/targets/incidental_win_copy/tasks/remote_tests.yml @@ -0,0 +1,471 @@ +--- +- name: fail when source does not exist remote + win_copy: + src: fakesource + dest: fakedest + remote_src: yes + register: fail_remote_invalid_source + failed_when: "'it does not exist' not in fail_remote_invalid_source.msg" + +- name: setup source folder for remote tests + win_copy: + src: files/ + dest: '{{test_win_copy_path}}\source\' + +- name: setup remote failure tests + win_file: + path: '{{item.path}}' + state: '{{item.state}}' + with_items: + - { 'path': '{{test_win_copy_path}}\target\folder', 'state': 'directory' } + - { 'path': '{{test_win_copy_path}}\target\file', 'state': 'touch' } + - { 'path': '{{test_win_copy_path}}\target\subdir', 'state': 'touch' } + +- name: fail source is a file but dest is a folder + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\folder' + remote_src: yes + register: fail_remote_file_to_folder + failed_when: "'dest is already a folder' not in fail_remote_file_to_folder.msg" + +- name: fail source is a file but dest is a folder + win_copy: + src: '{{test_win_copy_path}}\source\' + dest: '{{test_win_copy_path}}\target\' + remote_src: yes + register: fail_remote_folder_to_file + failed_when: "'dest is already a file' not in fail_remote_folder_to_file.msg" + +- name: fail source is a file dest parent dir is also a file + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\file\foo.txt' + remote_src: yes + register: fail_remote_file_parent_dir_file + failed_when: "'is currently a file' not in fail_remote_file_parent_dir_file.msg" + +- name: fail source is a folder dest parent dir is also a file + win_copy: + src: '{{test_win_copy_path}}\source\subdir' + dest: '{{test_win_copy_path}}\target\file' + remote_src: yes + register: fail_remote_folder_parent_dir_file + failed_when: "'object at dest parent dir is not a folder' not in fail_remote_folder_parent_dir_file.msg" + +- name: fail to copy a remote file with parent dir that doesn't exist and filename is set + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\missing-dir\foo.txt' + remote_src: yes + register: fail_remote_missing_parent_dir + failed_when: "'does not exist' not in fail_remote_missing_parent_dir.msg" + +- name: remove target after remote failure tests + win_file: + path: '{{test_win_copy_path}}\target' + state: absent + +- name: create remote target after cleaning + win_file: + path: '{{test_win_copy_path}}\target' + state: directory + +- name: copy single file remote (check mode) + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\foo-target.txt' + remote_src: yes + register: remote_copy_file_check + check_mode: yes + +- name: get result of copy single file remote (check mode) + win_stat: + path: '{{test_win_copy_path}}\target\foo-target.txt' + register: remote_copy_file_actual_check + +- name: assert copy single file remote (check mode) + assert: + that: + - remote_copy_file_check is changed + - remote_copy_file_actual_check.stat.exists == False + +- name: copy single file remote + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\foo-target.txt' + remote_src: yes + register: remote_copy_file + +- name: get result of copy single file remote + win_stat: + path: '{{test_win_copy_path}}\target\foo-target.txt' + register: remote_copy_file_actual + +- name: assert copy single file remote + assert: + that: + - remote_copy_file is changed + - remote_copy_file.operation == 'file_copy' + - remote_copy_file.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - remote_copy_file.size == 8 + - remote_copy_file.original_basename == 'foo.txt' + - remote_copy_file_actual.stat.exists == True + - remote_copy_file_actual.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + +- name: copy single file remote (idempotent) + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\foo-target.txt' + remote_src: yes + register: remote_copy_file_again + +- name: assert copy single file remote (idempotent) + assert: + that: + - remote_copy_file_again is not changed + +- name: copy single file into folder remote (check mode) + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\' + remote_src: yes + register: remote_copy_file_to_folder_check + check_mode: yes + +- name: get result of copy single file into folder remote (check mode) + win_stat: + path: '{{test_win_copy_path}}\target\foo.txt' + register: remote_copy_file_to_folder_actual_check + +- name: assert copy single file into folder remote (check mode) + assert: + that: + - remote_copy_file_to_folder_check is changed + - remote_copy_file_to_folder_actual_check.stat.exists == False + +- name: copy single file into folder remote + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\' + remote_src: yes + register: remote_copy_file_to_folder + +- name: get result of copy single file into folder remote + win_stat: + path: '{{test_win_copy_path}}\target\foo.txt' + register: remote_copy_file_to_folder_actual + +- name: assert copy single file into folder remote + assert: + that: + - remote_copy_file_to_folder is changed + - remote_copy_file_to_folder.operation == 'file_copy' + - remote_copy_file_to_folder.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - remote_copy_file_to_folder.size == 8 + - remote_copy_file_to_folder.original_basename == 'foo.txt' + - remote_copy_file_to_folder_actual.stat.exists == True + - remote_copy_file_to_folder_actual.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + +- name: copy single file into folder remote (idempotent) + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\' + remote_src: yes + register: remote_copy_file_to_folder_again + +- name: assert copy single file into folder remote + assert: + that: + - remote_copy_file_to_folder_again is not changed + +- name: copy single file to missing folder (check mode) + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\missing\' + remote_src: yes + register: remote_copy_file_to_missing_folder_check + check_mode: yes + +- name: get result of copy single file to missing folder remote (check mode) + win_stat: + path: '{{test_win_copy_path}}\target\missing\foo.txt' + register: remote_copy_file_to_missing_folder_actual_check + +- name: assert copy single file to missing folder remote (check mode) + assert: + that: + - remote_copy_file_to_missing_folder_check is changed + - remote_copy_file_to_missing_folder_check.operation == 'file_copy' + - remote_copy_file_to_missing_folder_actual_check.stat.exists == False + +- name: copy single file to missing folder remote + win_copy: + src: '{{test_win_copy_path}}\source\foo.txt' + dest: '{{test_win_copy_path}}\target\missing\' + remote_src: yes + register: remote_copy_file_to_missing_folder + +- name: get result of copy single file to missing folder remote + win_stat: + path: '{{test_win_copy_path}}\target\missing\foo.txt' + register: remote_copy_file_to_missing_folder_actual + +- name: assert copy single file to missing folder remote + assert: + that: + - remote_copy_file_to_missing_folder is changed + - remote_copy_file_to_missing_folder.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - remote_copy_file_to_missing_folder.operation == 'file_copy' + - remote_copy_file_to_missing_folder.size == 8 + - remote_copy_file_to_missing_folder_actual.stat.exists == True + - remote_copy_file_to_missing_folder_actual.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + +- name: clear target for folder to folder test + win_file: + path: '{{test_win_copy_path}}\target' + state: absent + +- name: copy folder to folder remote (check mode) + win_copy: + src: '{{test_win_copy_path}}\source' + dest: '{{test_win_copy_path}}\target' + remote_src: yes + register: remote_copy_folder_to_folder_check + check_mode: yes + +- name: get result of copy folder to folder remote (check mode) + win_stat: + path: '{{test_win_copy_path}}\target' + register: remote_copy_folder_to_folder_actual_check + +- name: assert copy folder to folder remote (check mode) + assert: + that: + - remote_copy_folder_to_folder_check is changed + - remote_copy_folder_to_folder_check.operation == 'folder_copy' + - remote_copy_folder_to_folder_actual_check.stat.exists == False + +- name: copy folder to folder remote + win_copy: + src: '{{test_win_copy_path}}\source' + dest: '{{test_win_copy_path}}\target' + remote_src: yes + register: remote_copy_folder_to_folder + +- name: get result of copy folder to folder remote + win_find: + paths: '{{test_win_copy_path}}\target' + recurse: yes + file_type: directory + register: remote_copy_folder_to_folder_actual + +- name: assert copy folder to folder remote + assert: + that: + - remote_copy_folder_to_folder is changed + - remote_copy_folder_to_folder.operation == 'folder_copy' + - remote_copy_folder_to_folder_actual.examined == 11 + - remote_copy_folder_to_folder_actual.matched == 6 + - remote_copy_folder_to_folder_actual.files[0].filename == 'source' + - remote_copy_folder_to_folder_actual.files[1].filename == 'subdir' + - remote_copy_folder_to_folder_actual.files[2].filename == 'empty' + - remote_copy_folder_to_folder_actual.files[3].filename == 'subdir2' + - remote_copy_folder_to_folder_actual.files[4].filename == 'subdir3' + - remote_copy_folder_to_folder_actual.files[5].filename == 'subdir4' + +- name: copy folder to folder remote (idempotent) + win_copy: + src: '{{test_win_copy_path}}\source' + dest: '{{test_win_copy_path}}\target' + remote_src: yes + register: remote_copy_folder_to_folder_again + +- name: assert copy folder to folder remote (idempotent) + assert: + that: + - remote_copy_folder_to_folder_again is not changed + +- name: change remote file after folder to folder test + win_copy: + content: bar.txt + dest: '{{test_win_copy_path}}\target\source\foo.txt' + +- name: remote remote folder after folder to folder test + win_file: + path: '{{test_win_copy_path}}\target\source\subdir\subdir2\subdir3\subdir4' + state: absent + +- name: copy folder to folder remote after change + win_copy: + src: '{{test_win_copy_path}}\source' + dest: '{{test_win_copy_path}}\target' + remote_src: yes + register: remote_copy_folder_to_folder_after_change + +- name: get result of copy folder to folder remote after change + win_find: + paths: '{{test_win_copy_path}}\target\source' + recurse: yes + patterns: ['foo.txt', 'qux.txt'] + register: remote_copy_folder_to_folder_after_change_actual + +- name: assert copy folder after changes + assert: + that: + - remote_copy_folder_to_folder_after_change is changed + - remote_copy_folder_to_folder_after_change_actual.matched == 2 + - remote_copy_folder_to_folder_after_change_actual.files[0].checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - remote_copy_folder_to_folder_after_change_actual.files[1].checksum == 'b54ba7f5621240d403f06815f7246006ef8c7d43' + +- name: clear target folder before folder contents to remote test + win_file: + path: '{{test_win_copy_path}}\target' + state: absent + +- name: copy folder contents to folder remote with backslash (check mode) + win_copy: + src: '{{test_win_copy_path}}\source\' + dest: '{{test_win_copy_path}}\target' + remote_src: yes + register: remote_copy_folder_content_backslash_check + check_mode: yes + +- name: get result of copy folder contents to folder remote with backslash (check mode) + win_stat: + path: '{{test_win_copy_path}}\target' + register: remote_copy_folder_content_backslash_actual_check + +- name: assert copy folder content to folder remote with backslash (check mode) + assert: + that: + - remote_copy_folder_content_backslash_check is changed + - remote_copy_folder_content_backslash_actual_check.stat.exists == False + +- name: copy folder contents to folder remote with backslash + win_copy: + src: '{{test_win_copy_path}}\source\' + dest: '{{test_win_copy_path}}\target' + remote_src: yes + register: remote_copy_folder_content_backslash + +- name: get result of copy folder contents to folder remote with backslash + win_find: + paths: '{{test_win_copy_path}}\target' + recurse: yes + file_type: directory + register: remote_copy_folder_content_backslash_actual + +- name: assert copy folder content to folder remote with backslash + assert: + that: + - remote_copy_folder_content_backslash is changed + - remote_copy_folder_content_backslash.operation == 'folder_copy' + - remote_copy_folder_content_backslash_actual.examined == 10 + - remote_copy_folder_content_backslash_actual.matched == 5 + - remote_copy_folder_content_backslash_actual.files[0].filename == 'subdir' + - remote_copy_folder_content_backslash_actual.files[1].filename == 'empty' + - remote_copy_folder_content_backslash_actual.files[2].filename == 'subdir2' + - remote_copy_folder_content_backslash_actual.files[3].filename == 'subdir3' + - remote_copy_folder_content_backslash_actual.files[4].filename == 'subdir4' + +- name: copy folder contents to folder remote with backslash (idempotent) + win_copy: + src: '{{test_win_copy_path}}\source\' + dest: '{{test_win_copy_path}}\target' + remote_src: yes + register: remote_copy_folder_content_backslash_again + +- name: assert copy folder content to folder remote with backslash (idempotent) + assert: + that: + - remote_copy_folder_content_backslash_again is not changed + +- name: change remote file after folder content to folder test + win_copy: + content: bar.txt + dest: '{{test_win_copy_path}}\target\foo.txt' + +- name: remote remote folder after folder content to folder test + win_file: + path: '{{test_win_copy_path}}\target\subdir\subdir2\subdir3\subdir4' + state: absent + +- name: copy folder content to folder remote after change + win_copy: + src: '{{test_win_copy_path}}/source/' + dest: '{{test_win_copy_path}}/target/' + remote_src: yes + register: remote_copy_folder_content_to_folder_after_change + +- name: get result of copy folder content to folder remote after change + win_find: + paths: '{{test_win_copy_path}}\target' + recurse: yes + patterns: ['foo.txt', 'qux.txt'] + register: remote_copy_folder_content_to_folder_after_change_actual + +- name: assert copy folder content to folder after changes + assert: + that: + - remote_copy_folder_content_to_folder_after_change is changed + - remote_copy_folder_content_to_folder_after_change_actual.matched == 2 + - remote_copy_folder_content_to_folder_after_change_actual.files[0].checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - remote_copy_folder_content_to_folder_after_change_actual.files[1].checksum == 'b54ba7f5621240d403f06815f7246006ef8c7d43' + +# https://github.com/ansible/ansible/issues/50077 +- name: create empty nested directory + win_file: + path: '{{ test_win_copy_path }}\source\empty-nested\nested-dir' + state: directory + +- name: copy empty nested directory (check mode) + win_copy: + src: '{{ test_win_copy_path }}\source\empty-nested' + dest: '{{ test_win_copy_path }}\target' + remote_src: True + check_mode: True + register: copy_empty_dir_check + +- name: get result of copy empty nested directory (check mode) + win_stat: + path: '{{ test_win_copy_path }}\target\empty-nested' + register: copy_empty_dir_actual_check + +- name: assert copy empty nested directory (check mode) + assert: + that: + - copy_empty_dir_check is changed + - copy_empty_dir_check.operation == "folder_copy" + - not copy_empty_dir_actual_check.stat.exists + +- name: copy empty nested directory + win_copy: + src: '{{ test_win_copy_path }}\source\empty-nested' + dest: '{{ test_win_copy_path }}\target' + remote_src: True + register: copy_empty_dir + +- name: get result of copy empty nested directory + win_stat: + path: '{{ test_win_copy_path }}\target\empty-nested\nested-dir' + register: copy_empty_dir_actual + +- name: assert copy empty nested directory + assert: + that: + - copy_empty_dir is changed + - copy_empty_dir.operation == "folder_copy" + - copy_empty_dir_actual.stat.exists + +- name: copy empty nested directory (idempotent) + win_copy: + src: '{{ test_win_copy_path }}\source\empty-nested' + dest: '{{ test_win_copy_path }}\target' + remote_src: True + register: copy_empty_dir_again + +- name: assert copy empty nested directory (idempotent) + assert: + that: + - not copy_empty_dir_again is changed diff --git a/test/integration/targets/incidental_win_copy/tasks/tests.yml b/test/integration/targets/incidental_win_copy/tasks/tests.yml new file mode 100644 index 0000000000..d15e71f65c --- /dev/null +++ b/test/integration/targets/incidental_win_copy/tasks/tests.yml @@ -0,0 +1,535 @@ +--- +- name: fail no source or content + win_copy: + dest: dest + register: fail_no_source_content + failed_when: fail_no_source_content.msg != 'src (or content) and dest are required' + +- name: fail content but dest isn't a file, unix ending + win_copy: + content: a + dest: a/ + register: fail_dest_not_file_unix + failed_when: fail_dest_not_file_unix.msg != 'dest must be a file if content is defined' + +- name: fail content but dest isn't a file, windows ending + win_copy: + content: a + dest: a\ + register: fail_dest_not_file_windows + failed_when: fail_dest_not_file_windows.msg != 'dest must be a file if content is defined' + +- name: fail to copy a file with parent dir that doesn't exist and filename is set + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\missing-dir\foo.txt' + register: fail_missing_parent_dir + failed_when: "'does not exist' not in fail_missing_parent_dir.msg" + +- name: fail to copy an encrypted file without the password set + win_copy: + src: '{{role_path}}/files-different/vault/vault-file' + dest: '{{test_win_copy_path}}\file' + register: fail_copy_encrypted_file + ignore_errors: yes # weird failed_when doesn't work in this case + +- name: assert failure message when copying an encrypted file without the password set + assert: + that: + - fail_copy_encrypted_file is failed + - fail_copy_encrypted_file.msg == 'A vault password or secret must be specified to decrypt {{role_path}}/files-different/vault/vault-file' + +- name: fail to copy a directory with an encrypted file without the password + win_copy: + src: '{{role_path}}/files-different/vault' + dest: '{{test_win_copy_path}}' + register: fail_copy_directory_with_enc_file + ignore_errors: yes + +- name: assert failure message when copying a directory that contains an encrypted file without the password set + assert: + that: + - fail_copy_directory_with_enc_file is failed + - fail_copy_directory_with_enc_file.msg == 'A vault password or secret must be specified to decrypt {{role_path}}/files-different/vault/vault-file' + +- name: copy with content (check mode) + win_copy: + content: a + dest: '{{test_win_copy_path}}\file' + register: copy_content_check + check_mode: yes + +- name: get result of copy with content (check mode) + win_stat: + path: '{{test_win_copy_path}}\file' + register: copy_content_actual_check + +- name: assert copy with content (check mode) + assert: + that: + - copy_content_check is changed + - copy_content_check.checksum == '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8' + - copy_content_check.operation == 'file_copy' + - copy_content_check.size == 1 + - copy_content_actual_check.stat.exists == False + +- name: copy with content + win_copy: + content: a + dest: '{{test_win_copy_path}}\file' + register: copy_content + +- name: get result of copy with content + win_stat: + path: '{{test_win_copy_path}}\file' + register: copy_content_actual + +- name: assert copy with content + assert: + that: + - copy_content is changed + - copy_content.checksum == '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8' + - copy_content.operation == 'file_copy' + - copy_content.size == 1 + - copy_content_actual.stat.exists == True + - copy_content_actual.stat.checksum == '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8' + +- name: copy with content (idempotent) + win_copy: + content: a + dest: '{{test_win_copy_path}}\file' + register: copy_content_again + +- name: assert copy with content (idempotent) + assert: + that: + - copy_content_again is not changed + +- name: copy with content change when missing + win_copy: + content: b + dest: '{{test_win_copy_path}}\file' + force: no + register: copy_content_when_missing + +- name: assert copy with content change when missing + assert: + that: + - copy_content_when_missing is not changed + +- name: copy single file (check mode) + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\foo-target.txt' + register: copy_file_check + check_mode: yes + +- name: get result of copy single file (check mode) + win_stat: + path: '{{test_win_copy_path}}\foo-target.txt' + register: copy_file_actual_check + +- name: assert copy single file (check mode) + assert: + that: + - copy_file_check is changed + - copy_file_check.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - copy_file_check.dest == test_win_copy_path + '\\foo-target.txt' + - copy_file_check.operation == 'file_copy' + - copy_file_check.size == 8 + - copy_file_actual_check.stat.exists == False + +- name: copy single file + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\foo-target.txt' + register: copy_file + +- name: get result of copy single file + win_stat: + path: '{{test_win_copy_path}}\foo-target.txt' + register: copy_file_actual + +- name: assert copy single file + assert: + that: + - copy_file is changed + - copy_file.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - copy_file.dest == test_win_copy_path + '\\foo-target.txt' + - copy_file.operation == 'file_copy' + - copy_file.size == 8 + - copy_file_actual.stat.exists == True + - copy_file_actual.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + +- name: copy single file (idempotent) + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\foo-target.txt' + register: copy_file_again + +- name: assert copy single file (idempotent) + assert: + that: + - copy_file_again is not changed + +- name: copy single file (backup) + win_copy: + content: "{{ lookup('file', 'foo.txt') }}\nfoo bar" + dest: '{{test_win_copy_path}}\foo-target.txt' + backup: yes + register: copy_file_backup + +- name: check backup_file + win_stat: + path: '{{ copy_file_backup.backup_file }}' + register: backup_file + +- name: assert copy single file (backup) + assert: + that: + - copy_file_backup is changed + - backup_file.stat.exists == true + +- name: copy single file to folder (check mode) + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\' + register: copy_file_to_folder_check + check_mode: yes + +- name: get result of copy single file to folder (check mode) + win_stat: + path: '{{test_win_copy_path}}\foo.txt' + register: copy_file_to_folder_actual_check + +- name: assert copy single file to folder (check mode) + assert: + that: + - copy_file_to_folder_check is changed + - copy_file_to_folder_check.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - copy_file_to_folder_check.dest == test_win_copy_path + '\\foo.txt' + - copy_file_to_folder_check.operation == 'file_copy' + - copy_file_to_folder_check.size == 8 + - copy_file_to_folder_actual_check.stat.exists == False + +- name: copy single file to folder + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\' + register: copy_file_to_folder + +- name: get result of copy single file to folder + win_stat: + path: '{{test_win_copy_path}}\foo.txt' + register: copy_file_to_folder_actual + +- name: assert copy single file to folder + assert: + that: + - copy_file_to_folder is changed + - copy_file_to_folder.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - copy_file_to_folder.dest == test_win_copy_path + '\\foo.txt' + - copy_file_to_folder.operation == 'file_copy' + - copy_file_to_folder.size == 8 + - copy_file_to_folder_actual.stat.exists == True + - copy_file_to_folder_actual.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + +- name: copy single file to folder (idempotent) + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\' + register: copy_file_to_folder_again + +- name: assert copy single file to folder (idempotent) + assert: + that: + - copy_file_to_folder_again is not changed + +- name: copy single file to missing folder (check mode) + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\missing\' + register: copy_file_to_missing_folder_check + check_mode: yes + +- name: get result of copy single file to missing folder (check mode) + win_stat: + path: '{{test_win_copy_path}}\missing\foo.txt' + register: copy_file_to_missing_folder_actual_check + +- name: assert copy single file to missing folder (check mode) + assert: + that: + - copy_file_to_missing_folder_check is changed + - copy_file_to_missing_folder_check.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - copy_file_to_missing_folder_check.operation == 'file_copy' + - copy_file_to_missing_folder_check.size == 8 + - copy_file_to_missing_folder_actual_check.stat.exists == False + +- name: copy single file to missing folder + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\missing\' + register: copy_file_to_missing_folder + +- name: get result of copy single file to missing folder + win_stat: + path: '{{test_win_copy_path}}\missing\foo.txt' + register: copy_file_to_missing_folder_actual + +- name: assert copy single file to missing folder + assert: + that: + - copy_file_to_missing_folder is changed + - copy_file_to_missing_folder.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - copy_file_to_missing_folder.operation == 'file_copy' + - copy_file_to_missing_folder.size == 8 + - copy_file_to_missing_folder_actual.stat.exists == True + - copy_file_to_missing_folder_actual.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + +- name: copy folder (check mode) + win_copy: + src: files + dest: '{{test_win_copy_path}}\recursive\folder' + register: copy_folder_check + check_mode: yes + +- name: get result of copy folder (check mode) + win_stat: + path: '{{test_win_copy_path}}\recursive\folder' + register: copy_folder_actual_check + +- name: assert copy folder (check mode) + assert: + that: + - copy_folder_check is changed + - copy_folder_check.operation == 'folder_copy' + - copy_folder_actual_check.stat.exists == False + +- name: copy folder + win_copy: + src: files + dest: '{{test_win_copy_path}}\recursive\folder' + register: copy_folder + +- name: get result of copy folder + win_find: + paths: '{{test_win_copy_path}}\recursive\folder' + recurse: yes + file_type: directory + register: copy_folder_actual + +- name: assert copy folder + assert: + that: + - copy_folder is changed + - copy_folder.operation == 'folder_copy' + - copy_folder_actual.examined == 11 # includes files and folders, the below is the nested order + - copy_folder_actual.matched == 6 + - copy_folder_actual.files[0].filename == 'files' + - copy_folder_actual.files[1].filename == 'subdir' + - copy_folder_actual.files[2].filename == 'empty' + - copy_folder_actual.files[3].filename == 'subdir2' + - copy_folder_actual.files[4].filename == 'subdir3' + - copy_folder_actual.files[5].filename == 'subdir4' + +- name: copy folder (idempotent) + win_copy: + src: files + dest: '{{test_win_copy_path}}\recursive\folder' + register: copy_folder_again + +- name: assert copy folder (idempotent) + assert: + that: + - copy_folder_again is not changed + +- name: change the text of a file in the remote source + win_copy: + content: bar.txt + dest: '{{test_win_copy_path}}\recursive\folder\files\foo.txt' + +- name: remove folder for test of recursive copy + win_file: + path: '{{test_win_copy_path}}\recursive\folder\files\subdir\subdir2\subdir3\subdir4' + state: absent + +- name: copy folder after changes + win_copy: + src: files + dest: '{{test_win_copy_path}}\recursive\folder' + register: copy_folder_after_change + +- name: get result of copy folder after changes + win_find: + paths: '{{test_win_copy_path}}\recursive\folder\files' + recurse: yes + patterns: ['foo.txt', 'qux.txt'] + register: copy_folder_after_changes_actual + +- name: assert copy folder after changes + assert: + that: + - copy_folder_after_change is changed + - copy_folder_after_changes_actual.matched == 2 + - copy_folder_after_changes_actual.files[0].checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6' + - copy_folder_after_changes_actual.files[1].checksum == 'b54ba7f5621240d403f06815f7246006ef8c7d43' + +- name: copy folder's contents (check mode) + win_copy: + src: files/ + dest: '{{test_win_copy_path}}\recursive-contents\' + register: copy_folder_contents_check + check_mode: yes + +- name: get result of copy folder'scontents (check mode) + win_stat: + path: '{{test_win_copy_path}}\recursive-contents' + register: copy_folder_contents_actual_check + +- name: assert copy folder's contents (check mode) + assert: + that: + - copy_folder_contents_check is changed + - copy_folder_contents_check.operation == 'folder_copy' + - copy_folder_contents_actual_check.stat.exists == False + +- name: copy folder's contents + win_copy: + src: files/ + dest: '{{test_win_copy_path}}\recursive-contents\' + register: copy_folder_contents + +- name: get result of copy folder + win_find: + paths: '{{test_win_copy_path}}\recursive-contents' + recurse: yes + file_type: directory + register: copy_folder_contents_actual + +- name: assert copy folder + assert: + that: + - copy_folder_contents is changed + - copy_folder_contents.operation == 'folder_copy' + - copy_folder_contents_actual.examined == 10 # includes files and folders, the below is the nested order + - copy_folder_contents_actual.matched == 5 + - copy_folder_contents_actual.files[0].filename == 'subdir' + - copy_folder_contents_actual.files[1].filename == 'empty' + - copy_folder_contents_actual.files[2].filename == 'subdir2' + - copy_folder_contents_actual.files[3].filename == 'subdir3' + - copy_folder_contents_actual.files[4].filename == 'subdir4' + +- name: fail to copy file to a folder + win_copy: + src: foo.txt + dest: '{{test_win_copy_path}}\recursive-contents' + register: fail_file_to_folder + failed_when: "'object at path is already a directory' not in fail_file_to_folder.msg" + +- name: fail to copy folder to a file + win_copy: + src: subdir/ + dest: '{{test_win_copy_path}}\recursive-contents\foo.txt' + register: fail_folder_to_file + failed_when: "'object at parent directory path is already a file' not in fail_folder_to_file.msg" + +# https://github.com/ansible/ansible/issues/31336 +- name: create file with colon in the name + copy: + dest: '{{role_path}}/files-different/colon:file' + content: test + delegate_to: localhost + +- name: copy a file with colon as a source + win_copy: + src: '{{role_path}}/files-different/colon:file' + dest: '{{test_win_copy_path}}\colon.file' + register: copy_file_with_colon + +- name: get result of file with colon as a source + win_stat: + path: '{{test_win_copy_path}}\colon.file' + register: copy_file_with_colon_result + +- name: assert results of copy a file with colon as a source + assert: + that: + - copy_file_with_colon is changed + - copy_file_with_colon_result.stat.exists == True + - copy_file_with_colon_result.stat.checksum == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3" + +- name: remove file with colon in the name + file: + path: '{{role_path}}/files-different/colon:file' + state: absent + delegate_to: localhost + +- name: copy an encrypted file without decrypting + win_copy: + src: '{{role_path}}/files-different/vault/vault-file' + dest: '{{test_win_copy_path}}\vault-file' + decrypt: no + register: copy_encrypted_file + +- name: get stat of copied encrypted file without decrypting + win_stat: + path: '{{test_win_copy_path}}\vault-file' + register: copy_encrypted_file_result + +- name: assert result of copy an encrypted file without decrypting + assert: + that: + - copy_encrypted_file is changed + - copy_encrypted_file_result.stat.checksum == "74a89620002d253f38834ee5b06cddd28956a43d" + +- name: copy an encrypted file without decrypting (idempotent) + win_copy: + src: '{{role_path}}/files-different/vault/vault-file' + dest: '{{test_win_copy_path}}\vault-file' + decrypt: no + register: copy_encrypted_file_again + +- name: assert result of copy an encrypted file without decrypting (idempotent) + assert: + that: + - copy_encrypted_file_again is not changed + +- name: copy folder with encrypted files without decrypting + win_copy: + src: '{{role_path}}/files-different/vault/' + dest: '{{test_win_copy_path}}\encrypted-test' + decrypt: no + register: copy_encrypted_file + +- name: get result of copy folder with encrypted files without decrypting + win_find: + paths: '{{test_win_copy_path}}\encrypted-test' + recurse: yes + patterns: '*vault*' + register: copy_encrypted_file_result + +- name: assert result of copy folder with encrypted files without decrypting + assert: + that: + - copy_encrypted_file is changed + - copy_encrypted_file_result.files|count == 2 + - copy_encrypted_file_result.files[0].checksum == "834563c94127730ecfa42dfc1e1821bbda2e51da" + - copy_encrypted_file_result.files[1].checksum == "74a89620002d253f38834ee5b06cddd28956a43d" + +- name: copy folder with encrypted files without decrypting (idempotent) + win_copy: + src: '{{role_path}}/files-different/vault/' + dest: '{{test_win_copy_path}}\encrypted-test' + decrypt: no + register: copy_encrypted_file_again + +- name: assert result of copy folder with encrypted files without decrypting (idempotent) + assert: + that: + - copy_encrypted_file_again is not changed + +- name: remove test folder after local to remote tests + win_file: + path: '{{test_win_copy_path}}' + state: absent diff --git a/test/integration/targets/incidental_win_data_deduplication/aliases b/test/integration/targets/incidental_win_data_deduplication/aliases new file mode 100644 index 0000000000..c7657537a7 --- /dev/null +++ b/test/integration/targets/incidental_win_data_deduplication/aliases @@ -0,0 +1,5 @@ +shippable/windows/incidental +windows +skip/windows/2008 +skip/windows/2008-R2 +skip/windows/2012 diff --git a/test/integration/targets/incidental_win_data_deduplication/meta/main.yml b/test/integration/targets/incidental_win_data_deduplication/meta/main.yml new file mode 100644 index 0000000000..9f37e96cd9 --- /dev/null +++ b/test/integration/targets/incidental_win_data_deduplication/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/test/integration/targets/incidental_win_data_deduplication/tasks/main.yml b/test/integration/targets/incidental_win_data_deduplication/tasks/main.yml new file mode 100644 index 0000000000..ae6be90ecb --- /dev/null +++ b/test/integration/targets/incidental_win_data_deduplication/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- include: pre_test.yml diff --git a/test/integration/targets/incidental_win_data_deduplication/tasks/pre_test.yml b/test/integration/targets/incidental_win_data_deduplication/tasks/pre_test.yml new file mode 100644 index 0000000000..f72955e46b --- /dev/null +++ b/test/integration/targets/incidental_win_data_deduplication/tasks/pre_test.yml @@ -0,0 +1,40 @@ +--- +- set_fact: + AnsibleVhdx: '{{ remote_tmp_dir }}\AnsiblePart.vhdx' + +- name: Install FS-Data-Deduplication + win_feature: + name: FS-Data-Deduplication + include_sub_features: true + state: present + register: data_dedup_feat_reg + +- name: Reboot windows after the feature has been installed + win_reboot: + reboot_timeout: 3600 + when: + - data_dedup_feat_reg.success + - data_dedup_feat_reg.reboot_required + +- name: Copy VHDX scripts + win_template: + src: "{{ item.src }}" + dest: '{{ remote_tmp_dir }}\{{ item.dest }}' + loop: + - { src: partition_creation_script.j2, dest: partition_creation_script.txt } + - { src: partition_deletion_script.j2, dest: partition_deletion_script.txt } + +- name: Create partition + win_command: diskpart.exe /s {{ remote_tmp_dir }}\partition_creation_script.txt + +- name: Format T with NTFS + win_format: + drive_letter: T + file_system: ntfs + +- name: Run tests + block: + - include: tests.yml + always: + - name: Detach disk + win_command: diskpart.exe /s {{ remote_tmp_dir }}\partition_deletion_script.txt diff --git a/test/integration/targets/incidental_win_data_deduplication/tasks/tests.yml b/test/integration/targets/incidental_win_data_deduplication/tasks/tests.yml new file mode 100644 index 0000000000..64a4292713 --- /dev/null +++ b/test/integration/targets/incidental_win_data_deduplication/tasks/tests.yml @@ -0,0 +1,47 @@ +--- + +- name: Enable Data Deduplication on the T drive - check mode + win_data_deduplication: + drive_letter: "T" + state: present + settings: + no_compress: true + minimum_file_age_days: 2 + minimum_file_size: 0 + check_mode: yes + register: win_data_deduplication_enable_check_mode + +- name: Check that it was successful with a change - check mode + assert: + that: + - win_data_deduplication_enable_check_mode is changed + +- name: Enable Data Deduplication on the T drive + win_data_deduplication: + drive_letter: "T" + state: present + settings: + no_compress: true + minimum_file_age_days: 2 + minimum_file_size: 0 + register: win_data_deduplication_enable + +- name: Check that it was successful with a change + assert: + that: + - win_data_deduplication_enable is changed + +- name: Enable Data Deduplication on the T drive + win_data_deduplication: + drive_letter: "T" + state: present + settings: + no_compress: true + minimum_file_age_days: 2 + minimum_file_size: 0 + register: win_data_deduplication_enable_again + +- name: Check that it was successful without a change + assert: + that: + - win_data_deduplication_enable_again is not changed diff --git a/test/integration/targets/incidental_win_data_deduplication/templates/partition_creation_script.j2 b/test/integration/targets/incidental_win_data_deduplication/templates/partition_creation_script.j2 new file mode 100644 index 0000000000..8e47fda95b --- /dev/null +++ b/test/integration/targets/incidental_win_data_deduplication/templates/partition_creation_script.j2 @@ -0,0 +1,11 @@ +create vdisk file="{{ AnsibleVhdx }}" maximum=2000 type=fixed + +select vdisk file="{{ AnsibleVhdx }}" + +attach vdisk + +convert mbr + +create partition primary + +assign letter="T" diff --git a/test/integration/targets/incidental_win_data_deduplication/templates/partition_deletion_script.j2 b/test/integration/targets/incidental_win_data_deduplication/templates/partition_deletion_script.j2 new file mode 100644 index 0000000000..c2be9cd144 --- /dev/null +++ b/test/integration/targets/incidental_win_data_deduplication/templates/partition_deletion_script.j2 @@ -0,0 +1,3 @@ +select vdisk file="{{ AnsibleVhdx }}" + +detach vdisk diff --git a/test/integration/targets/incidental_win_dsc/aliases b/test/integration/targets/incidental_win_dsc/aliases new file mode 100644 index 0000000000..9114c74227 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/aliases @@ -0,0 +1,6 @@ +shippable/windows/incidental +windows +skip/windows/2008 +skip/windows/2008-R2 +skip/windows/2012 +skip/windows/2012-R2 diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.psm1 b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.psm1 new file mode 100644 index 0000000000..dbf1ecf3ee --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.psm1 @@ -0,0 +1,41 @@ +#Requires -Version 5.0 -Modules CimCmdlets + +Function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([Hashtable])] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [String]$KeyParam + ) + return @{Value = [bool]$global:DSCMachineStatus} +} + +Function Set-TargetResource +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [String]$KeyParam, + [Bool]$Value = $true + ) + $global:DSCMachineStatus = [int]$Value +} + +Function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([Boolean])] + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [String]$KeyParam, + [Bool]$Value = $true + ) + $false +} + +Export-ModuleMember -Function *-TargetResource + diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.schema.mof b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.schema.mof new file mode 100644 index 0000000000..288b887722 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.schema.mof @@ -0,0 +1,7 @@ +[ClassVersion("1.0.0"), FriendlyName("xSetReboot")] +class ANSIBLE_xSetReboot : OMI_BaseResource +{ + [Key] String KeyParam; + [Write] Boolean Value; +}; + diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1 b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1 new file mode 100644 index 0000000000..79f6496962 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1 @@ -0,0 +1,214 @@ +#Requires -Version 5.0 -Modules CimCmdlets + +Function ConvertFrom-CimInstance { + param( + [Parameter(Mandatory=$true)][CimInstance]$Instance + ) + $hashtable = @{ + _cim_instance = $Instance.CimSystemProperties.ClassName + } + foreach ($prop in $Instance.CimInstanceProperties) { + $hashtable."$($prop.Name)" = ConvertTo-OutputValue -Value $prop.Value + } + return $hashtable +} + +Function ConvertTo-OutputValue { + param($Value) + + if ($Value -is [DateTime[]]) { + $Value = $Value | ForEach-Object { $_.ToString("o") } + } elseif ($Value -is [DateTime]) { + $Value = $Value.ToString("o") + } elseif ($Value -is [Double]) { + $Value = $Value.ToString() # To avoid Python 2 double parsing issues on test validation + } elseif ($Value -is [Double[]]) { + $Value = $Value | ForEach-Object { $_.ToString() } + } elseif ($Value -is [PSCredential]) { + $password = $null + $password_ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($Value.Password) + try { + $password = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($password_ptr) + } finally { + [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($password_ptr) + } + $Value = @{ + username = $Value.Username + password = $password + } + } elseif ($Value -is [CimInstance[]]) { + $value_list = [System.Collections.Generic.List`1[Hashtable]]@() + foreach ($cim_instance in $Value) { + $value_list.Add((ConvertFrom-CimInstance -Instance $cim_instance)) + } + $Value = $value_list.ToArray() + } elseif ($Value -is [CimInstance]) { + $Value = ConvertFrom-CimInstance -Instance $Value + } + + return ,$Value +} + +Function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([Hashtable])] + param( + [Parameter(Mandatory = $true)] + [ValidateSet("Present", "Absent")] + [String] $Ensure = "Present", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] $Path + ) + return @{ + Ensure = $Ensure + Path = $Path + } +} + +Function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet("Present", "Absent")] + [String] $Ensure = "Present", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] $Path, + + [String] $DefaultParam = "Default", + [String] $StringParam, + [String[]] $StringArrayParam, + [SByte] $Int8Param, + [SByte[]] $Int8ArrayParam, + [Byte] $UInt8Param, + [Byte[]] $UInt8ArrayParam, + [Int16] $Int16Param, + [Int16[]] $Int16ArrayParam, + [UInt16] $UInt16Param, + [UInt16[]] $UInt16ArrayParam, + [Int32] $Int32Param, + [Int32[]] $Int32ArrayParam, + [UInt32] $UInt32Param, + [UInt32[]] $UInt32ArrayParam, + [Int64] $Int64Param, + [Int64[]] $Int64ArrayParam, + [UInt64] $UInt64Param, + [UInt64[]] $UInt64ArrayParam, + [Bool] $BooleanParam, + [Bool[]] $BooleanArrayParam, + [Char] $CharParam, + [Char[]] $CharArrayParam, + [Single] $SingleParam, + [Single[]] $SingleArrayParam, + [Double] $DoubleParam, + [Double[]] $DoubleArrayParam, + [DateTime] $DateTimeParam, + [DateTime[]] $DateTimeArrayParam, + [PSCredential] $PSCredentialParam, + [CimInstance[]] $HashtableParam, + [CimInstance] $CimInstanceParam, + [CimInstance[]] $CimInstanceArrayParam, + [CimInstance] $NestedCimInstanceParam, + [CimInstance[]] $NestedCimInstanceArrayParam + ) + + $info = @{ + Version = "1.0.0" + Ensure = @{ + Type = $Ensure.GetType().FullName + Value = $Ensure + } + Path = @{ + Type = $Path.GetType().FullName + Value = $Path + } + DefaultParam = @{ + Type = $DefaultParam.GetType().FullName + Value = $DefaultParam + } + } + + foreach ($kvp in $PSCmdlet.MyInvocation.BoundParameters.GetEnumerator()) { + $info."$($kvp.Key)" = @{ + Type = $kvp.Value.GetType().FullName + Value = (ConvertTo-OutputValue -Value $kvp.Value) + } + } + + if (Test-Path -Path $Path) { + Remove-Item -Path $Path -Force > $null + } + New-Item -Path $Path -ItemType File > $null + Set-Content -Path $Path -Value (ConvertTo-Json -InputObject $info -Depth 10) > $null + Write-Verbose -Message "set verbose" + Write-Warning -Message "set warning" +} + +Function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet("Present", "Absent")] + [String] $Ensure = "Present", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] $Path, + + [String] $DefaultParam = "Default", + [String] $StringParam, + [String[]] $StringArrayParam, + [SByte] $Int8Param, + [SByte[]] $Int8ArrayParam, + [Byte] $UInt8Param, + [Byte[]] $UInt8ArrayParam, + [Int16] $Int16Param, + [Int16[]] $Int16ArrayParam, + [UInt16] $UInt16Param, + [UInt16[]] $UInt16ArrayParam, + [Int32] $Int32Param, + [Int32[]] $Int32ArrayParam, + [UInt32] $UInt32Param, + [UInt32[]] $UInt32ArrayParam, + [Int64] $Int64Param, + [Int64[]] $Int64ArrayParam, + [UInt64] $UInt64Param, + [UInt64[]] $UInt64ArrayParam, + [Bool] $BooleanParam, + [Bool[]] $BooleanArrayParam, + [Char] $CharParam, + [Char[]] $CharArrayParam, + [Single] $SingleParam, + [Single[]] $SingleArrayParam, + [Double] $DoubleParam, + [Double[]] $DoubleArrayParam, + [DateTime] $DateTimeParam, + [DateTime[]] $DateTimeArrayParam, + [PSCredential] $PSCredentialParam, + [CimInstance[]] $HashtableParam, + [CimInstance] $CimInstanceParam, + [CimInstance[]] $CimInstanceArrayParam, + [CimInstance] $NestedCimInstanceParam, + [CimInstance[]] $NestedCimInstanceArrayParam + ) + Write-Verbose -Message "test verbose" + Write-Warning -Message "test warning" + $exists = Test-Path -LiteralPath $Path -PathType Leaf + if ($Ensure -eq "Present") { + $exists + } else { + -not $exists + } +} + +Export-ModuleMember -Function *-TargetResource + diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof new file mode 100644 index 0000000000..c61b2b1e6a --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof @@ -0,0 +1,60 @@ +[ClassVersion("1.0.0")] +class ANSIBLE_xTestClass +{ + [Key] String Key; + [Write] String StringValue; + [Write] SInt32 IntValue; + [Write] String StringArrayValue[]; +}; + +[ClassVersion("1.0.0")] +class ANSIBLE_xNestedClass +{ + [Key] String KeyValue; + [Write, EmbeddedInstance("ANSIBLE_xTestClass")] String CimValue; + [Write, EmbeddedInstance("MSFT_KeyValuePair")] String HashValue[]; + [Write] SInt16 IntValue; +}; + +[ClassVersion("1.0.0"), FriendlyName("xTestResource")] +class ANSIBLE_xTestResource : OMI_BaseResource +{ + [Key] String Path; + [Required, ValueMap{"Present", "Absent"}, Values{"Present", "Absent"}] String Ensure; + [Read] String ReadParam; + [Write] String DefaultParam; + [Write] String StringParam; + [Write] String StringArrayParam[]; + [Write] SInt8 Int8Param; + [Write] SInt8 Int8ArrayParam[]; + [Write] UInt8 UInt8Param; + [Write] UInt8 UInt8ArrayParam[]; + [Write] SInt16 Int16Param; + [Write] SInt16 Int16ArrayParam[]; + [Write] UInt16 UInt16Param; + [Write] UInt16 UInt16ArrayParam[]; + [Write] SInt32 Int32Param; + [Write] SInt32 Int32ArrayParam[]; + [Write] UInt32 UInt32Param; + [Write] UInt32 UInt32ArrayParam[]; + [Write] SInt64 Int64Param; + [Write] SInt64 Int64ArrayParam[]; + [Write] UInt64 UInt64Param; + [Write] UInt64 UInt64ArrayParam[]; + [Write] Boolean BooleanParam; + [Write] Boolean BooleanArrayParam[]; + [Write] Char16 CharParam; + [Write] Char16 CharArrayParam[]; + [Write] Real32 SingleParam; + [Write] Real32 SingleArrayParam[]; + [Write] Real64 DoubleParam; + [Write] Real64 DoubleArrayParam[]; + [Write] DateTime DateTimeParam; + [Write] DateTime DateTimeArrayParam[]; + [Write, EmbeddedInstance("MSFT_Credential")] String PSCredentialParam; + [Write, EmbeddedInstance("MSFT_KeyValuePair")] String HashtableParam[]; + [Write, EmbeddedInstance("ANSIBLE_xTestClass")] String CimInstanceArrayParam[]; + [Write, EmbeddedInstance("ANSIBLE_xNestedClass")] String NestedCimInstanceParam; + [Write, EmbeddedInstance("ANSIBLE_xNestedClass")] String NestedCimInstanceArrayParam[]; +}; + diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/xTestDsc.psd1 b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/xTestDsc.psd1 new file mode 100644 index 0000000000..3d61611d70 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/xTestDsc.psd1 @@ -0,0 +1,13 @@ +@{ + ModuleVersion = '1.0.0' + GUID = '80c895c4-de3f-4d6d-8fa4-c504c96b6f22' + Author = 'Ansible' + CompanyName = 'Ansible' + Copyright = '(c) 2019' + Description = 'Test DSC Resource for Ansible integration tests' + PowerShellVersion = '5.0' + CLRVersion = '4.0' + FunctionsToExport = '*' + CmdletsToExport = '*' +} + diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1 b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1 new file mode 100644 index 0000000000..d75256e1d9 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1 @@ -0,0 +1,214 @@ +#Requires -Version 5.0 -Modules CimCmdlets + +Function ConvertFrom-CimInstance { + param( + [Parameter(Mandatory=$true)][CimInstance]$Instance + ) + $hashtable = @{ + _cim_instance = $Instance.CimSystemProperties.ClassName + } + foreach ($prop in $Instance.CimInstanceProperties) { + $hashtable."$($prop.Name)" = ConvertTo-OutputValue -Value $prop.Value + } + return $hashtable +} + +Function ConvertTo-OutputValue { + param($Value) + + if ($Value -is [DateTime[]]) { + $Value = $Value | ForEach-Object { $_.ToString("o") } + } elseif ($Value -is [DateTime]) { + $Value = $Value.ToString("o") + } elseif ($Value -is [Double]) { + $Value = $Value.ToString() # To avoid Python 2 double parsing issues on test validation + } elseif ($Value -is [Double[]]) { + $Value = $Value | ForEach-Object { $_.ToString() } + } elseif ($Value -is [PSCredential]) { + $password = $null + $password_ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($Value.Password) + try { + $password = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($password_ptr) + } finally { + [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($password_ptr) + } + $Value = @{ + username = $Value.Username + password = $password + } + } elseif ($Value -is [CimInstance[]]) { + $value_list = [System.Collections.Generic.List`1[Hashtable]]@() + foreach ($cim_instance in $Value) { + $value_list.Add((ConvertFrom-CimInstance -Instance $cim_instance)) + } + $Value = $value_list.ToArray() + } elseif ($Value -is [CimInstance]) { + $Value = ConvertFrom-CimInstance -Instance $Value + } + + return ,$Value +} + +Function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([Hashtable])] + param( + [Parameter(Mandatory = $true)] + [ValidateSet("Present", "Absent")] + [String] $Ensure = "Present", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] $Path + ) + return @{ + Ensure = $Ensure + Path = $Path + } +} + +Function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet("Present", "Absent")] + [String] $Ensure = "Present", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] $Path, + + [String] $DefaultParam = "Default", + [String] $StringParam, + [String[]] $StringArrayParam, + [SByte] $Int8Param, + [SByte[]] $Int8ArrayParam, + [Byte] $UInt8Param, + [Byte[]] $UInt8ArrayParam, + [Int16] $Int16Param, + [Int16[]] $Int16ArrayParam, + [UInt16] $UInt16Param, + [UInt16[]] $UInt16ArrayParam, + [Int32] $Int32Param, + [Int32[]] $Int32ArrayParam, + [UInt32] $UInt32Param, + [UInt32[]] $UInt32ArrayParam, + [Int64] $Int64Param, + [Int64[]] $Int64ArrayParam, + [UInt64] $UInt64Param, + [UInt64[]] $UInt64ArrayParam, + [Bool] $BooleanParam, + [Bool[]] $BooleanArrayParam, + [Char] $CharParam, + [Char[]] $CharArrayParam, + [Single] $SingleParam, + [Single[]] $SingleArrayParam, + [Double] $DoubleParam, + [Double[]] $DoubleArrayParam, + [DateTime] $DateTimeParam, + [DateTime[]] $DateTimeArrayParam, + [PSCredential] $PSCredentialParam, + [CimInstance[]] $HashtableParam, + [CimInstance] $CimInstanceParam, + [CimInstance[]] $CimInstanceArrayParam, + [CimInstance] $NestedCimInstanceParam, + [CimInstance[]] $NestedCimInstanceArrayParam + ) + + $info = @{ + Version = "1.0.1" + Ensure = @{ + Type = $Ensure.GetType().FullName + Value = $Ensure + } + Path = @{ + Type = $Path.GetType().FullName + Value = $Path + } + DefaultParam = @{ + Type = $DefaultParam.GetType().FullName + Value = $DefaultParam + } + } + + foreach ($kvp in $PSCmdlet.MyInvocation.BoundParameters.GetEnumerator()) { + $info."$($kvp.Key)" = @{ + Type = $kvp.Value.GetType().FullName + Value = (ConvertTo-OutputValue -Value $kvp.Value) + } + } + + if (Test-Path -Path $Path) { + Remove-Item -Path $Path -Force > $null + } + New-Item -Path $Path -ItemType File > $null + Set-Content -Path $Path -Value (ConvertTo-Json -InputObject $info -Depth 10) > $null + Write-Verbose -Message "set verbose" + Write-Warning -Message "set warning" +} + +Function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet("Present", "Absent")] + [String] $Ensure = "Present", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] $Path, + + [String] $DefaultParam = "Default", + [String] $StringParam, + [String[]] $StringArrayParam, + [SByte] $Int8Param, + [SByte[]] $Int8ArrayParam, + [Byte] $UInt8Param, + [Byte[]] $UInt8ArrayParam, + [Int16] $Int16Param, + [Int16[]] $Int16ArrayParam, + [UInt16] $UInt16Param, + [UInt16[]] $UInt16ArrayParam, + [Int32] $Int32Param, + [Int32[]] $Int32ArrayParam, + [UInt32] $UInt32Param, + [UInt32[]] $UInt32ArrayParam, + [Int64] $Int64Param, + [Int64[]] $Int64ArrayParam, + [UInt64] $UInt64Param, + [UInt64[]] $UInt64ArrayParam, + [Bool] $BooleanParam, + [Bool[]] $BooleanArrayParam, + [Char] $CharParam, + [Char[]] $CharArrayParam, + [Single] $SingleParam, + [Single[]] $SingleArrayParam, + [Double] $DoubleParam, + [Double[]] $DoubleArrayParam, + [DateTime] $DateTimeParam, + [DateTime[]] $DateTimeArrayParam, + [PSCredential] $PSCredentialParam, + [CimInstance[]] $HashtableParam, + [CimInstance] $CimInstanceParam, + [CimInstance[]] $CimInstanceArrayParam, + [CimInstance] $NestedCimInstanceParam, + [CimInstance[]] $NestedCimInstanceArrayParam + ) + Write-Verbose -Message "test verbose" + Write-Warning -Message "test warning" + $exists = Test-Path -LiteralPath $Path -PathType Leaf + if ($Ensure -eq "Present") { + $exists + } else { + -not $exists + } +} + +Export-ModuleMember -Function *-TargetResource + diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof new file mode 100644 index 0000000000..9301664b3c --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof @@ -0,0 +1,63 @@ +[ClassVersion("1.0.1")] +class ANSIBLE_xTestClass +{ + [Key] String KeyValue; + [Write, ValueMap{"Choice1", "Choice2"}, Values{"Choice1", "Choice2"}] String Choice; + [Write] String StringValue; + [Write] SInt32 IntValue; + [Write] String StringArrayValue[]; +}; + +[ClassVersion("1.0.1")] +class ANSIBLE_xNestedClass +{ + [Key] String KeyValue; + [Write, EmbeddedInstance("ANSIBLE_xTestClass")] String CimValue; + [Write, EmbeddedInstance("ANSIBLE_xTestClass")] String CimArrayValue[]; + [Write, EmbeddedInstance("MSFT_KeyValuePair")] String HashValue[]; + [Write] SInt16 IntValue; +}; + +[ClassVersion("1.0.1"), FriendlyName("xTestResource")] +class ANSIBLE_xTestResource : OMI_BaseResource +{ + [Key] String Path; + [Required, ValueMap{"Present", "Absent"}, Values{"Present", "Absent"}] String Ensure; + [Read] String ReadParam; + [Write] String DefaultParam; + [Write] String StringParam; + [Write] String StringArrayParam[]; + [Write] SInt8 Int8Param; + [Write] SInt8 Int8ArrayParam[]; + [Write] UInt8 UInt8Param; + [Write] UInt8 UInt8ArrayParam[]; + [Write] SInt16 Int16Param; + [Write] SInt16 Int16ArrayParam[]; + [Write] UInt16 UInt16Param; + [Write] UInt16 UInt16ArrayParam[]; + [Write] SInt32 Int32Param; + [Write] SInt32 Int32ArrayParam[]; + [Write] UInt32 UInt32Param; + [Write] UInt32 UInt32ArrayParam[]; + [Write] SInt64 Int64Param; + [Write] SInt64 Int64ArrayParam[]; + [Write] UInt64 UInt64Param; + [Write] UInt64 UInt64ArrayParam[]; + [Write] Boolean BooleanParam; + [Write] Boolean BooleanArrayParam[]; + [Write] Char16 CharParam; + [Write] Char16 CharArrayParam[]; + [Write] Real32 SingleParam; + [Write] Real32 SingleArrayParam[]; + [Write] Real64 DoubleParam; + [Write] Real64 DoubleArrayParam[]; + [Write] DateTime DateTimeParam; + [Write] DateTime DateTimeArrayParam[]; + [Write, EmbeddedInstance("MSFT_Credential")] String PSCredentialParam; + [Write, EmbeddedInstance("MSFT_KeyValuePair")] String HashtableParam[]; + [Write, EmbeddedInstance("ANSIBLE_xTestClass")] String CimInstanceParam; + [Write, EmbeddedInstance("ANSIBLE_xTestClass")] String CimInstanceArrayParam[]; + [Write, EmbeddedInstance("ANSIBLE_xNestedClass")] String NestedCimInstanceParam; + [Write, EmbeddedInstance("ANSIBLE_xNestedClass")] String NestedCimInstanceArrayParam[]; +}; + diff --git a/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/xTestDsc.psd1 b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/xTestDsc.psd1 new file mode 100644 index 0000000000..0c43b85238 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/xTestDsc.psd1 @@ -0,0 +1,13 @@ +@{ + ModuleVersion = '1.0.1' + GUID = '80c895c4-de3f-4d6d-8fa4-c504c96b6f22' + Author = 'Ansible' + CompanyName = 'Ansible' + Copyright = '(c) 2019' + Description = 'Test DSC Resource for Ansible integration tests' + PowerShellVersion = '5.0' + CLRVersion = '4.0' + FunctionsToExport = '*' + CmdletsToExport = '*' +} + diff --git a/test/integration/targets/incidental_win_dsc/meta/main.yml b/test/integration/targets/incidental_win_dsc/meta/main.yml new file mode 100644 index 0000000000..9f37e96cd9 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/test/integration/targets/incidental_win_dsc/tasks/main.yml b/test/integration/targets/incidental_win_dsc/tasks/main.yml new file mode 100644 index 0000000000..f37295ab71 --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/tasks/main.yml @@ -0,0 +1,39 @@ +--- +- name: get powershell version + win_shell: $PSVersionTable.PSVersion.Major + register: powershell_version + +- name: expect failure when running on old PS hosts + win_dsc: + resource_name: File + register: fail_dsc_old + failed_when: '"This module cannot run as it requires a minimum PowerShell version of 5.0" not in fail_dsc_old.msg' + when: powershell_version.stdout_lines[0]|int < 5 + +- name: run tests when PSv5+ + when: powershell_version.stdout_lines[0]|int >= 5 + block: + - name: add remote temp dir to PSModulePath + win_path: + name: PSModulePath + state: present + scope: machine + elements: + - '{{ remote_tmp_dir }}' + + - name: copy custom DSC resources to remote temp dir + win_copy: + src: xTestDsc + dest: '{{ remote_tmp_dir }}' + + - name: run tests + include_tasks: tests.yml + + always: + - name: remove remote tmp dir from PSModulePath + win_path: + name: PSModulePath + state: absent + scope: machine + elements: + - '{{ remote_tmp_dir }}' diff --git a/test/integration/targets/incidental_win_dsc/tasks/tests.yml b/test/integration/targets/incidental_win_dsc/tasks/tests.yml new file mode 100644 index 0000000000..d2a6802fdf --- /dev/null +++ b/test/integration/targets/incidental_win_dsc/tasks/tests.yml @@ -0,0 +1,544 @@ +--- +- name: fail with incorrect DSC resource name + win_dsc: + resource_name: FakeResource + register: fail_invalid_resource + failed_when: fail_invalid_resource.msg != "Resource 'FakeResource' not found." + +- name: fail with invalid DSC version + win_dsc: + resource_name: xTestResource + module_version: 0.0.1 + register: fail_invalid_version + failed_when: 'fail_invalid_version.msg != "Resource ''xTestResource'' with version ''0.0.1'' not found. Versions installed: ''1.0.0'', ''1.0.1''."' + +- name: fail with mandatory option not set + win_dsc: + resource_name: xSetReboot + Value: yes + register: fail_man_key + failed_when: 'fail_man_key.msg != "missing required arguments: KeyParam"' + +- name: fail with mandatory option not set in sub dict + win_dsc: + resource_name: xTestResource + Path: C:\path + Ensure: Present + CimInstanceParam: # Missing KeyValue in dict + Choice: Choice1 + register: fail_man_key_sub_dict + failed_when: 'fail_man_key_sub_dict.msg != "missing required arguments: KeyValue found in CimInstanceParam"' + +- name: fail invalid option + win_dsc: + resource_name: xSetReboot + KeyParam: key + OtherParam: invalid + register: fail_invalid_option + failed_when: 'fail_invalid_option.msg != "Unsupported parameters for (win_dsc) module: OtherParam. Supported parameters include: KeyParam, PsDscRunAsCredential_username, module_version, Value, PsDscRunAsCredential_password, resource_name, DependsOn"' + +- name: fail invalid option in sub dict + win_dsc: + resource_name: xTestResource + Path: C:\path + Ensure: Present + NestedCimInstanceParam: + KeyValue: key + CimValue: + KeyValue: other key + InvalidKey: invalid + register: fail_invalid_option_sub_dict + failed_when: 'fail_invalid_option_sub_dict.msg != "Unsupported parameters for (win_dsc) module: InvalidKey found in NestedCimInstanceParam -> CimValue. Supported parameters include: IntValue, KeyValue, StringArrayValue, Choice, StringValue"' + +- name: fail invalid read only option + win_dsc: + resource_name: xTestResource + Path: C:\path + Ensure: Present + ReadParam: abc + register: fail_invalid_option_read_only + failed_when: '"Unsupported parameters for (win_dsc) module: ReadParam" not in fail_invalid_option_read_only.msg' + +- name: fail invalid choice + win_dsc: + resource_name: xTestResource + Path: C:\path + Ensure: invalid + register: fail_invalid_choice + failed_when: 'fail_invalid_choice.msg != "value of Ensure must be one of: Present, Absent. Got no match for: invalid"' + +- name: fail invalid choice in sub dict + win_dsc: + resource_name: xTestResource + Path: C:\path + Ensure: Present + CimInstanceArrayParam: + - KeyValue: key + - KeyValue: key2 + Choice: Choice3 + register: fail_invalid_choice_sub_dict + failed_when: 'fail_invalid_choice_sub_dict.msg != "value of Choice must be one of: Choice1, Choice2. Got no match for: Choice3 found in CimInstanceArrayParam"' + +- name: fail old version missing new option + win_dsc: + resource_name: xTestResource + module_version: 1.0.0 + Path: C:\path + Ensure: Present + CimInstanceParam: # CimInstanceParam does not exist in the 1.0.0 version + Key: key + register: fail_invalid_option_old + failed_when: '"Unsupported parameters for (win_dsc) module: CimInstanceParam" not in fail_invalid_option_old.msg' + +- name: fail old version missing new option sub dict + win_dsc: + resource_name: xTestResource + module_version: 1.0.0 + Path: C:\path + Ensure: Present + CimInstanceArrayParam: + - Key: key + Choice: Choice1 + register: fail_invalid_option_old_sub_dict + failed_when: 'fail_invalid_option_old_sub_dict.msg != "Unsupported parameters for (win_dsc) module: Choice found in CimInstanceArrayParam. Supported parameters include: Key, IntValue, StringArrayValue, StringValue"' + +- name: create test file (check mode) + win_dsc: + resource_name: File + DestinationPath: '{{ remote_tmp_dir }}\dsc-file' + Contents: file contents + Attributes: + - Hidden + - ReadOnly + Ensure: Present + Type: File + register: create_file_check + check_mode: yes + +- name: get result of create test file (check mode) + win_stat: + path: '{{ remote_tmp_dir }}\dsc-file' + register: create_file_actual_check + +- name: assert create test file (check mode) + assert: + that: + - create_file_check is changed + - create_file_check.module_version == None # Some built in modules don't have a version set + - not create_file_check.reboot_required + - not create_file_actual_check.stat.exists + +- name: assert create test file verbosity (check mode) + assert: + that: + - create_file_check.verbose_test is defined + - not create_file_check.verbose_set is defined + when: ansible_verbosity >= 3 + +- name: create test file + win_dsc: + resource_name: File + DestinationPath: '{{ remote_tmp_dir }}\dsc-file' + Contents: file contents + Attributes: + - Hidden + - ReadOnly + Ensure: Present + Type: File + register: create_file + +- name: get result of create test file + win_stat: + path: '{{ remote_tmp_dir }}\dsc-file' + register: create_file_actual + +- name: assert create test file verbosity + assert: + that: + - create_file.verbose_test is defined + - create_file.verbose_set is defined + when: ansible_verbosity >= 3 + +- name: assert create test file + assert: + that: + - create_file is changed + - create_file.module_version == None + - not create_file.reboot_required + - create_file_actual.stat.exists + - create_file_actual.stat.attributes == "ReadOnly, Hidden, Archive" + - create_file_actual.stat.checksum == 'd48daab51112b49ecabd917adc345b8ba257055e' + +- name: create test file (idempotent) + win_dsc: + resource_name: File + DestinationPath: '{{ remote_tmp_dir }}\dsc-file' + Contents: file contents + Attributes: + - Hidden + - ReadOnly + Ensure: Present + Type: File + register: create_file_again + +- name: assert create test file (idempotent) + assert: + that: + - not create_file_again is changed + - create_file.module_version == None + - not create_file.reboot_required + +- name: get SID of the current Ansible user + win_shell: | + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + [System.DirectoryServices.AccountManagement.UserPrincipal]::Current.Sid.Value + register: actual_sid + +- name: run DSC process as another user + win_dsc: + resource_name: Script + GetScript: '@{ Result= "" }' + SetScript: | + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + $sid = [System.DirectoryServices.AccountManagement.UserPrincipal]::Current.Sid.Value + Set-Content -Path "{{ remote_tmp_dir }}\runas.txt" -Value $sid + TestScript: $false + PsDscRunAsCredential_username: '{{ ansible_user }}' + PsDscRunAsCredential_password: '{{ ansible_password }}' + register: runas_user + +- name: get result of run DSC process as another user + slurp: + path: '{{ remote_tmp_dir }}\runas.txt' + register: runas_user_result + +- name: assert run DSC process as another user + assert: + that: + - runas_user is changed + - runas_user.module_version != None # Can't reliably set the version but we can test it is set + - not runas_user.reboot_required + - runas_user_result.content|b64decode == actual_sid.stdout + +- name: run DSC that sets reboot_required with defaults + win_dsc: + resource_name: xSetReboot + KeyParam: value # Just to satisfy the Resource with key validation + register: set_reboot_defaults + +- name: assert run DSC that sets reboot_required with defaults + assert: + that: + - set_reboot_defaults.reboot_required + +- name: run DSC that sets reboot_required with False + win_dsc: + resource_name: xSetReboot + KeyParam: value + Value: no + register: set_reboot_false + +- name: assert run DSC that sets reboot_required with False + assert: + that: + - not set_reboot_false.reboot_required + +- name: run DSC that sets reboot_required with True + win_dsc: + resource_name: xSetReboot + KeyParam: value + Value: yes + register: set_reboot_true + +- name: assert run DSC that sets reboot_required with True + assert: + that: + - set_reboot_true.reboot_required + +- name: test DSC with all types + win_dsc: + resource_name: xTestResource + Path: '{{ remote_tmp_dir }}\test-types.json' + Ensure: Present + StringParam: string param + StringArrayParam: + - string 1 + - string 2 + Int8Param: 127 # [SByte]::MaxValue + Int8ArrayParam: + - 127 + - '127' + UInt8Param: 255 # [Byte]::MaxValue + UInt8ArrayParam: + - 255 + - '255' + Int16Param: 32767 # [Int16]::MaxValue + Int16ArrayParam: 32767, 32767 + UInt16Param: '65535' # [UInt16]::MaxValue + UInt16ArrayParam: 65535 + Int32Param: 2147483647 # [Int32]::MaxValue + Int32ArrayParam: '2147483647' + UInt32Param: '4294967295' # [UInt32]::MaxValue + UInt32ArrayParam: + - '4294967295' + - 4294967295 + Int64Param: 9223372036854775807 # [Int64]::MaxValue + Int64ArrayParam: + - -9223372036854775808 # [Int64]::MinValue + - 9223372036854775807 + UInt64Param: 18446744073709551615 # [UInt64]::MaxValue + UInt64ArrayParam: + - 0 # [UInt64]::MinValue + - 18446744073709551615 + BooleanParam: True + BooleanArrayParam: + - True + - 'True' + - 'true' + - 'y' + - 'yes' + - 1 + - False + - 'False' + - 'false' + - 'n' + - 'no' + - 0 + CharParam: c + CharArrayParam: + - c + - h + - a + - r + SingleParam: 3.402823E+38 + SingleArrayParam: + - '3.402823E+38' + - 1.2393494 + DoubleParam: 1.79769313486232E+300 + DoubleArrayParam: + - '1.79769313486232E+300' + - 3.56821831681516 + DateTimeParam: '2019-02-22T13:57:31.2311892-04:00' + DateTimeArrayParam: + - '2019-02-22T13:57:31.2311892+00:00' + - '2019-02-22T13:57:31.2311892+04:00' + PSCredentialParam_username: username1 + PSCredentialParam_password: password1 + HashtableParam: + key1: string 1 + key2: '' + key3: 1 + CimInstanceParam: + KeyValue: a + CimInstanceArrayParam: + - KeyValue: b + Choice: Choice1 + StringValue: string 1 + IntValue: 1 + StringArrayValue: + - abc + - def + - KeyValue: c + Choice: Choice2 + StringValue: string 2 + IntValue: '2' + StringArrayValue: + - ghi + - jkl + NestedCimInstanceParam: + KeyValue: key value + CimValue: + KeyValue: d + CimArrayValue: + - KeyValue: e + Choice: Choice2 + HashValue: + a: a + IntValue: '300' + register: dsc_types + +- name: get result of test DSC with all types + slurp: + path: '{{ remote_tmp_dir }}\test-types.json' + register: dsc_types_raw + +- name: convert result of test DSC with all types to dict + set_fact: + dsc_types_actual: '{{ dsc_types_raw.content | b64decode | from_json }}' + +- name: assert test DSC with all types + assert: + that: + - dsc_types is changed + - dsc_types.module_version == '1.0.1' + - not dsc_types.reboot_required + - dsc_types_actual.Version == '1.0.1' + - dsc_types_actual.Verbose.Value.IsPresent + - dsc_types_actual.DefaultParam.Value == 'Default' # ensures that the default is set in the engine if we don't set it outselves + - dsc_types_actual.Ensure.Value == 'Present' + - dsc_types_actual.Path.Value == remote_tmp_dir + "\\test-types.json" + - dsc_types_actual.StringParam.Type == 'System.String' + - dsc_types_actual.StringParam.Value == 'string param' + - dsc_types_actual.StringArrayParam.Type == 'System.String[]' + - dsc_types_actual.StringArrayParam.Value == ['string 1', 'string 2'] + - dsc_types_actual.Int8Param.Type == 'System.SByte' + - dsc_types_actual.Int8Param.Value == 127 + - dsc_types_actual.Int8ArrayParam.Type == 'System.SByte[]' + - dsc_types_actual.Int8ArrayParam.Value == [127, 127] + - dsc_types_actual.UInt8Param.Type == 'System.Byte' + - dsc_types_actual.UInt8Param.Value == 255 + - dsc_types_actual.UInt8ArrayParam.Type == 'System.Byte[]' + - dsc_types_actual.UInt8ArrayParam.Value == [255, 255] + - dsc_types_actual.Int16Param.Type == 'System.Int16' + - dsc_types_actual.Int16Param.Value == 32767 + - dsc_types_actual.Int16ArrayParam.Type == 'System.Int16[]' + - dsc_types_actual.Int16ArrayParam.Value == [32767, 32767] + - dsc_types_actual.UInt16Param.Type == 'System.UInt16' + - dsc_types_actual.UInt16Param.Value == 65535 + - dsc_types_actual.UInt16ArrayParam.Type == 'System.UInt16[]' + - dsc_types_actual.UInt16ArrayParam.Value == [65535] + - dsc_types_actual.Int32Param.Type == 'System.Int32' + - dsc_types_actual.Int32Param.Value == 2147483647 + - dsc_types_actual.Int32ArrayParam.Type == 'System.Int32[]' + - dsc_types_actual.Int32ArrayParam.Value == [2147483647] + - dsc_types_actual.UInt32Param.Type == 'System.UInt32' + - dsc_types_actual.UInt32Param.Value == 4294967295 + - dsc_types_actual.UInt32ArrayParam.Type == 'System.UInt32[]' + - dsc_types_actual.UInt32ArrayParam.Value == [4294967295, 4294967295] + - dsc_types_actual.Int64Param.Type == 'System.Int64' + - dsc_types_actual.Int64Param.Value == 9223372036854775807 + - dsc_types_actual.Int64ArrayParam.Type == 'System.Int64[]' + - dsc_types_actual.Int64ArrayParam.Value == [-9223372036854775808, 9223372036854775807] + - dsc_types_actual.UInt64Param.Type == 'System.UInt64' + - dsc_types_actual.UInt64Param.Value == 18446744073709551615 + - dsc_types_actual.UInt64ArrayParam.Type == 'System.UInt64[]' + - dsc_types_actual.UInt64ArrayParam.Value == [0, 18446744073709551615] + - dsc_types_actual.BooleanParam.Type == 'System.Boolean' + - dsc_types_actual.BooleanParam.Value == True + - dsc_types_actual.BooleanArrayParam.Type == 'System.Boolean[]' + - dsc_types_actual.BooleanArrayParam.Value == [True, True, True, True, True, True, False, False, False, False, False, False] + - dsc_types_actual.CharParam.Type == 'System.Char' + - dsc_types_actual.CharParam.Value == 'c' + - dsc_types_actual.CharArrayParam.Type == 'System.Char[]' + - dsc_types_actual.CharArrayParam.Value == ['c', 'h', 'a', 'r'] + - dsc_types_actual.SingleParam.Type == 'System.Single' + - dsc_types_actual.SingleParam.Value|string == '3.402823e+38' + - dsc_types_actual.SingleArrayParam.Type == 'System.Single[]' + - dsc_types_actual.SingleArrayParam.Value|length == 2 + - dsc_types_actual.SingleArrayParam.Value[0]|string == '3.402823e+38' + - dsc_types_actual.SingleArrayParam.Value[1]|string == '1.23934937' + - dsc_types_actual.DoubleParam.Type == 'System.Double' + - dsc_types_actual.DoubleParam.Value == '1.79769313486232E+300' + - dsc_types_actual.DoubleArrayParam.Type == 'System.Double[]' + - dsc_types_actual.DoubleArrayParam.Value|length == 2 + - dsc_types_actual.DoubleArrayParam.Value[0] == '1.79769313486232E+300' + - dsc_types_actual.DoubleArrayParam.Value[1] == '3.56821831681516' + - dsc_types_actual.DateTimeParam.Type == 'System.DateTime' + - dsc_types_actual.DateTimeParam.Value == '2019-02-22T17:57:31.2311890+00:00' + - dsc_types_actual.DateTimeArrayParam.Type == 'System.DateTime[]' + - dsc_types_actual.DateTimeArrayParam.Value == ['2019-02-22T13:57:31.2311890+00:00', '2019-02-22T09:57:31.2311890+00:00'] + - dsc_types_actual.PSCredentialParam.Type == 'System.Management.Automation.PSCredential' + - dsc_types_actual.PSCredentialParam.Value.username == 'username1' + - dsc_types_actual.PSCredentialParam.Value.password == 'password1' + # Hashtable is actually a CimInstance[] of MSFT_KeyValuePairs + - dsc_types_actual.HashtableParam.Type == 'Microsoft.Management.Infrastructure.CimInstance[]' + - dsc_types_actual.HashtableParam.Value|length == 3 + # Can't guarantee the order of the keys so just check they are the values they could be + - dsc_types_actual.HashtableParam.Value[0].Key in ["key1", "key2", "key3"] + - dsc_types_actual.HashtableParam.Value[0].Value in ["string 1", "1", ""] + - dsc_types_actual.HashtableParam.Value[0]._cim_instance == 'MSFT_KeyValuePair' + - dsc_types_actual.HashtableParam.Value[1].Key in ["key1", "key2", "key3"] + - dsc_types_actual.HashtableParam.Value[1].Value in ["string 1", "1", ""] + - dsc_types_actual.HashtableParam.Value[1]._cim_instance == 'MSFT_KeyValuePair' + - dsc_types_actual.HashtableParam.Value[2].Key in ["key1", "key2", "key3"] + - dsc_types_actual.HashtableParam.Value[2].Value in ["string 1", "1", ""] + - dsc_types_actual.HashtableParam.Value[2]._cim_instance == 'MSFT_KeyValuePair' + - dsc_types_actual.CimInstanceParam.Type == 'Microsoft.Management.Infrastructure.CimInstance' + - dsc_types_actual.CimInstanceParam.Value.Choice == None + - dsc_types_actual.CimInstanceParam.Value.IntValue == None + - dsc_types_actual.CimInstanceParam.Value.KeyValue == 'a' + - dsc_types_actual.CimInstanceParam.Value.StringArrayValue == None + - dsc_types_actual.CimInstanceParam.Value.StringValue == None + - dsc_types_actual.CimInstanceParam.Value._cim_instance == "ANSIBLE_xTestClass" + - dsc_types_actual.CimInstanceArrayParam.Type == 'Microsoft.Management.Infrastructure.CimInstance[]' + - dsc_types_actual.CimInstanceArrayParam.Value|length == 2 + - dsc_types_actual.CimInstanceArrayParam.Value[0].Choice == 'Choice1' + - dsc_types_actual.CimInstanceArrayParam.Value[0].IntValue == 1 + - dsc_types_actual.CimInstanceArrayParam.Value[0].KeyValue == 'b' + - dsc_types_actual.CimInstanceArrayParam.Value[0].StringArrayValue == ['abc', 'def'] + - dsc_types_actual.CimInstanceArrayParam.Value[0].StringValue == 'string 1' + - dsc_types_actual.CimInstanceArrayParam.Value[0]._cim_instance == 'ANSIBLE_xTestClass' + - dsc_types_actual.CimInstanceArrayParam.Value[1].Choice == 'Choice2' + - dsc_types_actual.CimInstanceArrayParam.Value[1].IntValue == 2 + - dsc_types_actual.CimInstanceArrayParam.Value[1].KeyValue == 'c' + - dsc_types_actual.CimInstanceArrayParam.Value[1].StringArrayValue == ['ghi', 'jkl'] + - dsc_types_actual.CimInstanceArrayParam.Value[1].StringValue == 'string 2' + - dsc_types_actual.CimInstanceArrayParam.Value[1]._cim_instance == 'ANSIBLE_xTestClass' + - dsc_types_actual.NestedCimInstanceParam.Type == 'Microsoft.Management.Infrastructure.CimInstance' + - dsc_types_actual.NestedCimInstanceParam.Value.CimArrayValue|length == 1 + - dsc_types_actual.NestedCimInstanceParam.Value.CimArrayValue[0].Choice == 'Choice2' + - dsc_types_actual.NestedCimInstanceParam.Value.CimArrayValue[0].IntValue == None + - dsc_types_actual.NestedCimInstanceParam.Value.CimArrayValue[0].KeyValue == 'e' + - dsc_types_actual.NestedCimInstanceParam.Value.CimArrayValue[0].StringArrayValue == None + - dsc_types_actual.NestedCimInstanceParam.Value.CimArrayValue[0].StringValue == None + - dsc_types_actual.NestedCimInstanceParam.Value.CimArrayValue[0]._cim_instance == 'ANSIBLE_xTestClass' + - dsc_types_actual.NestedCimInstanceParam.Value.CimValue.Choice == None + - dsc_types_actual.NestedCimInstanceParam.Value.CimValue.IntValue == None + - dsc_types_actual.NestedCimInstanceParam.Value.CimValue.KeyValue == 'd' + - dsc_types_actual.NestedCimInstanceParam.Value.CimValue.StringArrayValue == None + - dsc_types_actual.NestedCimInstanceParam.Value.CimValue.StringValue == None + - dsc_types_actual.NestedCimInstanceParam.Value.CimValue._cim_instance == 'ANSIBLE_xTestClass' + - dsc_types_actual.NestedCimInstanceParam.Value.HashValue|length == 1 + - dsc_types_actual.NestedCimInstanceParam.Value.HashValue[0].Key == 'a' + - dsc_types_actual.NestedCimInstanceParam.Value.HashValue[0].Value == 'a' + - dsc_types_actual.NestedCimInstanceParam.Value.HashValue[0]._cim_instance == 'MSFT_KeyValuePair' + - dsc_types_actual.NestedCimInstanceParam.Value.IntValue == 300 + - dsc_types_actual.NestedCimInstanceParam.Value.KeyValue == 'key value' + - dsc_types_actual.NestedCimInstanceParam.Value._cim_instance == 'ANSIBLE_xNestedClass' + +- name: test DSC with all types older version + win_dsc: + resource_name: xTestResource + module_version: 1.0.0 + Path: '{{ remote_tmp_dir }}\test-types.json' + Ensure: Absent + StringParam: string param old + CimInstanceArrayParam: + - Key: old key + StringValue: string old 1 + IntValue: 0 + StringArrayValue: + - zyx + - wvu + register: dsc_types_old + +- name: get result of test DSC with all types older version + slurp: + path: '{{ remote_tmp_dir }}\test-types.json' + register: dsc_types_old_raw + +- name: convert result of test DSC with all types to dict + set_fact: + dsc_types_old_actual: '{{ dsc_types_old_raw.content | b64decode | from_json }}' + +- name: assert test DSC with all types older version + assert: + that: + - dsc_types_old is changed + - dsc_types_old.module_version == '1.0.0' + - not dsc_types_old.reboot_required + - dsc_types_old_actual.Version == '1.0.0' + - dsc_types_old_actual.Verbose.Value.IsPresent + - dsc_types_old_actual.DefaultParam.Value == 'Default' + - dsc_types_old_actual.Ensure.Value == 'Absent' + - dsc_types_old_actual.Path.Value == remote_tmp_dir + "\\test-types.json" + - dsc_types_old_actual.StringParam.Type == 'System.String' + - dsc_types_old_actual.StringParam.Value == 'string param old' + - dsc_types_old_actual.CimInstanceArrayParam.Type == 'Microsoft.Management.Infrastructure.CimInstance[]' + - dsc_types_old_actual.CimInstanceArrayParam.Value|length == 1 + - not dsc_types_old_actual.CimInstanceArrayParam.Value[0].Choice is defined # 1.0.0 does not have a Choice option + - dsc_types_old_actual.CimInstanceArrayParam.Value[0].IntValue == 0 + - dsc_types_old_actual.CimInstanceArrayParam.Value[0].Key == 'old key' + - dsc_types_old_actual.CimInstanceArrayParam.Value[0].StringArrayValue == ['zyx', 'wvu'] + - dsc_types_old_actual.CimInstanceArrayParam.Value[0].StringValue == 'string old 1' + - dsc_types_old_actual.CimInstanceArrayParam.Value[0]._cim_instance == 'ANSIBLE_xTestClass' diff --git a/test/integration/targets/incidental_win_hosts/aliases b/test/integration/targets/incidental_win_hosts/aliases new file mode 100644 index 0000000000..a5fc90dcf4 --- /dev/null +++ b/test/integration/targets/incidental_win_hosts/aliases @@ -0,0 +1,2 @@ +shippable/windows/incidental +windows diff --git a/test/integration/targets/incidental_win_hosts/defaults/main.yml b/test/integration/targets/incidental_win_hosts/defaults/main.yml new file mode 100644 index 0000000000..c6270216d6 --- /dev/null +++ b/test/integration/targets/incidental_win_hosts/defaults/main.yml @@ -0,0 +1,13 @@ +--- +test_win_hosts_cname: testhost +test_win_hosts_ip: 192.168.168.1 + +test_win_hosts_aliases_set: + - alias1 + - alias2 + - alias3 + - alias4 + +test_win_hosts_aliases_remove: + - alias3 + - alias4 diff --git a/test/integration/targets/incidental_win_hosts/meta/main.yml b/test/integration/targets/incidental_win_hosts/meta/main.yml new file mode 100644 index 0000000000..9f37e96cd9 --- /dev/null +++ b/test/integration/targets/incidental_win_hosts/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/test/integration/targets/incidental_win_hosts/tasks/main.yml b/test/integration/targets/incidental_win_hosts/tasks/main.yml new file mode 100644 index 0000000000..0997375f9f --- /dev/null +++ b/test/integration/targets/incidental_win_hosts/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- name: take a copy of the original hosts file + win_copy: + src: C:\Windows\System32\drivers\etc\hosts + dest: '{{ remote_tmp_dir }}\hosts' + remote_src: yes + +- block: + - name: run tests + include_tasks: tests.yml + + always: + - name: restore hosts file + win_copy: + src: '{{ remote_tmp_dir }}\hosts' + dest: C:\Windows\System32\drivers\etc\hosts + remote_src: yes diff --git a/test/integration/targets/incidental_win_hosts/tasks/tests.yml b/test/integration/targets/incidental_win_hosts/tasks/tests.yml new file mode 100644 index 0000000000..a29e01a708 --- /dev/null +++ b/test/integration/targets/incidental_win_hosts/tasks/tests.yml @@ -0,0 +1,189 @@ +--- + +- name: add a simple host with address + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + register: add_ip + +- assert: + that: + - "add_ip.changed == true" + +- name: get actual dns result + win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ test_win_hosts_cname }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }" + register: add_ip_actual + +- assert: + that: + - "add_ip_actual.stdout_lines[0]|lower == 'true'" + +- name: add a simple host with ipv4 address (idempotent) + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + register: add_ip + +- assert: + that: + - "add_ip.changed == false" + +- name: remove simple host + win_hosts: + state: absent + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + register: remove_ip + +- assert: + that: + - "remove_ip.changed == true" + +- name: get actual dns result + win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ test_win_hosts_cname}}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }" + register: remove_ip_actual + failed_when: "remove_ip_actual.rc == 0" + +- assert: + that: + - "remove_ip_actual.stdout_lines[0]|lower == 'false'" + +- name: remove simple host (idempotent) + win_hosts: + state: absent + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + register: remove_ip + +- assert: + that: + - "remove_ip.changed == false" + +- name: add host and set aliases + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + aliases: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}" + action: set + register: set_aliases + +- assert: + that: + - "set_aliases.changed == true" + +- name: get actual dns result for host + win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ test_win_hosts_cname }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }" + register: set_aliases_actual_host + +- assert: + that: + - "set_aliases_actual_host.stdout_lines[0]|lower == 'true'" + +- name: get actual dns results for aliases + win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }" + register: set_aliases_actual + with_items: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}" + +- assert: + that: + - "item.stdout_lines[0]|lower == 'true'" + with_items: "{{ set_aliases_actual.results }}" + +- name: add host and set aliases (idempotent) + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + aliases: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}" + action: set + register: set_aliases + +- assert: + that: + - "set_aliases.changed == false" + +- name: remove aliases from the list + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + aliases: "{{ test_win_hosts_aliases_remove }}" + action: remove + register: remove_aliases + +- assert: + that: + - "remove_aliases.changed == true" + +- name: get actual dns result for removed aliases + win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }" + register: remove_aliases_removed_actual + failed_when: "remove_aliases_removed_actual.rc == 0" + with_items: "{{ test_win_hosts_aliases_remove }}" + +- assert: + that: + - "item.stdout_lines[0]|lower == 'false'" + with_items: "{{ remove_aliases_removed_actual.results }}" + +- name: get actual dns result for remaining aliases + win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }" + register: remove_aliases_remain_actual + with_items: "{{ test_win_hosts_aliases_set | difference(test_win_hosts_aliases_remove) }}" + +- assert: + that: + - "item.stdout_lines[0]|lower == 'true'" + with_items: "{{ remove_aliases_remain_actual.results }}" + +- name: remove aliases from the list (idempotent) + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + aliases: "{{ test_win_hosts_aliases_remove }}" + action: remove + register: remove_aliases + +- assert: + that: + - "remove_aliases.changed == false" + +- name: add aliases back + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + aliases: "{{ test_win_hosts_aliases_remove }}" + action: add + register: add_aliases + +- assert: + that: + - "add_aliases.changed == true" + +- name: get actual dns results for aliases + win_shell: "try{ [array]$t = [Net.DNS]::GetHostEntry('{{ item }}') } catch { return 'false' } if ($t[0].HostName -eq '{{ test_win_hosts_cname }}' -and $t[0].AddressList[0].toString() -eq '{{ test_win_hosts_ip }}'){ return 'true' } else { return 'false' }" + register: add_aliases_actual + with_items: "{{ test_win_hosts_aliases_set | union(test_win_hosts_aliases_remove) }}" + +- assert: + that: + - "item.stdout_lines[0]|lower == 'true'" + with_items: "{{ add_aliases_actual.results }}" + +- name: add aliases back (idempotent) + win_hosts: + state: present + ip_address: "{{ test_win_hosts_ip }}" + canonical_name: "{{ test_win_hosts_cname }}" + aliases: "{{ test_win_hosts_aliases_remove }}" + action: add + register: add_aliases + +- assert: + that: + - "add_aliases.changed == false" diff --git a/test/integration/targets/incidental_win_lineinfile/aliases b/test/integration/targets/incidental_win_lineinfile/aliases new file mode 100644 index 0000000000..194cbc3f2e --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/aliases @@ -0,0 +1,3 @@ +shippable/windows/incidental +windows +skip/windows/2016 # Host takes a while to run and module isn't OS dependent diff --git a/test/integration/targets/incidental_win_lineinfile/files/test.txt b/test/integration/targets/incidental_win_lineinfile/files/test.txt new file mode 100644 index 0000000000..8187db9f02 --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/files/test.txt @@ -0,0 +1,5 @@ +This is line 1 +This is line 2 +REF this is a line for backrefs REF +This is line 4 +This is line 5 diff --git a/test/integration/targets/incidental_win_lineinfile/files/test_linebreak.txt b/test/integration/targets/incidental_win_lineinfile/files/test_linebreak.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/files/test_linebreak.txt diff --git a/test/integration/targets/incidental_win_lineinfile/files/test_quoting.txt b/test/integration/targets/incidental_win_lineinfile/files/test_quoting.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/files/test_quoting.txt diff --git a/test/integration/targets/incidental_win_lineinfile/files/testempty.txt b/test/integration/targets/incidental_win_lineinfile/files/testempty.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/files/testempty.txt diff --git a/test/integration/targets/incidental_win_lineinfile/files/testnoeof.txt b/test/integration/targets/incidental_win_lineinfile/files/testnoeof.txt new file mode 100644 index 0000000000..152780b9ff --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/files/testnoeof.txt @@ -0,0 +1,2 @@ +This is line 1 +This is line 2
\ No newline at end of file diff --git a/test/integration/targets/incidental_win_lineinfile/meta/main.yml b/test/integration/targets/incidental_win_lineinfile/meta/main.yml new file mode 100644 index 0000000000..e0ff46db12 --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - incidental_win_prepare_tests diff --git a/test/integration/targets/incidental_win_lineinfile/tasks/main.yml b/test/integration/targets/incidental_win_lineinfile/tasks/main.yml new file mode 100644 index 0000000000..e5f047bec3 --- /dev/null +++ b/test/integration/targets/incidental_win_lineinfile/tasks/main.yml @@ -0,0 +1,708 @@ +# Test code for the win_lineinfile module, adapted from the standard lineinfile module tests +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + + +- name: deploy the test file for lineinfile + win_copy: src=test.txt dest={{win_output_dir}}/test.txt + register: result + +- name: assert that the test file was deployed + assert: + that: + - "result.changed == true" + +- name: stat the test file + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: check win_stat file result + assert: + that: + - "result.stat.exists" + - "not result.stat.isdir" + - "result.stat.checksum == '5feac65e442c91f557fc90069ce6efc4d346ab51'" + - "result is not failed" + - "result is not changed" + + +- name: insert a line at the beginning of the file, and back it up + win_lineinfile: dest={{win_output_dir}}/test.txt state=present line="New line at the beginning" insertbefore="BOF" backup=yes + register: result + +- name: check backup_file + win_stat: + path: '{{ result.backup_file }}' + register: backup_file + +- name: assert that the line was inserted at the head of the file + assert: + that: + - result.changed == true + - result.msg == 'line added' + - backup_file.stat.exists == true + +- name: stat the backup file + win_stat: path={{result.backup}} + register: result + +- name: assert the backup file matches the previous hash + assert: + that: + - "result.stat.checksum == '5feac65e442c91f557fc90069ce6efc4d346ab51'" + +- name: stat the test after the insert at the head + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test hash is what we expect for the file with the insert at the head + assert: + that: + - "result.stat.checksum == 'b526e2e044defc64dfb0fad2f56e105178f317d8'" + +- name: insert a line at the end of the file + win_lineinfile: dest={{win_output_dir}}/test.txt state=present line="New line at the end" insertafter="EOF" + register: result + +- name: assert that the line was inserted at the end of the file + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the test after the insert at the end + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the insert at the end + assert: + that: + - "result.stat.checksum == 'dd5e207e28ce694ab18e41c2b16deb74fde93b14'" + +- name: insert a line after the first line + win_lineinfile: dest={{win_output_dir}}/test.txt state=present line="New line after line 1" insertafter="^This is line 1$" + register: result + +- name: assert that the line was inserted after the first line + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the test after insert after the first line + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the insert after the first line + assert: + that: + - "result.stat.checksum == '604b17405f2088e6868af9680b7834087acdc8f4'" + +- name: insert a line before the last line + win_lineinfile: dest={{win_output_dir}}/test.txt state=present line="New line before line 5" insertbefore="^This is line 5$" + register: result + +- name: assert that the line was inserted before the last line + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the test after the insert before the last line + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the insert before the last line + assert: + that: + - "result.stat.checksum == '8f5b30e8f01578043d782e5a68d4c327e75a6e34'" + +- name: replace a line with backrefs + win_lineinfile: dest={{win_output_dir}}/test.txt state=present line="This is line 3" backrefs=yes regexp="^(REF).*$" + register: result + +- name: assert that the line with backrefs was changed + assert: + that: + - "result.changed == true" + - "result.msg == 'line replaced'" + +- name: stat the test after the backref line was replaced + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == 'ef6b02645908511a2cfd2df29d50dd008897c580'" + +- name: remove the middle line + win_lineinfile: dest={{win_output_dir}}/test.txt state=absent regexp="^This is line 3$" + register: result + +- name: assert that the line was removed + assert: + that: + - "result.changed == true" + - "result.msg == '1 line(s) removed'" + +- name: stat the test after the middle line was removed + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the middle line was removed + assert: + that: + - "result.stat.checksum == '11695efa472be5c31c736bc43e055f8ac90eabdf'" + +- name: run a validation script that succeeds + win_lineinfile: dest={{win_output_dir}}/test.txt state=absent regexp="^This is line 5$" validate="sort.exe %s" + register: result + +- name: assert that the file validated after removing a line + assert: + that: + - "result.changed == true" + - "result.msg == '1 line(s) removed'" + +- name: stat the test after the validation succeeded + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the validation succeeded + assert: + that: + - "result.stat.checksum == '39c38a30aa6ac6af9ec41f54c7ed7683f1249347'" + +- name: run a validation script that fails + win_lineinfile: dest={{win_output_dir}}/test.txt state=absent regexp="^This is line 1$" validate="sort.exe %s.foo" + register: result + ignore_errors: yes + +- name: assert that the validate failed + assert: + that: + - "result.failed == true" + +- name: stat the test after the validation failed + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches the previous after the validation failed + assert: + that: + - "result.stat.checksum == '39c38a30aa6ac6af9ec41f54c7ed7683f1249347'" + +- name: use create=yes + win_lineinfile: dest={{win_output_dir}}/new_test.txt create=yes insertbefore=BOF state=present line="This is a new file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: validate that the newly created file exists + win_stat: path={{win_output_dir}}/new_test.txt + register: result + ignore_errors: yes + +- name: assert the newly created test checksum matches + assert: + that: + - "result.stat.checksum == '84faac1183841c57434693752fc3debc91b9195d'" + +# Test EOF in cases where file has no newline at EOF +- name: testnoeof deploy the file for lineinfile + win_copy: src=testnoeof.txt dest={{win_output_dir}}/testnoeof.txt + register: result + +- name: testnoeof insert a line at the end of the file + win_lineinfile: dest={{win_output_dir}}/testnoeof.txt state=present line="New line at the end" insertafter="EOF" + register: result + +- name: testempty assert that the line was inserted at the end of the file + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: testnoeof stat the no newline EOF test after the insert at the end + win_stat: path={{win_output_dir}}/testnoeof.txt + register: result + +- name: testnoeof assert test checksum matches after the insert at the end + assert: + that: + - "result.stat.checksum == '229852b09f7e9921fbcbb0ee0166ba78f7f7f261'" + +- name: add multiple lines at the end of the file + win_lineinfile: dest={{win_output_dir}}/test.txt state=present line="This is a line\r\nwith newline character" insertafter="EOF" + register: result + +- name: assert that the multiple lines was inserted + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat file after adding multiple lines + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after inserting multiple lines + assert: + that: + - "result.stat.checksum == '1401413cd4eac732be66cd6aceddd334c4240f86'" + + + +# Test EOF with empty file to make sure no unnecessary newline is added +- name: testempty deploy the testempty file for lineinfile + win_copy: src=testempty.txt dest={{win_output_dir}}/testempty.txt + register: result + +- name: testempty insert a line at the end of the file + win_lineinfile: dest={{win_output_dir}}/testempty.txt state=present line="New line at the end" insertafter="EOF" + register: result + +- name: testempty assert that the line was inserted at the end of the file + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: testempty stat the test after the insert at the end + win_stat: path={{win_output_dir}}/testempty.txt + register: result + +- name: testempty assert test checksum matches after the insert at the end + assert: + that: + - "result.stat.checksum == 'd3d34f11edda51be7ca5dcb0757cf3e1257c0bfe'" + + + +- name: replace a line with backrefs included in the line + win_lineinfile: dest={{win_output_dir}}/test.txt state=present line="New $1 created with the backref" backrefs=yes regexp="^This is (line 4)$" + register: result + +- name: assert that the line with backrefs was changed + assert: + that: + - "result.changed == true" + - "result.msg == 'line replaced'" + +- name: stat the test after the backref line was replaced + win_stat: path={{win_output_dir}}/test.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == 'e6ff42e926dac2274c93dff0b8a323e07ae09149'" + +################################################################### +# issue 8535 + +- name: create a new file for testing quoting issues + win_copy: src=test_quoting.txt dest={{win_output_dir}}/test_quoting.txt + register: result + +- name: assert the new file was created + assert: + that: + - result.changed + +- name: use with_items to add code-like strings to the quoting txt file + win_lineinfile: > + dest={{win_output_dir}}/test_quoting.txt + line="{{ item }}" + insertbefore="BOF" + with_items: + - "'foo'" + - "dotenv.load();" + - "var dotenv = require('dotenv');" + register: result + +- name: assert the quote test file was modified correctly + assert: + that: + - result.results|length == 3 + - result.results[0].changed + - result.results[0].item == "'foo'" + - result.results[1].changed + - result.results[1].item == "dotenv.load();" + - result.results[2].changed + - result.results[2].item == "var dotenv = require('dotenv');" + +- name: stat the quote test file + win_stat: path={{win_output_dir}}/test_quoting.txt + register: result + +- name: assert test checksum matches for quote test file + assert: + that: + - "result.stat.checksum == 'f3bccdbdfa1d7176c497ef87d04957af40ab48d2'" + +- name: append a line into the quoted file with a single quote + win_lineinfile: dest={{win_output_dir}}/test_quoting.txt line="import g'" + register: result + +- name: assert that the quoted file was changed + assert: + that: + - result.changed + +- name: stat the quote test file + win_stat: path={{win_output_dir}}/test_quoting.txt + register: result + +- name: assert test checksum matches adding line with single quote + assert: + that: + - "result.stat.checksum == 'dabf4cbe471e1797d8dcfc773b6b638c524d5237'" + +- name: insert a line into the quoted file with many double quotation strings + win_lineinfile: dest={{win_output_dir}}/test_quoting.txt line='"quote" and "unquote"' + register: result + +- name: assert that the quoted file was changed + assert: + that: + - result.changed + +- name: stat the quote test file + win_stat: path={{win_output_dir}}/test_quoting.txt + register: result + +- name: assert test checksum matches quoted line added + assert: + that: + - "result.stat.checksum == '9dc1fc1ff19942e2936564102ad37134fa83b91d'" + + +# Windows vs. Unix line separator test cases + +- name: Create windows test file with initial line + win_lineinfile: dest={{win_output_dir}}/test_windows_sep.txt create=yes insertbefore=BOF state=present line="This is a new file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: validate that the newly created file exists + win_stat: path={{win_output_dir}}/test_windows_sep.txt + register: result + +- name: assert the newly created file checksum matches + assert: + that: + - "result.stat.checksum == '84faac1183841c57434693752fc3debc91b9195d'" + +- name: Test appending to the file using the default (windows) line separator + win_lineinfile: dest={{win_output_dir}}/test_windows_sep.txt insertbefore=EOF state=present line="This is the last line" + register: result + +- name: assert that the new line was added + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the file + win_stat: path={{win_output_dir}}/test_windows_sep.txt + register: result + +- name: assert the file checksum matches expected checksum + assert: + that: + - "result.stat.checksum == '71a17ddd1d57ed7c7912e4fd11ecb2ead0b27033'" + + +- name: Create unix test file with initial line + win_lineinfile: dest={{win_output_dir}}/test_unix_sep.txt create=yes insertbefore=BOF state=present line="This is a new file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: validate that the newly created file exists + win_stat: path={{win_output_dir}}/test_unix_sep.txt + register: result + +- name: assert the newly created file checksum matches + assert: + that: + - "result.stat.checksum == '84faac1183841c57434693752fc3debc91b9195d'" + +- name: Test appending to the file using unix line separator + win_lineinfile: dest={{win_output_dir}}/test_unix_sep.txt insertbefore=EOF state=present line="This is the last line" newline="unix" + register: result + +- name: assert that the new line was added + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the file + win_stat: path={{win_output_dir}}/test_unix_sep.txt + register: result + +- name: assert the file checksum matches expected checksum + assert: + that: + - "result.stat.checksum == 'f1f634a37ab1c73efb77a71a5ad2cc87b61b17ae'" + + +# Encoding management test cases + +# Default (auto) encoding should use utf-8 with no BOM +- name: Test create file without explicit encoding results in utf-8 without BOM + win_lineinfile: dest={{win_output_dir}}/test_auto_utf8.txt create=yes insertbefore=BOF state=present line="This is a new utf-8 file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-8'" + +- name: validate that the newly created file exists + win_stat: path={{win_output_dir}}/test_auto_utf8.txt + register: result + +- name: assert the newly created file checksum matches + assert: + that: + - "result.stat.checksum == 'b69fcbacca8291a4668f57fba91d7c022f1c3dc7'" + +- name: Test appending to the utf-8 without BOM file - should autodetect UTF-8 no BOM + win_lineinfile: dest={{win_output_dir}}/test_auto_utf8.txt insertbefore=EOF state=present line="This is the last line" + register: result + +- name: assert that the new line was added and encoding did not change + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-8'" + +- name: stat the file + win_stat: path={{win_output_dir}}/test_auto_utf8.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - "result.stat.checksum == '64d747f1ebf8c9d793dbfd27126e4152d39a3848'" + + +# UTF-8 explicit (with BOM) +- name: Test create file with explicit utf-8 encoding results in utf-8 with a BOM + win_lineinfile: dest={{win_output_dir}}/test_utf8.txt create=yes encoding="utf-8" insertbefore=BOF state=present line="This is a new utf-8 file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-8'" + +- name: validate that the newly created file exists + win_stat: path={{win_output_dir}}/test_utf8.txt + register: result + +- name: assert the newly created file checksum matches + assert: + that: + - "result.stat.checksum == 'd45344b2b3bf1cf90eae851b40612f5f37a88bbb'" + +- name: Test appending to the utf-8 with BOM file - should autodetect utf-8 with BOM encoding + win_lineinfile: dest={{win_output_dir}}/test_utf8.txt insertbefore=EOF state=present line="This is the last line" + register: result + +- name: assert that the new line was added and encoding did not change + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-8'" + +- name: stat the file + win_stat: path={{win_output_dir}}/test_utf8.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - "result.stat.checksum == '9b84254489f40f258871a4c6573cacc65895ee1a'" + + +# UTF-16 explicit +- name: Test create file with explicit utf-16 encoding + win_lineinfile: dest={{win_output_dir}}/test_utf16.txt create=yes encoding="utf-16" insertbefore=BOF state=present line="This is a new utf-16 file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-16'" + +- name: validate that the newly created file exists + win_stat: path={{win_output_dir}}/test_utf16.txt + register: result + +- name: assert the newly created file checksum matches + assert: + that: + - "result.stat.checksum == '785b0693cec13b60e2c232782adeda2f8a967434'" + +- name: Test appending to the utf-16 file - should autodetect utf-16 encoding + win_lineinfile: dest={{win_output_dir}}/test_utf16.txt insertbefore=EOF state=present line="This is the last line" + register: result + +- name: assert that the new line was added and encoding did not change + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-16'" + +- name: stat the file + win_stat: path={{win_output_dir}}/test_utf16.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - "result.stat.checksum == '70e4eb3ba795e1ba94d262db47e4fd17c64b2e73'" + +# UTF-32 explicit +- name: Test create file with explicit utf-32 encoding + win_lineinfile: dest={{win_output_dir}}/test_utf32.txt create=yes encoding="utf-32" insertbefore=BOF state=present line="This is a new utf-32 file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-32'" + +- name: validate that the newly created file exists + win_stat: path={{win_output_dir}}/test_utf32.txt + register: result + +- name: assert the newly created file checksum matches + assert: + that: + - "result.stat.checksum == '7a6e3f3604c0def431aaa813173a4ddaa10fd1fb'" + +- name: Test appending to the utf-32 file - should autodetect utf-32 encoding + win_lineinfile: dest={{win_output_dir}}/test_utf32.txt insertbefore=EOF state=present line="This is the last line" + register: result + +- name: assert that the new line was added and encoding did not change + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.encoding == 'utf-32'" + +- name: stat the file + win_stat: path={{win_output_dir}}/test_utf32.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - "result.stat.checksum == '66a72e71f42c4775f4326da95cfe82c8830e5022'" + +######################################################################### +# issue #33858 +# \r\n causes line break instead of printing literally which breaks paths. + +- name: create testing file + win_copy: + src: test_linebreak.txt + dest: "{{win_output_dir}}/test_linebreak.txt" + +- name: stat the test file + win_stat: + path: "{{win_output_dir}}/test_linebreak.txt" + register: result + +# (Get-FileHash -path C:\ansible\test\integration\targets\win_lineinfile\files\test_linebreak.txt -Algorithm sha1).hash.tolower() +- name: check win_stat file result + assert: + that: + - result.stat.exists + - not result.stat.isdir + - result.stat.checksum == 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + - result is not failed + - result is not changed + +- name: insert path c:\return\new to test file + win_lineinfile: + dest: "{{win_output_dir}}/test_linebreak.txt" + line: c:\return\new + register: result_literal + +- name: insert path "c:\return\new" to test file, will cause line breaks + win_lineinfile: + dest: "{{win_output_dir}}/test_linebreak.txt" + line: "c:\return\new" + register: result_expand + +- name: assert that the lines were inserted + assert: + that: + - result_literal.changed == true + - result_literal.msg == 'line added' + - result_expand.changed == true + - result_expand.msg == 'line added' + +- name: stat the test file + win_stat: + path: "{{win_output_dir}}/test_linebreak.txt" + register: result + +- debug: + var: result + verbosity: 1 + +# expect that the file looks like this: +# c:\return\new +# c: +# eturn +# ew #or c:eturnew on windows +- name: assert that one line is literal and the other has breaks + assert: + that: + - result.stat.checksum == 'd2dfd11bc70526ff13a91153c76a7ae5595a845b' diff --git a/test/integration/targets/incidental_win_ping/aliases b/test/integration/targets/incidental_win_ping/aliases new file mode 100644 index 0000000000..a5fc90dcf4 --- /dev/null +++ b/test/integration/targets/incidental_win_ping/aliases @@ -0,0 +1,2 @@ +shippable/windows/incidental +windows diff --git a/test/integration/targets/incidental_win_ping/library/win_ping_set_attr.ps1 b/test/integration/targets/incidental_win_ping/library/win_ping_set_attr.ps1 new file mode 100644 index 0000000000..f17049643b --- /dev/null +++ b/test/integration/targets/incidental_win_ping/library/win_ping_set_attr.ps1 @@ -0,0 +1,31 @@ +#!powershell +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# POWERSHELL_COMMON + +$params = Parse-Args $args $true; + +$data = Get-Attr $params "data" "pong"; + +$result = @{ + changed = $false + ping = "pong" +}; + +# Test that Set-Attr will replace an existing attribute. +Set-Attr $result "ping" $data + +Exit-Json $result; diff --git a/test/integration/targets/incidental_win_ping/library/win_ping_strict_mode_error.ps1 b/test/integration/targets/incidental_win_ping/library/win_ping_strict_mode_error.ps1 new file mode 100644 index 0000000000..508174afcc --- /dev/null +++ b/test/integration/targets/incidental_win_ping/library/win_ping_strict_mode_error.ps1 @@ -0,0 +1,30 @@ +#!powershell +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# POWERSHELL_COMMON + +$params = Parse-Args $args $true; + +$params.thisPropertyDoesNotExist + +$data = Get-Attr $params "data" "pong"; + +$result = @{ + changed = $false + ping = $data +}; + +Exit-Json $result; diff --git a/test/integration/targets/incidental_win_ping/library/win_ping_syntax_error.ps1 b/test/integration/targets/incidental_win_ping/library/win_ping_syntax_error.ps1 new file mode 100644 index 0000000000..d4c9f07ad5 --- /dev/null +++ b/test/integration/targets/incidental_win_ping/library/win_ping_syntax_error.ps1 @@ -0,0 +1,30 @@ +#!powershell +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# POWERSHELL_COMMON + +$blah = 'I can't quote my strings correctly.' + +$params = Parse-Args $args $true; + +$data = Get-Attr $params "data" "pong"; + +$result = @{ + changed = $false + ping = $data +}; + +Exit-Json $result; diff --git a/test/integration/targets/incidental_win_ping/library/win_ping_throw.ps1 b/test/integration/targets/incidental_win_ping/library/win_ping_throw.ps1 new file mode 100644 index 0000000000..7306f4d280 --- /dev/null +++ b/test/integration/targets/incidental_win_ping/library/win_ping_throw.ps1 @@ -0,0 +1,30 @@ +#!powershell +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# POWERSHELL_COMMON + +throw + +$params = Parse-Args $args $true; + +$data = Get-Attr $params "data" "pong"; + +$result = @{ + changed = $false + ping = $data +}; + +Exit-Json $result; diff --git a/test/integration/targets/incidental_win_ping/library/win_ping_throw_string.ps1 b/test/integration/targets/incidental_win_ping/library/win_ping_throw_string.ps1 new file mode 100644 index 0000000000..09e3b7cb45 --- /dev/null +++ b/test/integration/targets/incidental_win_ping/library/win_ping_throw_string.ps1 @@ -0,0 +1,30 @@ +#!powershell +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# POWERSHELL_COMMON + +throw "no ping for you" + +$params = Parse-Args $args $true; + +$data = Get-Attr $params "data" "pong"; + +$result = @{ + changed = $false + ping = $data +}; + +Exit-Json $result; diff --git a/test/integration/targets/incidental_win_ping/tasks/main.yml b/test/integration/targets/incidental_win_ping/tasks/main.yml new file mode 100644 index 0000000000..a7e6ba7fc4 --- /dev/null +++ b/test/integration/targets/incidental_win_ping/tasks/main.yml @@ -0,0 +1,67 @@ +# test code for the win_ping module +# (c) 2014, Chris Church <chris@ninemoreminutes.com> + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +- name: test win_ping + action: win_ping + register: win_ping_result + +- name: check win_ping result + assert: + that: + - win_ping_result is not failed + - win_ping_result is not changed + - win_ping_result.ping == 'pong' + +- name: test win_ping with data + win_ping: + data: ☠ + register: win_ping_with_data_result + +- name: check win_ping result with data + assert: + that: + - win_ping_with_data_result is not failed + - win_ping_with_data_result is not changed + - win_ping_with_data_result.ping == '☠' + +- name: test win_ping.ps1 with data as complex args + # win_ping.ps1: # TODO: do we want to actually support this? no other tests that I can see... + win_ping: + data: bleep + register: win_ping_ps1_result + +- name: check win_ping.ps1 result with data + assert: + that: + - win_ping_ps1_result is not failed + - win_ping_ps1_result is not changed + - win_ping_ps1_result.ping == 'bleep' + +- name: test win_ping using data=crash so that it throws an exception + win_ping: + data: crash + register: win_ping_crash_result + ignore_errors: yes + +- name: check win_ping_crash result + assert: + that: + - win_ping_crash_result is failed + - win_ping_crash_result is not changed + - 'win_ping_crash_result.msg == "Unhandled exception while executing module: boom"' + - '"throw \"boom\"" in win_ping_crash_result.exception' diff --git a/test/integration/targets/incidental_win_prepare_tests/aliases b/test/integration/targets/incidental_win_prepare_tests/aliases new file mode 100644 index 0000000000..136c05e0d0 --- /dev/null +++ b/test/integration/targets/incidental_win_prepare_tests/aliases @@ -0,0 +1 @@ +hidden diff --git a/test/integration/targets/incidental_win_prepare_tests/meta/main.yml b/test/integration/targets/incidental_win_prepare_tests/meta/main.yml new file mode 100644 index 0000000000..cf5427b608 --- /dev/null +++ b/test/integration/targets/incidental_win_prepare_tests/meta/main.yml @@ -0,0 +1,3 @@ +--- + +allow_duplicates: yes diff --git a/test/integration/targets/incidental_win_prepare_tests/tasks/main.yml b/test/integration/targets/incidental_win_prepare_tests/tasks/main.yml new file mode 100644 index 0000000000..e87b614b22 --- /dev/null +++ b/test/integration/targets/incidental_win_prepare_tests/tasks/main.yml @@ -0,0 +1,29 @@ +# test code for the windows versions of copy, file and template module +# originally +# (c) 2014, Michael DeHaan <michael.dehaan@gmail.com> + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + + +- name: clean out the test directory + win_file: name={{win_output_dir|mandatory}} state=absent + tags: + - prepare + +- name: create the test directory + win_file: name={{win_output_dir}} state=directory + tags: + - prepare diff --git a/test/integration/targets/incidental_win_psexec/aliases b/test/integration/targets/incidental_win_psexec/aliases new file mode 100644 index 0000000000..a5fc90dcf4 --- /dev/null +++ b/test/integration/targets/incidental_win_psexec/aliases @@ -0,0 +1,2 @@ +shippable/windows/incidental +windows diff --git a/test/integration/targets/incidental_win_psexec/meta/main.yml b/test/integration/targets/incidental_win_psexec/meta/main.yml new file mode 100644 index 0000000000..9f37e96cd9 --- /dev/null +++ b/test/integration/targets/incidental_win_psexec/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/test/integration/targets/incidental_win_psexec/tasks/main.yml b/test/integration/targets/incidental_win_psexec/tasks/main.yml new file mode 100644 index 0000000000..27783f9e62 --- /dev/null +++ b/test/integration/targets/incidental_win_psexec/tasks/main.yml @@ -0,0 +1,80 @@ +# Would use [] but this has troubles with PATH and trying to find the executable so just resort to keeping a space +- name: record special path for tests + set_fact: + testing_dir: '{{ remote_tmp_dir }}\ansible win_psexec' + +- name: create special path testing dir + win_file: + path: '{{ testing_dir }}' + state: directory + +- name: Download PsExec + win_get_url: + url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_psexec/PsExec.exe + dest: '{{ testing_dir }}\PsExec.exe' + +- name: Get the existing PATH env var + win_shell: '$env:PATH' + register: system_path + changed_when: False + +- name: Run whoami + win_psexec: + command: whoami.exe + nobanner: true + register: whoami + environment: + PATH: '{{ testing_dir }};{{ system_path.stdout | trim }}' + +- name: Test whoami + assert: + that: + - whoami.rc == 0 + - whoami.stdout == '' + # FIXME: Standard output does not work or is truncated + #- whoami.stdout == '{{ ansible_hostname|lower }}' + +- name: Run whoami as SYSTEM + win_psexec: + command: whoami.exe + system: yes + nobanner: true + executable: '{{ testing_dir }}\PsExec.exe' + register: whoami_as_system + # Seems to be a bug with PsExec where the stdout can be empty, just retry the task to make this test a bit more stable + until: whoami_as_system.rc == 0 and whoami_as_system.stdout == 'nt authority\system' + retries: 3 + delay: 2 + +# FIXME: Behaviour is not consistent on all Windows systems +#- name: Run whoami as ELEVATED +# win_psexec: +# command: whoami.exe +# elevated: yes +# register: whoami_as_elevated +# +## Ensure we have basic facts +#- setup: +# +#- debug: +# msg: '{{ whoami_as_elevated.stdout|lower }} == {{ ansible_hostname|lower }}\{{ ansible_user_id|lower }}' +# +#- name: Test whoami +# assert: +# that: +# - whoami_as_elevated.rc == 0 +# - whoami_as_elevated.stdout|lower == '{{ ansible_hostname|lower }}\{{ ansible_user_id|lower }}' + +- name: Run command with multiple arguments + win_psexec: + command: powershell.exe -NonInteractive "exit 1" + ignore_errors: yes + register: whoami_multiple_args + environment: + PATH: '{{ testing_dir }};{{ system_path.stdout | trim }}' + +- name: Test command with multiple argumetns + assert: + that: + - whoami_multiple_args.rc == 1 + - whoami_multiple_args.psexec_command == "psexec.exe -accepteula powershell.exe -NonInteractive \"exit 1\"" diff --git a/test/integration/targets/incidental_win_reboot/aliases b/test/integration/targets/incidental_win_reboot/aliases new file mode 100644 index 0000000000..a5fc90dcf4 --- /dev/null +++ b/test/integration/targets/incidental_win_reboot/aliases @@ -0,0 +1,2 @@ +shippable/windows/incidental +windows diff --git a/test/integration/targets/incidental_win_reboot/tasks/main.yml b/test/integration/targets/incidental_win_reboot/tasks/main.yml new file mode 100644 index 0000000000..7757e08fcd --- /dev/null +++ b/test/integration/targets/incidental_win_reboot/tasks/main.yml @@ -0,0 +1,70 @@ +--- +- name: make sure win output dir exists + win_file: + path: "{{win_output_dir}}" + state: directory + +- name: reboot with defaults + win_reboot: + +- name: test with negative values for delays + win_reboot: + post_reboot_delay: -0.5 + pre_reboot_delay: -61 + +- name: schedule a reboot for sometime in the future + win_command: shutdown.exe /r /t 599 + +- name: reboot with a shutdown already scheduled + win_reboot: + +# test a reboot that reboots again during the test_command phase +- name: create test file + win_file: + path: '{{win_output_dir}}\win_reboot_test' + state: touch + +- name: reboot with secondary reboot stage + win_reboot: + test_command: '{{ lookup("template", "post_reboot.ps1") }}' + +- name: reboot with test command that fails + win_reboot: + test_command: 'FAIL' + reboot_timeout: 120 + register: reboot_fail_test + failed_when: "reboot_fail_test.msg != 'Timed out waiting for post-reboot test command (timeout=120)'" + +- name: remove SeRemoteShutdownPrivilege + win_user_right: + name: SeRemoteShutdownPrivilege + users: [] + action: set + register: removed_shutdown_privilege + +- block: + - name: try and reboot without required privilege + win_reboot: + register: fail_privilege + failed_when: + - "'Reboot command failed, error was:' not in fail_privilege.msg" + - "'Access is denied.(5)' not in fail_privilege.msg" + + always: + - name: reset the SeRemoteShutdownPrivilege + win_user_right: + name: SeRemoteShutdownPrivilege + users: '{{ removed_shutdown_privilege.removed }}' + action: add + +- name: Use invalid parameter + reboot: + foo: bar + ignore_errors: true + register: invalid_parameter + +- name: Ensure task fails with error + assert: + that: + - invalid_parameter is failed + - "invalid_parameter.msg == 'Invalid options for reboot: foo'" diff --git a/test/integration/targets/incidental_win_reboot/templates/post_reboot.ps1 b/test/integration/targets/incidental_win_reboot/templates/post_reboot.ps1 new file mode 100644 index 0000000000..e4a99a721d --- /dev/null +++ b/test/integration/targets/incidental_win_reboot/templates/post_reboot.ps1 @@ -0,0 +1,8 @@ +if (Test-Path -Path '{{win_output_dir}}\win_reboot_test') { + New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' ` + -Name PendingFileRenameOperations ` + -Value @("\??\{{win_output_dir}}\win_reboot_test`0") ` + -PropertyType MultiString + Restart-Computer -Force + exit 1 +} diff --git a/test/integration/targets/incidental_win_security_policy/aliases b/test/integration/targets/incidental_win_security_policy/aliases new file mode 100644 index 0000000000..a5fc90dcf4 --- /dev/null +++ b/test/integration/targets/incidental_win_security_policy/aliases @@ -0,0 +1,2 @@ +shippable/windows/incidental +windows diff --git a/test/integration/targets/incidental_win_security_policy/library/test_win_security_policy.ps1 b/test/integration/targets/incidental_win_security_policy/library/test_win_security_policy.ps1 new file mode 100644 index 0000000000..5c83c1b5d0 --- /dev/null +++ b/test/integration/targets/incidental_win_security_policy/library/test_win_security_policy.ps1 @@ -0,0 +1,53 @@ +#!powershell + +# WANT_JSON +# POWERSHELL_COMMON + +# basic script to get the lsit of users in a particular right +# this is quite complex to put as a simple script so this is +# just a simple module + +$ErrorActionPreference = 'Stop' + +$params = Parse-Args $args -supports_check_mode $false +$section = Get-AnsibleParam -obj $params -name "section" -type "str" -failifempty $true +$key = Get-AnsibleParam -obj $params -name "key" -type "str" -failifempty $true + +$result = @{ + changed = $false +} + +Function ConvertFrom-Ini($file_path) { + $ini = @{} + switch -Regex -File $file_path { + "^\[(.+)\]" { + $section = $matches[1] + $ini.$section = @{} + } + "(.+?)\s*=(.*)" { + $name = $matches[1].Trim() + $value = $matches[2].Trim() + if ($value -match "^\d+$") { + $value = [int]$value + } elseif ($value.StartsWith('"') -and $value.EndsWith('"')) { + $value = $value.Substring(1, $value.Length - 2) + } + + $ini.$section.$name = $value + } + } + + $ini +} + +$secedit_ini_path = [IO.Path]::GetTempFileName() +&SecEdit.exe /export /cfg $secedit_ini_path /quiet +$secedit_ini = ConvertFrom-Ini -file_path $secedit_ini_path + +if ($secedit_ini.ContainsKey($section)) { + $result.value = $secedit_ini.$section.$key +} else { + $result.value = $null +} + +Exit-Json $result diff --git a/test/integration/targets/incidental_win_security_policy/tasks/main.yml b/test/integration/targets/incidental_win_security_policy/tasks/main.yml new file mode 100644 index 0000000000..28fdb5ea09 --- /dev/null +++ b/test/integration/targets/incidental_win_security_policy/tasks/main.yml @@ -0,0 +1,41 @@ +--- +- name: get current entry for audit + test_win_security_policy: + section: Event Audit + key: AuditSystemEvents + register: before_value_audit + +- name: get current entry for guest + test_win_security_policy: + section: System Access + key: NewGuestName + register: before_value_guest + +- block: + - name: set AuditSystemEvents entry before tests + win_security_policy: + section: Event Audit + key: AuditSystemEvents + value: 0 + + - name: set NewGuestName entry before tests + win_security_policy: + section: System Access + key: NewGuestName + value: Guest + + - name: run tests + include_tasks: tests.yml + + always: + - name: reset entries for AuditSystemEvents + win_security_policy: + section: Event Audit + key: AuditSystemEvents + value: "{{before_value_audit.value}}" + + - name: reset entries for NewGuestName + win_security_policy: + section: System Access + key: NewGuestName + value: "{{before_value_guest.value}}" diff --git a/test/integration/targets/incidental_win_security_policy/tasks/tests.yml b/test/integration/targets/incidental_win_security_policy/tasks/tests.yml new file mode 100644 index 0000000000..724b6010a3 --- /dev/null +++ b/test/integration/targets/incidental_win_security_policy/tasks/tests.yml @@ -0,0 +1,186 @@ +--- +- name: fail with invalid section name + win_security_policy: + section: This is not a valid section + key: KeyName + value: 0 + register: fail_invalid_section + failed_when: fail_invalid_section.msg != "The section 'This is not a valid section' does not exist in SecEdit.exe output ini" + +- name: fail with invalid key name + win_security_policy: + section: System Access + key: InvalidKey + value: 0 + register: fail_invalid_key + failed_when: fail_invalid_key.msg != "The key 'InvalidKey' in section 'System Access' is not a valid key, cannot set this value" + +- name: change existing key check + win_security_policy: + section: Event Audit + key: AuditSystemEvents + value: 1 + register: change_existing_check + check_mode: yes + +- name: get actual change existing key check + test_win_security_policy: + section: Event Audit + key: AuditSystemEvents + register: change_existing_actual_check + +- name: assert change existing key check + assert: + that: + - change_existing_check is changed + - change_existing_actual_check.value == 0 + +- name: change existing key + win_security_policy: + section: Event Audit + key: AuditSystemEvents + value: 1 + register: change_existing + +- name: get actual change existing key + test_win_security_policy: + section: Event Audit + key: AuditSystemEvents + register: change_existing_actual + +- name: assert change existing key + assert: + that: + - change_existing is changed + - change_existing_actual.value == 1 + +- name: change existing key again + win_security_policy: + section: Event Audit + key: AuditSystemEvents + value: 1 + register: change_existing_again + +- name: assert change existing key again + assert: + that: + - change_existing_again is not changed + - change_existing_again.value == 1 + +- name: change existing key with string type + win_security_policy: + section: Event Audit + key: AuditSystemEvents + value: "1" + register: change_existing_key_with_type + +- name: assert change existing key with string type + assert: + that: + - change_existing_key_with_type is not changed + - change_existing_key_with_type.value == "1" + +- name: change existing string key check + win_security_policy: + section: System Access + key: NewGuestName + value: New Guest + register: change_existing_string_check + check_mode: yes + +- name: get actual change existing string key check + test_win_security_policy: + section: System Access + key: NewGuestName + register: change_existing_string_actual_check + +- name: assert change existing string key check + assert: + that: + - change_existing_string_check is changed + - change_existing_string_actual_check.value == "Guest" + +- name: change existing string key + win_security_policy: + section: System Access + key: NewGuestName + value: New Guest + register: change_existing_string + +- name: get actual change existing string key + test_win_security_policy: + section: System Access + key: NewGuestName + register: change_existing_string_actual + +- name: assert change existing string key + assert: + that: + - change_existing_string is changed + - change_existing_string_actual.value == "New Guest" + +- name: change existing string key again + win_security_policy: + section: System Access + key: NewGuestName + value: New Guest + register: change_existing_string_again + +- name: assert change existing string key again + assert: + that: + - change_existing_string_again is not changed + - change_existing_string_again.value == "New Guest" + +- name: add policy setting + win_security_policy: + section: Privilege Rights + # following key is empty by default + key: SeCreateTokenPrivilege + # add Guests + value: '*S-1-5-32-546' + +- name: get actual policy setting + test_win_security_policy: + section: Privilege Rights + key: SeCreateTokenPrivilege + register: add_policy_setting_actual + +- name: assert add policy setting + assert: + that: + - add_policy_setting_actual.value == '*S-1-5-32-546' + +- name: remove policy setting + win_security_policy: + section: Privilege Rights + key: SeCreateTokenPrivilege + value: '' + diff: yes + register: remove_policy_setting + +- name: get actual policy setting + test_win_security_policy: + section: Privilege Rights + key: SeCreateTokenPrivilege + register: remove_policy_setting_actual + +- name: assert remove policy setting + assert: + that: + - remove_policy_setting is changed + - remove_policy_setting.diff.prepared == "[Privilege Rights]\n-SeCreateTokenPrivilege = *S-1-5-32-546\n+SeCreateTokenPrivilege = " + - remove_policy_setting_actual.value is none + +- name: remove policy setting again + win_security_policy: + section: Privilege Rights + key: SeCreateTokenPrivilege + value: '' + register: remove_policy_setting_again + +- name: assert remove policy setting again + assert: + that: + - remove_policy_setting_again is not changed + - remove_policy_setting_again.value == '' |