summaryrefslogtreecommitdiff
path: root/test/integration
diff options
context:
space:
mode:
authorMatt Clay <mclay@redhat.com>2020-02-27 16:05:47 -0800
committerGitHub <noreply@github.com>2020-02-27 16:05:47 -0800
commitf735fd672a783638b8187e61569386ef27410ff7 (patch)
treedcc4c0d7ce029a970fdce2a0143f05587b033ae6 /test/integration
parentda30e6d2e1efa411b26e6a48e5d7f7cea644bbef (diff)
downloadansible-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')
-rw-r--r--test/integration/targets/incidental_win_copy/aliases2
-rw-r--r--test/integration/targets/incidental_win_copy/defaults/main.yml1
-rw-r--r--test/integration/targets/incidental_win_copy/files-different/vault/folder/nested-vault-file6
-rw-r--r--test/integration/targets/incidental_win_copy/files-different/vault/readme.txt5
-rw-r--r--test/integration/targets/incidental_win_copy/files-different/vault/vault-file6
-rw-r--r--test/integration/targets/incidental_win_copy/files/empty.txt0
-rw-r--r--test/integration/targets/incidental_win_copy/files/foo.txt1
-rw-r--r--test/integration/targets/incidental_win_copy/files/subdir/bar.txt1
-rw-r--r--test/integration/targets/incidental_win_copy/files/subdir/subdir2/baz.txt1
-rw-r--r--test/integration/targets/incidental_win_copy/files/subdir/subdir2/subdir3/subdir4/qux.txt1
-rw-r--r--test/integration/targets/incidental_win_copy/tasks/main.yml34
-rw-r--r--test/integration/targets/incidental_win_copy/tasks/remote_tests.yml471
-rw-r--r--test/integration/targets/incidental_win_copy/tasks/tests.yml535
-rw-r--r--test/integration/targets/incidental_win_data_deduplication/aliases5
-rw-r--r--test/integration/targets/incidental_win_data_deduplication/meta/main.yml2
-rw-r--r--test/integration/targets/incidental_win_data_deduplication/tasks/main.yml2
-rw-r--r--test/integration/targets/incidental_win_data_deduplication/tasks/pre_test.yml40
-rw-r--r--test/integration/targets/incidental_win_data_deduplication/tasks/tests.yml47
-rw-r--r--test/integration/targets/incidental_win_data_deduplication/templates/partition_creation_script.j211
-rw-r--r--test/integration/targets/incidental_win_data_deduplication/templates/partition_deletion_script.j23
-rw-r--r--test/integration/targets/incidental_win_dsc/aliases6
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.psm141
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xSetReboot/ANSIBLE_xSetReboot.schema.mof7
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1214
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof60
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.0/xTestDsc.psd113
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.psm1214
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/DSCResources/ANSIBLE_xTestResource/ANSIBLE_xTestResource.schema.mof63
-rw-r--r--test/integration/targets/incidental_win_dsc/files/xTestDsc/1.0.1/xTestDsc.psd113
-rw-r--r--test/integration/targets/incidental_win_dsc/meta/main.yml2
-rw-r--r--test/integration/targets/incidental_win_dsc/tasks/main.yml39
-rw-r--r--test/integration/targets/incidental_win_dsc/tasks/tests.yml544
-rw-r--r--test/integration/targets/incidental_win_hosts/aliases2
-rw-r--r--test/integration/targets/incidental_win_hosts/defaults/main.yml13
-rw-r--r--test/integration/targets/incidental_win_hosts/meta/main.yml2
-rw-r--r--test/integration/targets/incidental_win_hosts/tasks/main.yml17
-rw-r--r--test/integration/targets/incidental_win_hosts/tasks/tests.yml189
-rw-r--r--test/integration/targets/incidental_win_lineinfile/aliases3
-rw-r--r--test/integration/targets/incidental_win_lineinfile/files/test.txt5
-rw-r--r--test/integration/targets/incidental_win_lineinfile/files/test_linebreak.txt0
-rw-r--r--test/integration/targets/incidental_win_lineinfile/files/test_quoting.txt0
-rw-r--r--test/integration/targets/incidental_win_lineinfile/files/testempty.txt0
-rw-r--r--test/integration/targets/incidental_win_lineinfile/files/testnoeof.txt2
-rw-r--r--test/integration/targets/incidental_win_lineinfile/meta/main.yml2
-rw-r--r--test/integration/targets/incidental_win_lineinfile/tasks/main.yml708
-rw-r--r--test/integration/targets/incidental_win_ping/aliases2
-rw-r--r--test/integration/targets/incidental_win_ping/library/win_ping_set_attr.ps131
-rw-r--r--test/integration/targets/incidental_win_ping/library/win_ping_strict_mode_error.ps130
-rw-r--r--test/integration/targets/incidental_win_ping/library/win_ping_syntax_error.ps130
-rw-r--r--test/integration/targets/incidental_win_ping/library/win_ping_throw.ps130
-rw-r--r--test/integration/targets/incidental_win_ping/library/win_ping_throw_string.ps130
-rw-r--r--test/integration/targets/incidental_win_ping/tasks/main.yml67
-rw-r--r--test/integration/targets/incidental_win_prepare_tests/aliases1
-rw-r--r--test/integration/targets/incidental_win_prepare_tests/meta/main.yml3
-rw-r--r--test/integration/targets/incidental_win_prepare_tests/tasks/main.yml29
-rw-r--r--test/integration/targets/incidental_win_psexec/aliases2
-rw-r--r--test/integration/targets/incidental_win_psexec/meta/main.yml2
-rw-r--r--test/integration/targets/incidental_win_psexec/tasks/main.yml80
-rw-r--r--test/integration/targets/incidental_win_reboot/aliases2
-rw-r--r--test/integration/targets/incidental_win_reboot/tasks/main.yml70
-rw-r--r--test/integration/targets/incidental_win_reboot/templates/post_reboot.ps18
-rw-r--r--test/integration/targets/incidental_win_security_policy/aliases2
-rw-r--r--test/integration/targets/incidental_win_security_policy/library/test_win_security_policy.ps153
-rw-r--r--test/integration/targets/incidental_win_security_policy/tasks/main.yml41
-rw-r--r--test/integration/targets/incidental_win_security_policy/tasks/tests.yml186
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 == ''