diff --git a/lib/ansible/modules/windows/win_copy.py b/lib/ansible/modules/windows/win_copy.py index f7ccd25effb..7068572303e 100644 --- a/lib/ansible/modules/windows/win_copy.py +++ b/lib/ansible/modules/windows/win_copy.py @@ -28,6 +28,12 @@ options: specified value. This is for simple values, for anything complex or with formatting please switch to the template module. version_added: "2.3" + decrypt: + description: + - This option controls the autodecryption of source files using vault. + type: bool + default: 'yes' + version_added: "2.5" dest: description: - Remote absolute path where the file should be copied to. If src is a diff --git a/lib/ansible/plugins/action/win_copy.py b/lib/ansible/plugins/action/win_copy.py index 55b61c2526a..09cca23c152 100644 --- a/lib/ansible/plugins/action/win_copy.py +++ b/lib/ansible/plugins/action/win_copy.py @@ -22,7 +22,7 @@ from ansible.plugins.action import ActionBase from ansible.utils.hashing import checksum -def _walk_dirs(topdir, loader, base_path=None, local_follow=False, trailing_slash_detector=None, checksum_check=False): +def _walk_dirs(topdir, loader, decrypt=True, base_path=None, local_follow=False, trailing_slash_detector=None, checksum_check=False): """ Walk a filesystem tree returning enough information to copy the files. This is similar to the _walk_dirs function in ``copy.py`` but returns @@ -31,6 +31,7 @@ def _walk_dirs(topdir, loader, base_path=None, local_follow=False, trailing_slas :arg topdir: The directory that the filesystem tree is rooted at :arg loader: The self._loader object from ActionBase + :kwarg decrypt: Whether to decrypt a file encrypted with ansible-vault :kwarg base_path: The initial directory structure to strip off of the files for the destination directory. If this is None (the default), the base_path is set to ``top_dir``. @@ -101,7 +102,7 @@ def _walk_dirs(topdir, loader, base_path=None, local_follow=False, trailing_slas if os.path.islink(filepath): # Dereference the symlnk - real_file = loader.get_real_file(os.path.realpath(filepath), decrypt=True) + real_file = loader.get_real_file(os.path.realpath(filepath), decrypt=decrypt) if local_follow and os.path.isfile(real_file): # Add the file pointed to by the symlink r_files['files'].append( @@ -116,7 +117,7 @@ def _walk_dirs(topdir, loader, base_path=None, local_follow=False, trailing_slas r_files['symlinks'].append({"src": os.readlink(filepath), "dest": dest_filepath}) else: # Just a normal file - real_file = loader.get_real_file(filepath, decrypt=True) + real_file = loader.get_real_file(filepath, decrypt=decrypt) r_files['files'].append( { "src": real_file, @@ -340,6 +341,7 @@ class ActionModule(ActionBase): remote_src = boolean(self._task.args.get('remote_src', False), strict=False) local_follow = boolean(self._task.args.get('local_follow', False), strict=False) force = boolean(self._task.args.get('force', True), strict=False) + decrypt = boolean(self._task.args.get('decrypt', True), strict=False) result['src'] = source result['dest'] = dest @@ -414,7 +416,7 @@ class ActionModule(ActionBase): result['operation'] = 'folder_copy' # Get a list of the files we want to replicate on the remote side - source_files = _walk_dirs(source, self._loader, local_follow=local_follow, + source_files = _walk_dirs(source, self._loader, decrypt=decrypt, local_follow=local_follow, trailing_slash_detector=self._connection._shell.path_has_trailing_slash, checksum_check=force) @@ -430,7 +432,7 @@ class ActionModule(ActionBase): # If the local file does not exist, get_real_file() raises AnsibleFileNotFound try: - source_full = self._loader.get_real_file(source, decrypt=True) + source_full = self._loader.get_real_file(source, decrypt=decrypt) except AnsibleFileNotFound as e: result['failed'] = True result['msg'] = "could not find src=%s, %s" % (source_full, to_text(e)) diff --git a/test/integration/targets/win_copy/tasks/tests.yml b/test/integration/targets/win_copy/tasks/tests.yml index a061e974128..2764d49c81d 100644 --- a/test/integration/targets/win_copy/tasks/tests.yml +++ b/test/integration/targets/win_copy/tasks/tests.yml @@ -443,6 +443,70 @@ 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|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: + - not copy_encrypted_file_again|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|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: + - not copy_encrypted_file_again|changed + - name: remove test folder after local to remote tests win_file: path: '{{test_win_copy_path}}'