diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:04:41 +0000 |
commit | 975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch) | |
tree | 89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/community/windows/tests/integration | |
parent | Initial commit. (diff) | |
download | ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip |
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/windows/tests/integration')
438 files changed, 30050 insertions, 0 deletions
diff --git a/ansible_collections/community/windows/tests/integration/targets/psexec/aliases b/ansible_collections/community/windows/tests/integration/targets/psexec/aliases new file mode 100644 index 000000000..d76b41fdb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/psexec/aliases @@ -0,0 +1,3 @@ +windows +shippable/windows/group5 + diff --git a/ansible_collections/community/windows/tests/integration/targets/psexec/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/psexec/tasks/main.yml new file mode 100644 index 000000000..3b0471436 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/psexec/tasks/main.yml @@ -0,0 +1,49 @@ +--- +- name: check whether the host supports encryption + ansible.windows.win_shell: | + if ([System.Environment]::OSVersion.Version -lt [Version]"6.2") { + "false" + } else { + "true" + } + register: encryption_supported_raw + +- name: install pypsexec Python library for tests + command: '{{ ansible_python_interpreter | default("python") }} -m pip install pypsexec' + register: psexec_install + changed_when: '"Requirement already satisfied: pypsexec" not in psexec_install.stdout' + delegate_to: localhost + +- name: define psexec variables + set_fact: + psexec_hostname: '{{ansible_host}}' + psexec_username: '{{ansible_user}}' + psexec_password: '{{ansible_password}}' + psexec_encrypt: '{{encryption_supported_raw.stdout_lines[0]|bool}}' + +- name: create test rule to allow SMB traffic inbound + win_firewall_rule: + name: File and Printer Sharing (SMB-In) Test + direction: in + action: allow + localport: 445 + enabled: yes + protocol: tcp + program: System + profiles: + - domain + - private + - public + state: present + +- name: run tests + block: + - include_tasks: tests.yml + + always: + - name: remove test rule that allows SMB traffic inbound + win_firewall_rule: + name: File and Printer Sharing (SMB-In) Test + direction: in + action: allow + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/psexec/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/psexec/tasks/tests.yml new file mode 100644 index 000000000..b542cb43e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/psexec/tasks/tests.yml @@ -0,0 +1,231 @@ +--- +- name: fail when process_password is not set with process_username + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: hostname.exe + process_username: '{{psexec_username}}' + delegate_to: localhost + register: fail_no_process_pass + failed_when: 'fail_no_process_pass.msg != "parameters are required together when not running as System: process_username, process_password"' + +- name: get current host + ansible.windows.win_command: hostname.exe + register: actual_hostname + +- name: run basic psexec command + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: hostname.exe + delegate_to: localhost + register: psexec_hostname_actual + +- name: assert basic psexec command matches expected output + assert: + that: + - psexec_hostname_actual is changed + - psexec_hostname_actual.rc == 0 + - psexec_hostname_actual.stderr == '' + - psexec_hostname_actual.stdout == actual_hostname.stdout + +- name: get output for executable with arguments + ansible.windows.win_command: hostname.exe /? + register: actual_hostname_help + failed_when: actual_hostname_help.rc != 1 + +- name: run psexec command with arguments + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: hostname.exe + arguments: /? + delegate_to: localhost + register: psexec_hostname_help + failed_when: psexec_hostname_help.rc != 1 + +- name: assert basic pesexec command with arguments matches expected output + assert: + that: + - psexec_hostname_help is changed + - psexec_hostname_help.rc == 1 + - psexec_hostname_help.stderr == actual_hostname_help.stderr + - psexec_hostname_help.stdout == actual_hostname_help.stdout + +- name: run psexec command and send data through stdin + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: powershell.exe + arguments: '-' + stdin: | + Write-Host hello world + Write-Host this is another message + exit 0 + delegate_to: localhost + register: psexec_stdin + +- name: assert psexec ommand and send data through stdin + assert: + that: + - psexec_stdin is changed + - psexec_stdin.rc == 0 + - psexec_stdin.stderr == '' + - psexec_stdin.stdout == 'hello world\nthis is another message\n' + +- name: run psexec command with specific process username + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + load_profile: no # on Azure, the profile does not exist yet so we don't load it for this task + executable: powershell.exe + arguments: '-' + stdin: | + ((Get-CimInstance Win32_Process -filter "processid = $pid") | Get-CimAssociatedInstance -Association Win32_SessionProcess).LogonType + exit 0 + process_username: '{{psexec_username}}' + process_password: '{{psexec_password}}' + delegate_to: localhost + register: psexec_process_username + +- name: assert psexec command with specific process username + assert: + that: + - psexec_process_username is changed + - psexec_process_username.rc == 0 + - psexec_process_username.stderr == '' + - psexec_process_username.stdout_lines[0] != '3' # 3 is Network Logon Type, we assert we are not a network logon with process credentials + +- name: run psexec command with both stderr and stdout + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: cmd.exe + arguments: /c echo first && echo second 1>&2 && echo third + delegate_to: localhost + register: psexec_process_stderr + +- name: assert psexec command with both stderr and stdout + assert: + that: + - psexec_process_stderr is changed + - psexec_process_stderr.rc == 0 + - psexec_process_stderr.stderr == 'second \r\n' + - psexec_process_stderr.stdout == 'first \r\nthird\r\n' + +- name: run process asynchronously + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: powershell.exe + arguments: Start-Sleep -Seconds 30 + asynchronous: yes + delegate_to: localhost + register: psexec_process_async + +- name: check if process is still running + ansible.windows.win_shell: (Get-Process -ID {{psexec_process_async.pid}}).ProcessName + register: psexec_process_async_actual + +- name: assert run process asynchronously + assert: + that: + - psexec_process_async is changed + - psexec_process_async.rc is not defined + - psexec_process_async.pid is defined + - psexec_process_async.stdout is not defined + - psexec_process_async.stderr is not defined + - psexec_process_async_actual.stdout_lines[0] == 'powershell' + +- name: run process interactively + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: powershell.exe + arguments: Write-Host hi + interactive: yes + delegate_to: localhost + register: psexec_process_interactive + +- name: assert run process interactively + assert: + that: + - psexec_process_interactive is changed + - psexec_process_interactive.rc == 0 + - psexec_process_interactive.stdout is not defined + - psexec_process_interactive.stderr is not defined + +- name: run process with timeout + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: powershell.exe + arguments: Start-Sleep -Seconds 30 + process_timeout: 5 + delegate_to: localhost + register: psexec_process_timeout + failed_when: psexec_process_timeout.rc == 0 + +- name: assert psexec process with timeout + assert: + that: + - psexec_process_timeout.rc != 0 + - psexec_process_timeout.stdout == '' + - psexec_process_timeout.stderr == '' + +- name: run process as system + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: whoami.exe + process_username: System + delegate_to: localhost + register: psexec_process_system + +- name: assert run process as system + assert: + that: + - psexec_process_system is changed + - psexec_process_system.rc == 0 + - psexec_process_system.stderr == '' + - psexec_process_system.stdout == 'nt authority\system\r\n' + +- name: run process with different chdir + psexec: + hostname: '{{psexec_hostname}}' + connection_username: '{{psexec_username}}' + connection_password: '{{psexec_password}}' + encrypt: '{{psexec_encrypt}}' + executable: powershell.exe + arguments: (pwd).Path + working_directory: C:\Windows + delegate_to: localhost + register: psexec_process_working_dir + +- name: assert run process with different chdir + assert: + that: + - psexec_process_working_dir is changed + - psexec_process_working_dir.rc == 0 + - psexec_process_working_dir.stderr == '' + - psexec_process_working_dir.stdout == 'C:\Windows\r\n' diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_domain_tests/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_domain_tests/tasks/main.yml new file mode 100644 index 000000000..b46d69078 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_domain_tests/tasks/main.yml @@ -0,0 +1,61 @@ +--- +- name: Change the hostname to ansible-tester + ansible.windows.win_hostname: + name: ansible-tester + register: rename_host + +- name: Reboot after changing hostname + ansible.windows.win_reboot: + when: rename_host.reboot_required + +- name: Ensure the ActiveDirectory module is installed + ansible.windows.win_feature: + name: + - RSAT-AD-PowerShell + state: present + +- name: Ensure domain is present + ansible.windows.win_domain: + dns_domain_name: ansible.test + safe_mode_password: password123! + register: ensure_domain + +- name: Reboot after domain promotion + ansible.windows.win_reboot: + when: ensure_domain.reboot_required + +# While usually win_reboot waits until it is fully done before continuing I've seen Server 2019 in CI still waiting +# for things to initialise. By tested if ADWS is available with a simple check we can ensure the host is at least +# ready to test AD. Typically I've found it takes about 60 retries so doubling it should cover even an absolute worst +# case. +- name: Post reboot test for ADWS to come online + ansible.windows.win_powershell: + parameters: + Delay: 5 + Retries: 120 + script: | + [CmdletBinding()] + param ( + [int]$Delay, + [int]$Retries + ) + $Ansible.Changed = $false + $attempts = 0 + $err = $null + while ($true) { + $attempts++ + try { + Get-ADRootDSE -ErrorAction Stop + break + } + catch { + if ($attempts -eq $Retries) { + throw + } + Start-Sleep -Seconds $Delay + } + } + $attempts + become: yes + become_method: runas + become_user: SYSTEM diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/defaults/main.yml new file mode 100644 index 000000000..a1e5b8d10 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/defaults/main.yml @@ -0,0 +1,4 @@ +badssl_host: wrong.host.badssl.com +httpbin_host: httpbin.org +sni_host: ci-files.testing.ansible.com +badssl_host_substring: wrong.host.badssl.com diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/handlers/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/handlers/main.yml new file mode 100644 index 000000000..a14627931 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/handlers/main.yml @@ -0,0 +1,6 @@ +- name: remove CA trust store cert + ansible.windows.win_certificate_store: + thumbprint: '{{ httptester_ca_cert_info.thumbprints[0] }}' + state: absent + store_location: LocalMachine + store_name: Root diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/meta/main.yml new file mode 100644 index 000000000..1810d4bec --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/tasks/main.yml new file mode 100644 index 000000000..d4f1d789b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/tasks/main.yml @@ -0,0 +1,42 @@ +# The docker --link functionality gives us an ENV var we can key off of to see if we have access to +# the httptester container +- set_fact: + has_httptester: "{{ lookup('env', 'HTTPTESTER') != '' }}" + +- name: If we are running with access to a httptester container, grab it's cacert and install it + when: has_httptester | bool + block: + - name: Override hostname defaults with httptester linked names + include_vars: httptester.yml + + - name: make sure the port forwarder is active + ansible.windows.win_wait_for: + host: ansible.http.tests + port: 80 + state: started + timeout: 300 + + - name: get client cert/key + ansible.windows.win_get_url: + url: http://ansible.http.tests/{{ item }} + dest: '{{ remote_tmp_dir }}\{{ item }}' + register: win_download + retries: 5 # Just have a retry in case the host is running a bit slower today. + until: win_download is successful + with_items: + - client.pem + - client.key + + - name: retrieve test cacert + ansible.windows.win_get_url: + url: http://ansible.http.tests/cacert.pem + dest: '{{ remote_tmp_dir }}\cacert.pem' + + - name: update ca trust + ansible.windows.win_certificate_store: + path: '{{ remote_tmp_dir }}\cacert.pem' + state: present + store_location: LocalMachine + store_name: Root + register: httptester_ca_cert_info + notify: remove CA trust store cert diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/vars/httptester.yml b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/vars/httptester.yml new file mode 100644 index 000000000..0e23ae936 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_http_tests/vars/httptester.yml @@ -0,0 +1,5 @@ +# these are fake hostnames provided by docker link for the httptester container +badssl_host: fail.ansible.http.tests +httpbin_host: ansible.http.tests +sni_host: sni1.ansible.http.tests +badssl_host_substring: HTTP Client Testing Service diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml new file mode 100644 index 000000000..f0f0ee5ee --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml @@ -0,0 +1,4 @@ +- name: delete temporary directory + ansible.windows.win_file: + path: '{{ remote_tmp_dir }}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml new file mode 100644 index 000000000..4b6e1395b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml @@ -0,0 +1,11 @@ +- name: create temporary directory + ansible.windows.win_tempfile: + state: directory + suffix: .test + register: remote_tmp_dir + notify: + - delete temporary directory + +- name: record temporary directory + set_fact: + remote_tmp_dir: "{{ remote_tmp_dir.path }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_win_device/handlers/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_win_device/handlers/main.yml new file mode 100644 index 000000000..5c01331ad --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_win_device/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: remove dummy network adapter device + win_device: + name: '{{ network_device_name_raw.name }}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_win_device/library/win_device.ps1 b/ansible_collections/community/windows/tests/integration/targets/setup_win_device/library/win_device.ps1 new file mode 100644 index 000000000..77fac9086 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_win_device/library/win_device.ps1 @@ -0,0 +1,546 @@ +#!powershell + +#AnsibleRequires -CSharpUtil Ansible.Basic +#Requires -Module Ansible.ModuleUtils.AddType + +$spec = @{ + options = @{ + hardware_id = @{ type = "str" } + name = @{ type = "str" } + path = @{ type = "path" } + state = @{ type = "str"; choices = @("absent", "present"); default = "present" } + } + required_if = @( + @("state", "present", @("path", "hardware_id"), $true), + @("state", "absent", @(, "name")) + ) + supports_check_mode = $true +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) + +$hardware_id = $module.Params.hardware_id +$name = $module.Params.name +$path = $module.Params.path +$state = $module.Params.state + +$module.Result.reboot_required = $false + +Add-CSharpType -References @' +using Microsoft.Win32.SafeHandles; +using System; +using System.ComponentModel; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ansible.Device +{ + public class NativeHelpers + { + [StructLayout(LayoutKind.Sequential)] + public class SP_DEVINFO_DATA + { + public UInt32 cbSize; + public Guid ClassGuid; + public UInt32 DevInst; + public IntPtr Reserved; + + public SP_DEVINFO_DATA() + { + this.cbSize = (UInt32)Marshal.SizeOf(this); + this.ClassGuid = Guid.Empty; + } + } + + [Flags] + public enum DeviceInfoCreationFlags : uint + { + DICD_GENERATE_ID = 0x00000001, + DICD_INHERIT_CLASSDRVS = 0x00000002, + } + + public enum DeviceProperty : uint + { + SPDRP_DEVICEDESC = 0x0000000, + SPDRP_HARDWAREID = 0x0000001, + SPDRP_COMPATIBLEIDS = 0x0000002, + SPDRP_UNUSED0 = 0x0000003, + SPDRP_SERVICE = 0x0000004, + SPDRP_UNUSED1 = 0x0000005, + SPDRP_UNUSED2 = 0x0000006, + SPDRP_CLASS = 0x0000007, // Read only - tied to ClassGUID + SPDRP_CLASSGUID = 0x0000008, + SPDRP_DRIVER = 0x0000009, + SPDRP_CONFIGFLAGS = 0x000000a, + SPDRP_MFG = 0x000000b, + SPDRP_FRIENDLYNAME = 0x000000c, + SPDRP_LOCATION_INFORMATION = 0x000000d, + SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x000000e, // Read only + SPDRP_CAPABILITIES = 0x000000f, // Read only + SPDRP_UI_NUMBER = 0x0000010, // Read only + SPDRP_UPPERFILTERS = 0x0000011, + SPDRP_LOWERFILTERS = 0x0000012, + SPDRP_BUSTYPEGUID = 0x0000013, // Read only + SPDRP_LEGACYBUSTYPE = 0x0000014, // Read only + SPDRP_BUSNUMBER = 0x0000015, // Read only + SPDRP_ENUMERATOR_NAME = 0x0000016, // Read only + SPDRP_SECURITY = 0x0000017, + SPDRP_SECURITY_SDS = 0x0000018, + SPDRP_DEVTYPE = 0x0000019, + SPDRP_EXCLUSIVE = 0x000001a, + SPDRP_CHARACTERISTICS = 0x000001b, + SPDRP_ADDRESS = 0x000001c, // Read only + SPDRP_UI_NUMBER_DESC_FORMAT = 0x000001d, + SPDRP_DEVICE_POWER_DATA = 0x000001e, // Read only + SPDRP_REMOVAL_POLICY = 0x000001f, // Read only + SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x0000020, // Read only + SPDRP_REMOVAL_POLICY_OVERRIDE = 0x0000021, + SPDRP_INSTALL_STATE = 0x0000022, // Read only + SPDRP_LOCATION_PATHS = 0x0000023, // Read only + SPDRP_BASE_CONTAINERID = 0x0000024, // Read only + } + + // https://docs.microsoft.com/en-us/previous-versions/ff549793%28v%3dvs.85%29 + public enum DifCodes : uint + { + DIF_SELECTDIVE = 0x00000001, + DIF_INSTALLDEVICE = 0x00000002, + DIF_ASSIGNRESOURCES = 0x00000003, + DIF_PROPERTIES = 0x00000004, + DIF_REMOVE = 0x00000005, + DIF_FIRSTTIMESETUP = 0x00000006, + DIF_FOUNDDEVICE = 0x00000007, + DIF_SELECTCLASSDRIVERS = 0x00000008, + DIF_VALIDATECLASSDRIVERS = 0x00000009, + DIF_INSTALLCLASSDRIVERS = 0x0000000a, + DIF_CALCDISKSPACE = 0x0000000b, + DIF_DESTROYPRIVATEDATA = 0x0000000c, + DIF_VALIDATEDRIVER = 0x0000000d, + DIF_DETECT = 0x0000000f, + DIF_INSTALLWIZARD = 0x00000010, + DIF_DESTROYWIZARDDATA = 0x00000011, + DIF_PROPERTYCHANGE = 0x00000012, + DIF_ENABLECLASS = 0x00000013, + DIF_DETECTVERIFY = 0x00000014, + DIF_INSTALLDEVICEFILES = 0x00000015, + DIF_UNREMOVE = 0x00000016, + DIF_SELECTBESTCOMPATDRV = 0x00000017, + DIF_ALLOW_INSTALL = 0x00000018, + DIF_REGISTERDEVICE = 0x00000019, + DIF_NEWDEVICEWIZARD_PRESELECT = 0x0000001a, + DIF_NEWDEVICEWIZARD_SELECT = 0x0000001b, + DIF_NEWDEVICEWIZARD_PREANALYZE = 0x0000001c, + DIF_NEWDEVICEWIZARD_POSTANALYZE = 0x0000001d, + DIF_NEWDEVICEWIZARD_FINISHINSTALL = 0x0000001e, + DIF_UNUSED1 = 0x0000001e, + DIF_INSTALLINTERFACES = 0x00000020, + DIF_DETECTCANCEL = 0x00000021, + DIF_REGISTER_COINSTALLERS = 0x00000022, + DIF_ADDPROPERTYPAGE_ADVANCED = 0x00000023, + DIF_ADDPROPERTYPAGE_BASIC = 0x00000024, + DIF_RESERVED1 = 0x00000025, + DIF_TROUBLESHOOTER = 0x00000026, + DIF_POWERMESSAGEWAKE = 0x00000027, + DIF_ADDREMOTEPROPERTYPAGE_ADVANCED = 0x00000028, + DIF_UPDATEDRIVER_UI = 0x00000029, + DIF_FINISHINSTALL_ACTION = 0x0000002a, + } + + [Flags] + public enum GetClassFlags : uint + { + DIGCF_DEFAULT = 0x00000001, + DIGCF_PRESENT = 0x00000002, + DIGCF_ALLCLASSES = 0x00000004, + DIGCF_PROFILE = 0x00000008, + DIGCF_DEVICEINTERFACE = 0x00000010, + } + + [Flags] + public enum InstallFlags : uint + { + INSTALLFLAG_FORCE = 0x00000001, + INSTALLFLAG_READONLY = 0x00000002, + INSTALLFLAG_NONINTERACTIVE = 0x00000004, + INSTALLFLAG_BITS = 0x00000007, + } + } + + public class NativeMethods + { + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern bool SetupDiCallClassInstaller( + NativeHelpers.DifCodes InstallFunction, + SafeDeviceInfoSet DeviceInfoSet, + NativeHelpers.SP_DEVINFO_DATA DeviceInfoData); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern SafeDeviceInfoSet SetupDiCreateDeviceInfoList( + Guid ClassGuid, + IntPtr hwndParent); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern bool SetupDiCreateDeviceInfoW( + SafeDeviceInfoSet DeviceInfoSet, + [MarshalAs(UnmanagedType.LPWStr)] string DeviceName, + Guid ClassGuid, + [MarshalAs(UnmanagedType.LPWStr)] string DeviceDescription, + IntPtr hwndParent, + NativeHelpers.DeviceInfoCreationFlags CreationFlags, + NativeHelpers.SP_DEVINFO_DATA DeviceInfoData); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern bool SetupDiDestroyDeviceInfoList( + IntPtr DeviceInfoSet); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern bool SetupDiEnumDeviceInfo( + SafeDeviceInfoSet DeviceInfoSet, + UInt32 MemberIndex, + NativeHelpers.SP_DEVINFO_DATA DeviceInfoData); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern SafeDeviceInfoSet SetupDiGetClassDevsW( + Guid ClassGuid, + [MarshalAs(UnmanagedType.LPWStr)] string Enumerator, + IntPtr hwndParent, + NativeHelpers.GetClassFlags Flags); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern bool SetupDiGetDeviceRegistryPropertyW( + SafeDeviceInfoSet DeviceInfoSet, + NativeHelpers.SP_DEVINFO_DATA DeviceInfoData, + NativeHelpers.DeviceProperty Property, + out UInt32 PropertyRegDataType, + SafeMemoryBuffer PropertyBuffer, + UInt32 PropertyBufferSize, + ref UInt32 RequiredSize); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern bool SetupDiGetINFClassW( + [MarshalAs(UnmanagedType.LPWStr)] string InfName, + ref Guid ClassGuid, + [MarshalAs(UnmanagedType.LPWStr)] StringBuilder ClassName, + UInt32 ClassNameSize, + ref UInt32 RequiredSize); + + [DllImport("Setupapi.dll", SetLastError = true)] + public static extern bool SetupDiSetDeviceRegistryPropertyW( + SafeDeviceInfoSet DeviceInfoSet, + NativeHelpers.SP_DEVINFO_DATA DeviceInfoData, + NativeHelpers.DeviceProperty Property, + SafeMemoryBuffer PropertyBuffer, + UInt32 PropertyBufferSize); + + [DllImport("Newdev.dll", SetLastError = true)] + public static extern bool UpdateDriverForPlugAndPlayDevicesW( + IntPtr hwndParent, + [MarshalAs(UnmanagedType.LPWStr)] string HardwareId, + [MarshalAs(UnmanagedType.LPWStr)] string FullInfPath, + NativeHelpers.InstallFlags InstallFlags, + ref bool bRebootRequired); + } + + public class SafeDeviceInfoSet : SafeHandleZeroOrMinusOneIsInvalid + { + public SafeDeviceInfoSet() : base(true) { } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + return NativeMethods.SetupDiDestroyDeviceInfoList(handle); + } + } + + public class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid + { + public int Length = 0; + + public SafeMemoryBuffer() : base(true) { } + + public SafeMemoryBuffer(int cb) : base(true) + { + Length = cb; + base.SetHandle(Marshal.AllocHGlobal(cb)); + } + + public SafeMemoryBuffer(string sz) : base(true) + { + Length = sz.Length * sizeof(char); + base.SetHandle(Marshal.StringToHGlobalUni(sz)); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + Marshal.FreeHGlobal(handle); + return true; + } + } + + public class DeviceUtil + { + public static string GetDeviceFriendlyName(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo) + { + string friendlyName = GetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_FRIENDLYNAME); + + // Older Windows versions may not have a friendly name set. This seems to be the case when the device has + // a unique description so we fallback to that value. + if (null == friendlyName) + friendlyName = GetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_DEVICEDESC); + + return friendlyName; + } + + public static void SetDeviceHardwareId(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo, + string hardwareId) + { + SetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_HARDWAREID, hardwareId); + } + + private static string GetDeviceStringProp(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo, + NativeHelpers.DeviceProperty property) + { + using (SafeMemoryBuffer memBuf = GetDeviceProperty(devInfoSet, devInfo, property)) + { + if (memBuf.IsInvalid) // Property does not exist so just return null. + return null; + + return Marshal.PtrToStringUni(memBuf.DangerousGetHandle()); + } + } + + private static SafeMemoryBuffer GetDeviceProperty(SafeDeviceInfoSet devInfoSet, + NativeHelpers.SP_DEVINFO_DATA devInfo, NativeHelpers.DeviceProperty property) + { + UInt32 requiredSize = 0; + UInt32 regDataType = 0; + if (!NativeMethods.SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfo, property, + out regDataType, new SafeMemoryBuffer(0), 0, ref requiredSize)) + { + int errCode = Marshal.GetLastWin32Error(); + if (errCode == 0x0000000D) // ERROR_INVALID_DATA + return new SafeMemoryBuffer(); // The FRIENDLYNAME property does not exist + else if (errCode != 0x0000007A) // ERROR_INSUFFICIENT_BUFFER + throw new Win32Exception(errCode); + } + + SafeMemoryBuffer memBuf = new SafeMemoryBuffer((int)requiredSize); + if (!NativeMethods.SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfo, property, + out regDataType, memBuf, requiredSize, ref requiredSize)) + { + int errCode = Marshal.GetLastWin32Error(); + memBuf.Dispose(); + + throw new Win32Exception(errCode); + } + + return memBuf; + } + + private static void SetDeviceStringProp(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo, + NativeHelpers.DeviceProperty property, string value) + { + using (SafeMemoryBuffer buffer = new SafeMemoryBuffer(value)) + SetDeviceProperty(devInfoSet, devInfo, property, buffer); + } + + private static void SetDeviceProperty(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo, + NativeHelpers.DeviceProperty property, SafeMemoryBuffer buffer) + { + if (!NativeMethods.SetupDiSetDeviceRegistryPropertyW(devInfoSet, devInfo, property, buffer, + (UInt32)buffer.Length)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + } +} +'@ + +Function Get-Win32ErrorMessage { + Param ([System.Int32]$ErrorCode) + + $exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $ErrorCode + return ("{0} (Win32 ErrorCode {1} - 0x{1:X8}" -f $exp.Message, $ErrorCode) +} + +# Determine if the device is already installed +$dev_info_set = [Ansible.Device.NativeMethods]::SetupDiGetClassDevsW( + [Guid]::Empty, + [NullString]::Value, + [System.IntPtr]::Zero, + [Ansible.Device.NativeHelpers+GetClassFlags]::DIGCF_ALLCLASSES +); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + +try { + if ($dev_info_set.IsInvalid) { + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to get device information set for installed devices: $msg") + } + + $dev_info = $null + if ($null -ne $name) { + # Loop through the set of all devices and compare the name + $idx = 0 + while ($true) { + $dev_info = New-Object -TypeName Ansible.Device.NativeHelpers+SP_DEVINFO_DATA + $res = [Ansible.Device.NativeMethods]::SetupDiEnumDeviceInfo( + $dev_info_set, + $idx, + $dev_info + ); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if (-not $res) { + $dev_info = $null + if ($err -eq 0x00000103) { + # ERROR_NO_MORE_ITEMS + break + } + + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to enumerate device information set at index $($idx): $msg") + } + + $device_name = [Ansible.Device.DeviceUtil]::GetDeviceFriendlyName($dev_info_set, $dev_info) + if ($device_name -eq $name) { + break + } + + $dev_info = $null + $idx++ + } + } + + if ($state -eq "absent" -and $null -ne $dev_info) { + if (-not $module.CheckMode) { + $res = [Ansible.Device.NativeMethods]::SetupDiCallClassInstaller( + [Ansible.Device.NativeHelpers+DifCodes]::DIF_REMOVE, + $dev_info_set, + $dev_info + ); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if (-not $res) { + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to remove device $($name): $msg") + } + } + + $module.Result.changed = $true + } + elseif ($state -eq "present" -and $null -eq $dev_info) { + # Populate the class guid and display name if the path to an inf file was set. + $class_id = [Guid]::Empty + $class_name = $null + if ($path) { + if (-not (Test-Path -LiteralPath $path)) { + $module.FailJson("Could not find the inf file specified at '$path'") + } + + $class_name_sb = New-Object -TypeName System.Text.StringBuilder -ArgumentList 32 # MAX_CLASS_NAME_LEN + $required_size = 0 + $res = [Ansible.Device.NativeMethods]::SetupDiGetINFClassW( + $path, + [ref]$class_id, + $class_name_sb, + $class_name_sb.Capacity, + [ref]$required_size + ); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if (-not $res) { + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to parse driver inf at '$path': $msg") + } + + $class_name = $class_name_sb.ToString() + } + + # When creating a new device we want to start with a blank device information set. + $dev_info_set.Dispose() + + $dev_info_set = [Ansible.Device.NativeMethods]::SetupDiCreateDeviceInfoList( + $class_id, + [System.IntPtr]::Zero + ); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if ($dev_info_set.IsInvalid) { + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to create device info set for the class $($class_id): $msg") + } + + # Create the new device element and add it to the device info set + $dev_info = New-Object -TypeName Ansible.Device.NativeHelpers+SP_DEVINFO_DATA + $res = [Ansible.Device.NativeMethods]::SetupDiCreateDeviceInfoW( + $dev_info_set, + $class_name, + $class_id, + $null, + [System.IntPtr]::Zero, + [Ansible.Device.NativeHelpers+DeviceInfoCreationFlags]::DICD_GENERATE_ID, + $dev_info + ); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if (-not $res) { + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to create new device element for class $($class_name): $msg") + } + + # Set the hardware id of the new device so we can load the proper driver. + [Ansible.Device.DeviceUtil]::SetDeviceHardwareId($dev_info_set, $dev_info, $hardware_id) + + if (-not $module.CheckMode) { + # Install the device + $res = [Ansible.Device.NativeMethods]::SetupDiCallClassInstaller( + [Ansible.Device.NativeHelpers+DifCodes]::DIF_REGISTERDEVICE, + $dev_info_set, + $dev_info + ); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if (-not $res) { + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to register new device for class $($class_name): $msg") + } + + # Load the drivers for the new device + $reboot_required = $false + $res = [Ansible.Device.NativeMethods]::UpdateDriverForPlugAndPlayDevicesW( + [System.IntPtr]::Zero, + $hardware_id, + $path, + [Ansible.Device.NativeHelpers+InstallFlags]'INSTALLFLAG_FORCE, INSTALLFLAG_NONINTERACTIVE', + [ref]$reboot_required + ); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + + if (-not $res) { + # On a failure make sure we cleanup the "installed" device + [Ansible.Device.NativeMethods]::SetupDiCallClassInstaller( + [Ansible.Device.NativeHelpers+DifCodes]::DIF_REMOVE, + $dev_info_set, + $dev_info + ) > $null + + $msg = Get-Win32ErrorMessage -ErrorCode $err + $module.FailJson("Failed to update device driver: $msg") + } + + $module.Result.reboot_required = $reboot_required + + # Now get the name of the newly created device which we return back to Ansible. + $name = [Ansible.Device.DeviceUtil]::GetDeviceFriendlyName($dev_info_set, $dev_info) + } + else { + # Generate random name for check mode output + $name = "Check mode generated device for $($class_name)" + } + $module.Result.changed = $true + } +} +finally { + $dev_info_set.Dispose() +} + +$module.Result.name = $name + +$module.ExitJson() + diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_win_device/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_win_device/tasks/main.yml new file mode 100644 index 000000000..9bfe36fcf --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_win_device/tasks/main.yml @@ -0,0 +1,22 @@ +# Creates a network adapter device for testing purposes and registers the following vars +# network_device_name: The name of the network device +# network_adapter_name: The name of the network adapter +--- +- name: create dummy network adapter device + win_device: + path: '%WinDir%\Inf\netloop.inf' + hardware_id: '*msloop' + state: present + register: network_device_name_raw + notify: remove dummy network adapter device + +- set_fact: + network_device_name: '{{ network_device_name_raw.name }}' + +- name: get name of the dummy network adapter + ansible.windows.win_shell: (Get-CimInstance -Class Win32_NetworkAdapter -Filter "Name='{{ network_device_name }}'").NetConnectionID + changed_when: False + register: network_adapter_name_raw + +- set_fact: + network_adapter_name: '{{ network_adapter_name_raw.stdout | trim }}' diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_win_psget/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_win_psget/meta/main.yml new file mode 100644 index 000000000..45806c8dc --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_win_psget/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/setup_win_psget/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/setup_win_psget/tasks/main.yml new file mode 100644 index 000000000..ce1607647 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/setup_win_psget/tasks/main.yml @@ -0,0 +1,129 @@ +# Installs PackageManagement and PowerShellGet to the required versions for testing +--- +- name: check if PackageManagement has been installed + ansible.windows.win_powershell: + script: | + $ErrorActionPreference = 'Stop' + $Ansible.Changed = $false + + if (-not (Get-Command -Name Install-Module -ErrorAction SilentlyContinue)) { + [PSCustomObject]@{ + Install = $true + Action = "scratch" + } + return + } + + $psGet = Get-Module -Name PowerShellGet -ListAvailable | + Sort-Object -Property Version -Descending | + Select-Object -First 1 -ExpandProperty Version + $package = Get-Module -Name PackageManagement -ListAvailable | + Sort-Object -Property Version -Descending | + Select-Object -First 1 -ExpandProperty Version + + if ($psGet -lt [Version]"1.6.0" -or $package -lt [Version]"1.1.7") { + [PSCustomObject]@{ + Install = $true + Action = "module" + } + } + else { + [PSCustomObject]@{ + Install = $false + } + } + + register: module_installed + +- name: bootstrap required modules + when: module_installed.output[0].Install + block: + - name: install PackageManagement for older hosts + ansible.windows.win_package: + path: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/setup_win_psget/PackageManagement_x64.msi + product_id: '{57E5A8BB-41EB-4F09-B332-B535C5954A28}' + state: present + when: module_installed.output[0].Action == "scratch" + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + + - name: remove the old versions of PackageManagement and PowerShellGet + ansible.windows.win_file: + path: C:\Program Files\WindowsPowerShell\Modules\{{ item }} + state: absent + when: module_installed.output[0].Action == "scratch" + loop: + - PackageManagement + - PowerShellGet + + - name: create the required folder for nuget + ansible.windows.win_file: + path: C:\Program Files\PackageManagement\ProviderAssemblies\nuget\2.8.5.208 + state: directory + + - name: download nuget provider dll + ansible.windows.win_get_url: + url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/setup_win_psget/Microsoft.PackageManagement.NuGetProvider-2.8.5.208.dll + dest: C:\Program Files\PackageManagement\ProviderAssemblies\nuget\2.8.5.208\Microsoft.PackageManagement.NuGetProvider.dll + force: false + register: nuget_download_res + until: nuget_download_res is successful + retries: 3 + delay: 5 + + - name: download newer PackageManagement and PowerShellGet nupkg + ansible.windows.win_get_url: + url: '{{ item.url }}' + dest: '{{ remote_tmp_dir }}\{{ item.name }}.{{ "nupkg" if module_installed.output[0].Action == "module" else "zip" }}' # .zip is required for win_unzip + when: module_installed.output[0].Install + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + loop: + - name: PackageManagement + url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/setup_win_psget/packagemanagement.1.1.7.nupkg + - name: PowerShellGet + url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/setup_win_psget/powershellget.1.6.0.nupkg + + - name: extract new modules to correct location for older hosts + win_unzip: + src: '{{ remote_tmp_dir }}\{{ item }}.zip' + dest: C:\Program Files\WindowsPowerShell\Modules\{{ item }} + when: module_installed.output[0].Action == "scratch" + loop: + - PackageManagement + - PowerShellGet + + - name: update PackageManagement and PowerShellGet + when: module_installed.output[0].Action == "module" + block: + - name: register local PSRepo + ansible.windows.win_powershell: + script: | + param($Path) + + Register-PSRepository -Name LocalNuget -SourceLocation $Path + parameters: + Path: '{{ remote_tmp_dir }}' + + - name: ensure PowerShellGet and PackageManagement requirements have been met + win_psmodule: + name: PowerShellGet + repository: LocalNuget + accept_license: true + state: present + + always: + - name: unregister local PSRepo + ansible.windows.win_powershell: + script: | + if (Get-PSRepository -Name LocalNuget -ErrorAction SilentlyContinue) { + Unregister-PSRepository -Name LocalNuget + $Ansible.Changed = $true + } + else { + $Ansible.Changed = $false + } diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/aliases b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/defaults/main.yml new file mode 100644 index 000000000..9e0d35c77 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/defaults/main.yml @@ -0,0 +1,3 @@ +#important that the subcategory is from a different category +category_name: detailed tracking +subcategory_name: file system diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/add.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/add.yml new file mode 100644 index 000000000..75ea23045 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/add.yml @@ -0,0 +1,108 @@ +######################## +### check mode apply ### +######################## +- name: check mode enable category + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: success + check_mode: yes + register: category + +- name: check mode enable subcategory + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: success, failure + check_mode: yes + register: subcategory + +- name: check mode assert that changed is true + assert: + that: + - category is changed + - subcategory is changed + +- name: check mode assert that audit_type is "no auditing" + assert: + that: + - item == "no auditing" + with_items: + - "{{ subcategory.current_audit_policy.values() | list }}" + - "{{ category.current_audit_policy.values() | list | unique }}" + +#alternative check for category...pretty noise and requires more lines +# - name: assert that audit_type is no auditing +# assert: +# that: item.value == "no auditing" +# with_dict: "{{ category.current_audit_policy }}" + +#################### +### apply change ### +#################### + +- name: enable category + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: success + register: category + +- name: enable subcategory + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: success, failure + register: subcategory + +- name: enable assert that changed is true + assert: + that: + - category is changed + - subcategory is changed + +- name: enable assert that audit_type is "success" for category + assert: + that: + - item == "success" + with_items: + - "{{ category.current_audit_policy.values() | list | unique }}" + +- name: enable assert that audit_type is "success and failure" for subcategory + assert: + that: + - item == "success and failure" + with_items: + - "{{ subcategory.current_audit_policy.values() | list }}" + +############################### +### idempotent apply change ### +############################### + +- name: idem enable category + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: success + register: category + +- name: idem enable subcategory + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: success, failure + register: subcategory + +- name: idem assert that changed is false + assert: + that: + - category is not changed + - subcategory is not changed + +- name: idem assert that audit_type is "success" for category + assert: + that: + - item == "success" + with_items: + - "{{ category.current_audit_policy.values() | list | unique }}" + +- name: idem assert that audit_type is "success and failure" for subcategory + assert: + that: + - item == "success and failure" + with_items: + - "{{ subcategory.current_audit_policy.values() | list }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/main.yml new file mode 100644 index 000000000..c2e55accf --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/main.yml @@ -0,0 +1,25 @@ +#turn off so then we can test changes occur on enable. Turning off for object access also +#covers our subcategory test for file system +- name: turn off auditing for category + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: none + +- name: turn off auditing for subcategory + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: none + +- block: + - include_tasks: add.yml + - include_tasks: remove.yml + always: + - name: CLEANUP turn "{{ category_name }}" back to no auditing + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: none + + - name: CLEANUP turn "{{ subcategory_name }}" back to no auditing + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: none diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/remove.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/remove.yml new file mode 100644 index 000000000..1cd60b0ab --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_policy_system/tasks/remove.yml @@ -0,0 +1,96 @@ +######################### +### check mode remove ### +######################### +- name: check mode disable category + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: none + check_mode: yes + register: category + +- name: check mode disable subcategory + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: none + check_mode: yes + register: subcategory + +- name: check mode assert that changed is true + assert: + that: + - category is changed + - subcategory is changed + +- name: check mode assert that audit_type is still "success" (old value) for category + assert: + that: + - item == "success" + with_items: + - "{{ category.current_audit_policy.values() | list | unique }}" + +- name: check mode assert that audit_type is still "success and failure" (old value) for subcategory + assert: + that: + - item == "success and failure" + with_items: + - "{{ subcategory.current_audit_policy.values() | list }}" + +###################### +### disable policy ### +###################### + +- name: disable category + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: none + register: category + +- name: disable subcategory + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: none + register: subcategory + +- name: assert that changed is true + assert: + that: + - category is changed + - subcategory is changed + +- name: assert that audit_type is "no auditing" + assert: + that: + - item == "no auditing" + with_items: + - "{{ subcategory.current_audit_policy.values() | list }}" + - "{{ category.current_audit_policy.values() | list | unique }}" + +########################## +### idempotent disable ### +########################## + +- name: idem disable category + win_audit_policy_system: + category: "{{ category_name }}" + audit_type: none + register: category + +- name: idem disable subcategory + win_audit_policy_system: + subcategory: "{{ subcategory_name }}" + audit_type: none + register: subcategory + +- name: idem assert that changed is false + assert: + that: + - category is not changed + - subcategory is not changed + +- name: assert that audit_type is "no auditing" + assert: + that: + - item == "no auditing" + with_items: + - "{{ subcategory.current_audit_policy.values() | list }}" + - "{{ category.current_audit_policy.values() | list | unique }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/aliases b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/defaults/main.yml new file mode 100644 index 000000000..f0faa9a56 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/defaults/main.yml @@ -0,0 +1,7 @@ +test_audit_rule_folder: c:\windows\temp\{{ 'ansible test win_audit_policy' | to_uuid }} +test_audit_rule_file: c:\windows\temp\{{ 'ansible test win_audit_policy' | to_uuid }}.txt +test_audit_rule_registry: HKCU:\{{ 'ansible test win_audit_policy' | to_uuid }} +test_audit_rule_rights: 'delete' +test_audit_rule_new_rights: 'delete,changepermissions' +test_audit_rule_user: 'everyone' +test_audit_rule_audit_flags: success diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/library/test_get_audit_rule.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/library/test_get_audit_rule.ps1 new file mode 100644 index 000000000..37096c21a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/library/test_get_audit_rule.ps1 @@ -0,0 +1,93 @@ +#!powershell + +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#Requires -Module Ansible.ModuleUtils.Legacy +#Requires -Module Ansible.ModuleUtils.SID + +$params = Parse-Args -arguments $args -supports_check_mode $true + +# these are your module parameters +$path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -aliases "destination", "dest" +$user = Get-AnsibleParam -obj $params -name "user" -type "str" -failifempty $true +$rights = Get-AnsibleParam -obj $params -name "rights" -type "list" +$inheritance_flags = Get-AnsibleParam -obj $params -name "inheritance_flags" -type "list" -default 'ContainerInherit', 'ObjectInherit' +$propOptions = 'InheritOnly', 'None', 'NoPropagateInherit' +$propagation_flags = Get-AnsibleParam -obj $params -name "propagation_flags" -type "str" -default "none" -ValidateSet $propOptions +$audit_flags = Get-AnsibleParam -obj $params -name "audit_flags" -type "list" -default "success" #-ValidateSet 'Success','Failure' +#$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset 'present','absent' + + +If (! (Test-Path $path) ) { + Fail-Json $result "Path not found ($path)" +} + +Function Get-CurrentAuditRule ($path) { + $ACL = Get-Acl -Path $path -Audit + + $HT = Foreach ($Obj in $ACL.Audit) { + @{ + user = $Obj.IdentityReference.ToString() + rights = ($Obj | Select-Object -expand "*rights").ToString() + audit_flags = $Obj.AuditFlags.ToString() + is_inherited = $Obj.InheritanceFlags.ToString() + inheritance_flags = $Obj.IsInherited.ToString() + propagation_flags = $Obj.PropagationFlags.ToString() + } + } + + If (-Not $HT) { + "No audit rules defined on $path" + } + Else { $HT } +} + + +$result = @{ + changed = $false + matching_rule_found = $false + current_audit_rules = Get-CurrentAuditRule $path +} + +$ACL = Get-ACL $Path -Audit +$SID = Convert-ToSid $user + +$ItemType = (Get-Item $path).GetType() +switch ($ItemType) { + ([Microsoft.Win32.RegistryKey]) { + $rights = [System.Security.AccessControl.RegistryRights]$rights + $result.path_type = 'registry' + } + ([System.IO.FileInfo]) { + $rights = [System.Security.AccessControl.FileSystemRights]$rights + $result.path_type = 'file' + } + ([System.IO.DirectoryInfo]) { + $rights = [System.Security.AccessControl.FileSystemRights]$rights + $result.path_type = 'directory' + } +} + +$flags = [System.Security.AccessControl.AuditFlags]$audit_flags +$inherit = [System.Security.AccessControl.InheritanceFlags]$inheritance_flags +$prop = [System.Security.AccessControl.PropagationFlags]$propagation_flags + +Foreach ($group in $ACL.Audit) { + #exit here if any existing rule matches defined rule, otherwise exit below + #with no matches + If ( + ($group | Select-Object -expand "*Rights") -eq $rights -and + $group.AuditFlags -eq $flags -and + $group.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $SID -and + $group.InheritanceFlags -eq $inherit -and + $group.PropagationFlags -eq $prop + ) { + $result.matching_rule_found = $true + $result.current_audit_rules = Get-CurrentAuditRule $path + Exit-Json $result + } +} + +$result.current_audit_rules = Get-CurrentAuditRule $path +Exit-Json $result diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/add.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/add.yml new file mode 100644 index 000000000..2a059a88c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/add.yml @@ -0,0 +1,172 @@ +###################### +### check mode add ### +###################### +- name: check mode ADD audit policy directory + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory + check_mode: yes + +- name: check mode ADD audit policy file + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file + check_mode: yes + +- name: check mode ADD audit policy registry + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry + check_mode: yes + +- name: check mode ADD get directory results + test_get_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory_results + +- name: check mode ADD get file results + test_get_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file_results + +- name: check mode ADD get REGISTRY results + test_get_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry_results + +- name: check mode ADD assert that a change is needed, but no change occurred to the audit rules + assert: + that: + - directory is changed + - file is changed + - registry is changed + - not directory_results.matching_rule_found and directory_results.path_type == 'directory' + - not file_results.matching_rule_found and file_results.path_type == 'file' + - not registry_results.matching_rule_found and registry_results.path_type == 'registry' + +################## +### add a rule ### +################## +- name: ADD audit policy directory + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory + +- name: ADD audit policy file + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file + +- name: ADD audit policy registry + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry + +- name: ADD get directory results + test_get_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory_results + +- name: ADD get file results + test_get_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file_results + +- name: ADD get REGISTRY results + test_get_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry_results + +- name: ADD assert that the rules were added and a change is detected + assert: + that: + - directory is changed + - file is changed + - registry is changed + - directory_results.matching_rule_found and directory_results.path_type == 'directory' + - file_results.matching_rule_found and file_results.path_type == 'file' + - registry_results.matching_rule_found and registry_results.path_type == 'registry' + +############################# +### idempotent add a rule ### +############################# +- name: idempotent ADD audit policy directory + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory + +- name: idempotent ADD audit policy file + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file + +- name: idempotent ADD audit policy registry idempotent + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry + +- name: idempotent ADD assert that a change did not occur + assert: + that: + - directory is not changed and directory.path_type == 'directory' + - file is not changed and file.path_type == 'file' + - registry is not changed and registry.path_type == 'registry' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/main.yml new file mode 100644 index 000000000..cdeff7a3a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/main.yml @@ -0,0 +1,33 @@ +- name: create temporary folder to test with + ansible.windows.win_file: + path: "{{ test_audit_rule_folder }}" + state: directory + +- name: create temporary file to test with + ansible.windows.win_file: + path: "{{ test_audit_rule_file }}" + state: touch + +- name: create temporary registry key to test with + ansible.windows.win_regedit: + path: "{{ test_audit_rule_registry }}" + +- block: + - include_tasks: add.yml + - include_tasks: modify.yml + - include_tasks: remove.yml + always: + - name: remove testing folder + ansible.windows.win_file: + path: "{{ test_audit_rule_folder }}" + state: absent + + - name: remove testing file + ansible.windows.win_file: + path: "{{ test_audit_rule_file }}" + state: absent + + - name: remove registry key + ansible.windows.win_regedit: + path: "{{ test_audit_rule_registry }}" + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/modify.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/modify.yml new file mode 100644 index 000000000..1db07e2b4 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/modify.yml @@ -0,0 +1,172 @@ +######################### +### modify check mode ### +######################### +- name: check mode modify audit policy directory + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory + check_mode: yes + +- name: check mode modify audit policy file + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file + check_mode: yes + +- name: check mode modify audit policy registry + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry + check_mode: yes + +- name: check mode modify get directory rule results + test_get_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory_results + +- name: check mode modify get file rule results + test_get_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file_results + +- name: check mode modify get REGISTRY rule results + test_get_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry_results + +- name: check mode modify assert that change is needed but rights still equal the original rights and not test_audit_rule_new_rights + assert: + that: + - directory is changed + - file is changed + - registry is changed + - not directory_results.matching_rule_found and directory_results.path_type == 'directory' + - not file_results.matching_rule_found and file_results.path_type == 'file' + - not registry_results.matching_rule_found and registry_results.path_type == 'registry' + +############## +### modify ### +############## +- name: modify audit policy directory + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory + +- name: modify audit policy file + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file + +- name: modify audit policy registry + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry + +- name: modify get directory rule results + test_get_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory_results + +- name: modify get file rule results + test_get_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file_results + +- name: modify get REGISTRY rule results + test_get_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry_results + +- name: modify assert that the rules were modified and a change is detected + assert: + that: + - directory is changed + - file is changed + - registry is changed + - directory_results.matching_rule_found and directory_results.path_type == 'directory' + - file_results.matching_rule_found and file_results.path_type == 'file' + - registry_results.matching_rule_found and registry_results.path_type == 'registry' + +##################################### +### idempotent test modify a rule ### +##################################### +- name: idempotent modify audit policy directory + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory + +- name: idempotent modify audit policy file + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file + +- name: idempotent modify audit policy registry + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + state: present + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry + +- name: idempotent modify assert that and a change is not detected + assert: + that: + - directory is not changed and directory.path_type == 'directory' + - file is not changed and file.path_type == 'file' + - registry is not changed and registry.path_type == 'registry' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/remove.yml b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/remove.yml new file mode 100644 index 000000000..3102bc748 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_audit_rule/tasks/remove.yml @@ -0,0 +1,151 @@ +################################ +### check mode remove a rule ### +################################ +- name: check mode remove directory rule + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: directory + check_mode: yes + +- name: check mode remove file rule + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: file + check_mode: yes + +- name: check mode remove registry rule + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: registry + check_mode: yes + +- name: check mode remove get directory rule results + test_get_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory_results + +- name: check mode remove get file rule results + test_get_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file_results + +- name: check mode remove get REGISTRY rule results + test_get_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry_results + +- name: check mode remove assert that change detected, but rule is still present + assert: + that: + - directory is changed + - file is changed + - registry is changed + - directory_results.matching_rule_found and directory_results.path_type == 'directory' + - file_results.matching_rule_found and file_results.path_type == 'file' + - registry_results.matching_rule_found and registry_results.path_type == 'registry' + +##################### +### remove a rule ### +##################### +- name: remove directory rule + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: directory + +- name: remove file rule + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: file + +- name: remove registry rule + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: registry + +- name: remove get directory rule results + test_get_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: directory_results + +- name: remove get file rule results + test_get_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + inheritance_flags: none + register: file_results + +- name: remove get REGISTRY rule results + test_get_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + rights: "{{ test_audit_rule_new_rights }}" + audit_flags: "{{ test_audit_rule_audit_flags }}" + register: registry_results + +- name: remove assert that change detected and rule is gone + assert: + that: + - directory is changed + - file is changed + - registry is changed + - not directory_results.matching_rule_found and directory_results.path_type == 'directory' + - not file_results.matching_rule_found and file_results.path_type == 'file' + - not registry_results.matching_rule_found and registry_results.path_type == 'registry' + +################################ +### idempotent remove a rule ### +################################ +- name: idempotent remove directory rule + win_audit_rule: + path: "{{ test_audit_rule_folder }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: directory + +- name: idempotent remove file rule + win_audit_rule: + path: "{{ test_audit_rule_file }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: file + +- name: idempotent remove registry rule + win_audit_rule: + path: "{{ test_audit_rule_registry }}" + user: "{{ test_audit_rule_user }}" + state: absent + register: registry + +- name: idempotent remove assert that no change detected + assert: + that: + - directory is not changed and directory.path_type == 'directory' + - file is not changed and file.path_type == 'file' + - registry is not changed and registry.path_type == 'registry' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/aliases b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/defaults/main.yml new file mode 100644 index 000000000..d5462bb6a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/defaults/main.yml @@ -0,0 +1,3 @@ +# This doesn't have to be valid, just testing weird chars in the pass +test_logon_password: 'café - 💩' +test_logon_password2: '.ÅÑŚÌβŁÈ [$!@^&test(;)]' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/library/test_autologon_info.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/library/test_autologon_info.ps1 new file mode 100644 index 000000000..2819151ff --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/library/test_autologon_info.ps1 @@ -0,0 +1,215 @@ +#!powershell + +#AnsibleRequires -CSharpUtil Ansible.Basic +#Requires -Module Ansible.ModuleUtils.AddType + +$module = [Ansible.Basic.AnsibleModule]::Create($args, @{}) + +Add-CSharpType -AnsibleModule $module -References @' +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ansible.TestAutoLogonInfo +{ + internal class NativeHelpers + { + [StructLayout(LayoutKind.Sequential)] + public class LSA_OBJECT_ATTRIBUTES + { + public UInt32 Length = 0; + public IntPtr RootDirectory = IntPtr.Zero; + public IntPtr ObjectName = IntPtr.Zero; + public UInt32 Attributes = 0; + public IntPtr SecurityDescriptor = IntPtr.Zero; + public IntPtr SecurityQualityOfService = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct LSA_UNICODE_STRING + { + public UInt16 Length; + public UInt16 MaximumLength; + public IntPtr Buffer; + + public static explicit operator string(LSA_UNICODE_STRING s) + { + byte[] strBytes = new byte[s.Length]; + Marshal.Copy(s.Buffer, strBytes, 0, s.Length); + return Encoding.Unicode.GetString(strBytes); + } + + public static SafeMemoryBuffer CreateSafeBuffer(string s) + { + if (s == null) + return new SafeMemoryBuffer(IntPtr.Zero); + + byte[] stringBytes = Encoding.Unicode.GetBytes(s); + int structSize = Marshal.SizeOf(typeof(LSA_UNICODE_STRING)); + IntPtr buffer = Marshal.AllocHGlobal(structSize + stringBytes.Length); + try + { + LSA_UNICODE_STRING lsaString = new LSA_UNICODE_STRING() + { + Length = (UInt16)(stringBytes.Length), + MaximumLength = (UInt16)(stringBytes.Length), + Buffer = IntPtr.Add(buffer, structSize), + }; + Marshal.StructureToPtr(lsaString, buffer, false); + Marshal.Copy(stringBytes, 0, lsaString.Buffer, stringBytes.Length); + return new SafeMemoryBuffer(buffer); + } + catch + { + // Make sure we free the pointer before raising the exception. + Marshal.FreeHGlobal(buffer); + throw; + } + } + } + } + + internal class NativeMethods + { + [DllImport("Advapi32.dll")] + public static extern UInt32 LsaClose( + IntPtr ObjectHandle); + + [DllImport("Advapi32.dll")] + public static extern UInt32 LsaFreeMemory( + IntPtr Buffer); + + [DllImport("Advapi32.dll")] + internal static extern Int32 LsaNtStatusToWinError( + UInt32 Status); + + [DllImport("Advapi32.dll")] + public static extern UInt32 LsaOpenPolicy( + IntPtr SystemName, + NativeHelpers.LSA_OBJECT_ATTRIBUTES ObjectAttributes, + UInt32 AccessMask, + out SafeLsaHandle PolicyHandle); + + [DllImport("Advapi32.dll")] + public static extern UInt32 LsaRetrievePrivateData( + SafeLsaHandle PolicyHandle, + SafeMemoryBuffer KeyName, + out SafeLsaMemory PrivateData); + } + + internal class SafeLsaMemory : SafeBuffer + { + internal SafeLsaMemory() : base(true) { } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + + protected override bool ReleaseHandle() + { + return NativeMethods.LsaFreeMemory(handle) == 0; + } + } + + internal class SafeMemoryBuffer : SafeBuffer + { + internal SafeMemoryBuffer() : base(true) { } + + internal SafeMemoryBuffer(IntPtr ptr) : base(true) + { + base.SetHandle(ptr); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + + protected override bool ReleaseHandle() + { + if (handle != IntPtr.Zero) + Marshal.FreeHGlobal(handle); + return true; + } + } + + public class SafeLsaHandle : SafeHandleZeroOrMinusOneIsInvalid + { + internal SafeLsaHandle() : base(true) { } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + + protected override bool ReleaseHandle() + { + return NativeMethods.LsaClose(handle) == 0; + } + } + + public class Win32Exception : System.ComponentModel.Win32Exception + { + private string _exception_msg; + public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { } + public Win32Exception(int errorCode, string message) : base(errorCode) + { + _exception_msg = String.Format("{0} - {1} (Win32 Error Code {2}: 0x{3})", message, base.Message, errorCode, errorCode.ToString("X8")); + } + public override string Message { get { return _exception_msg; } } + public static explicit operator Win32Exception(string message) { return new Win32Exception(message); } + } + + public class LsaUtil + { + public static SafeLsaHandle OpenPolicy(UInt32 access) + { + NativeHelpers.LSA_OBJECT_ATTRIBUTES oa = new NativeHelpers.LSA_OBJECT_ATTRIBUTES(); + SafeLsaHandle lsaHandle; + UInt32 res = NativeMethods.LsaOpenPolicy(IntPtr.Zero, oa, access, out lsaHandle); + if (res != 0) + throw new Win32Exception(NativeMethods.LsaNtStatusToWinError(res), + String.Format("LsaOpenPolicy({0}) failed", access.ToString())); + return lsaHandle; + } + + public static string RetrievePrivateData(SafeLsaHandle handle, string key) + { + using (SafeMemoryBuffer keyBuffer = NativeHelpers.LSA_UNICODE_STRING.CreateSafeBuffer(key)) + { + SafeLsaMemory buffer; + UInt32 res = NativeMethods.LsaRetrievePrivateData(handle, keyBuffer, out buffer); + using (buffer) + { + if (res != 0) + { + // If the data object was not found we return null to indicate it isn't set. + if (res == 0xC0000034) // STATUS_OBJECT_NAME_NOT_FOUND + return null; + + throw new Win32Exception(NativeMethods.LsaNtStatusToWinError(res), + String.Format("LsaRetrievePrivateData({0}) failed", key)); + } + + NativeHelpers.LSA_UNICODE_STRING lsaString = (NativeHelpers.LSA_UNICODE_STRING) + Marshal.PtrToStructure(buffer.DangerousGetHandle(), + typeof(NativeHelpers.LSA_UNICODE_STRING)); + return (string)lsaString; + } + } + } + } +} +'@ + +$details = Get-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' +$module.Result.AutoAdminLogon = $details.AutoAdminLogon +$module.Result.DefaultUserName = $details.DefaultUserName +$module.Result.DefaultDomainName = $details.DefaultDomainName +$module.Result.DefaultPassword = $details.DefaultPassword +$module.Result.AutoLogonCount = $details.AutoLogonCount + +$handle = [Ansible.TestAutoLogonInfo.LsaUtil]::OpenPolicy(0x00000004) +try { + $password = [Ansible.TestAutoLogonInfo.LsaUtil]::RetrievePrivateData($handle, 'DefaultPassword') + $module.Result.LsaPassword = $password +} +finally { + $handle.Dispose() +} + +$module.ExitJson() diff --git a/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/tasks/main.yml new file mode 100644 index 000000000..d99649e3e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/tasks/main.yml @@ -0,0 +1,42 @@ +--- +- name: get user domain split for ansible_user + ansible.windows.win_shell: | + $account = New-Object -TypeName System.Security.Principal.NTAccount -ArgumentList '{{ ansible_user }}' + $sid = $account.Translate([System.Security.Principal.SecurityIdentifier]) + $sid.Translate([System.Security.Principal.NTAccount]).Value -split '{{ "\\" }}' + changed_when: False + register: test_user_split + +- set_fact: + test_domain: '{{ test_user_split.stdout_lines[0] }}' + test_user: '{{ test_user_split.stdout_lines[1] }}' + +- name: ensure auto logon is cleared before test + win_auto_logon: + state: absent + +- name: ensure defaults are set + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon + name: '{{ item.name }}' + data: '{{ item.value }}' + type: '{{ item.type }}' + state: present + loop: + # We set the DefaultPassword to ensure win_auto_logon clears this out + - name: DefaultPassword + value: abc + type: string + # Ensures the host we test on has a baseline key to check against + - name: AutoAdminLogon + value: 0 + type: dword + +- block: + - name: run tests + include_tasks: tests.yml + + always: + - name: make sure the auto logon is cleared + win_auto_logon: + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/tasks/tests.yml new file mode 100644 index 000000000..c25e07709 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_auto_logon/tasks/tests.yml @@ -0,0 +1,178 @@ +# Copyright: (c) 2019, Prasoon Karunan V (@prasoonkarunan) <kvprasoon@Live.in> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: set autologon registry keys (check mode) + win_auto_logon: + username: '{{ ansible_user }}' + password: '{{ test_logon_password }}' + state: present + register: set_check + check_mode: yes + +- name: get acutal of set autologon registry keys (check mode) + test_autologon_info: + register: set_actual_check + +- name: assert set autologon registry keys (check mode) + assert: + that: + - set_check is changed + - set_actual_check.AutoAdminLogon == 0 + - set_actual_check.AutoLogonCount == None + - set_actual_check.DefaultDomainName == None + - set_actual_check.DefaultPassword == 'abc' + - set_actual_check.DefaultUserName == None + - set_actual_check.LsaPassword == None + +- name: set autologon registry keys + win_auto_logon: + username: '{{ ansible_user }}' + password: '{{ test_logon_password }}' + state: present + register: set + +- name: get acutal of set autologon registry keys + test_autologon_info: + register: set_actual + +- name: assert set autologon registry keys + assert: + that: + - set is changed + - set_actual.AutoAdminLogon == 1 + - set_actual.AutoLogonCount == None + - set_actual.DefaultDomainName == test_domain + - set_actual.DefaultPassword == None + - set_actual.DefaultUserName == test_user + - set_actual.LsaPassword == test_logon_password + +- name: set autologon registry keys (idempotent) + win_auto_logon: + username: '{{ ansible_user }}' + password: '{{ test_logon_password }}' + state: present + register: set_again + +- name: assert set autologon registry keys (idempotent) + assert: + that: + - not set_again is changed + +- name: add logon count (check mode) + win_auto_logon: + username: '{{ ansible_user }}' + password: '{{ test_logon_password }}' + logon_count: 2 + state: present + register: logon_count_check + check_mode: yes + +- name: get result of add logon count (check mode) + test_autologon_info: + register: logon_count_actual_check + +- name: assert add logon count (check mode) + assert: + that: + - logon_count_check is changed + - logon_count_actual_check.AutoLogonCount == None + +- name: add logon count + win_auto_logon: + username: '{{ ansible_user }}' + password: '{{ test_logon_password }}' + logon_count: 2 + state: present + register: logon_count + +- name: get result of add logon count + test_autologon_info: + register: logon_count_actual + +- name: assert add logon count + assert: + that: + - logon_count is changed + - logon_count_actual.AutoLogonCount == 2 + +- name: change auto logon (check mode) + win_auto_logon: + username: '{{ ansible_user }}' + password: '{{ test_logon_password2 }}' + state: present + register: change_check + check_mode: yes + +- name: get reuslt of change auto logon (check mode) + test_autologon_info: + register: change_actual_check + +- name: assert change auto logon (check mode) + assert: + that: + - change_check is changed + - change_actual_check == logon_count_actual + +- name: change auto logon + win_auto_logon: + username: '{{ ansible_user }}' + password: '{{ test_logon_password2 }}' + state: present + register: change + +- name: get reuslt of change auto logon + test_autologon_info: + register: change_actual + +- name: assert change auto logon + assert: + that: + - change is changed + - change_actual.AutoLogonCount == None + - change_actual.LsaPassword == test_logon_password2 + +- name: remove autologon registry keys (check mode) + win_auto_logon: + state: absent + register: remove_check + check_mode: yes + +- name: get result of remove autologon registry keys (check mode) + test_autologon_info: + register: remove_actual_check + +- name: assert remove autologon registry keys (check mode) + assert: + that: + - remove_check is changed + - remove_actual_check == change_actual + +- name: remove autologon registry keys + win_auto_logon: + state: absent + register: remove + +- name: get result of remove autologon registry keys + test_autologon_info: + register: remove_actual + +- name: assert remove autologon registry keys + assert: + that: + - remove is changed + - remove_actual.AutoAdminLogon == 0 + - remove_actual.AutoLogonCount == None + - remove_actual.DefaultDomainName == None + - remove_actual.DefaultPassword == None + - remove_actual.DefaultUserName == None + - remove_actual.LsaPassword == None + +- name: remove autologon registry keys (idempotent) + win_auto_logon: + state: absent + register: remove_again + +- name: assert remove autologon registry keys (idempotent) + assert: + that: + - not remove_again is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/aliases b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/defaults/main.yml new file mode 100644 index 000000000..871dfe91d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/defaults/main.yml @@ -0,0 +1,3 @@ +win_cert_dir: '{{ remote_tmp_dir }}\win_certificate .ÅÑŚÌβŁÈ [$!@^&test(;)]' +subj_thumbprint: 'BD7AF104CF1872BDB518D95C9534EA941665FD27' +root_thumbprint: 'BC05633694E675449136679A658281F17A191087' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/files/root-cert.pem b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/files/root-cert.pem new file mode 100644 index 000000000..edbe6b868 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/files/root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDKDCCAhCgAwIBAgIJAP1vIdGgMJv/MA0GCSqGSIb3DQEBCwUAMCgxGTAXBgNV +BAMMEHJvb3QuYW5zaWJsZS5jb20xCzAJBgNVBAYTAlVTMCAXDTE3MTIxNTA4Mzkz +MloYDzIwODYwMTAyMDgzOTMyWjAoMRkwFwYDVQQDDBByb290LmFuc2libGUuY29t +MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMmq +YT8eZY6rFQKnmScUGnnUH1tLQ+3WQpfKiWygCUSb1CNqO3J1u3pGMEqYM58LK4Kr +Mpskv7K1tCV/EMZqGTqXAIfSLy9umlb/9C3AhL9thBPn5I9dam/EmrIZktI9/w5Y +wBXn4toe+OopA3QkMQh9BUjUCPb9fdOI+ir7OGFZMmxXmiM64+BEeywM2oSGsdZ9 +5hU378UBu2IX4+OAV8Fbr2l6VW+Fxg/tKIOo6Bs46Pa4EZgtemOqs3kxYBOltBTb +vFcLsLa4KYVu5Ge5YfB0Axfaem7PoP8IlMs8gxyojZ/r0o5hzxUcYlL/h8GeeoLW +PFFdiAS+UgxWINOqNXMCAwEAAaNTMFEwHQYDVR0OBBYEFLp9k4LmOnAR4ROrqhb+ +CFdbk2+oMB8GA1UdIwQYMBaAFLp9k4LmOnAR4ROrqhb+CFdbk2+oMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGksycHsjGbXfWfuhQh+CvXk/A2v +MoNgiHtNMTGliVNgoVp1B1rj4x9xyZ8YrO8GAmv8jaCwCShd0B5Ul4aZVk1wglVv +lFAwb4IAZN9jv9+fw5BRzQ2tLhkVWIEwx6pZkhGhhjBvMaplLN5JwBtsdZorFbm7 +wuKiUKcFAM28acoOhCmOhgyNNBZpZn5wXaQDY43AthJOhitAV7vph4MPUkwIJnOh +MA5GJXEqS58TE9z9pkhQnn9598G8tmOXyA2erAoM9JAXM3EYHxVpoHBb9QRj6WAw +XVBo6qRXkwjNEM5CbnD4hVIBsdkOGsDrgd4Q5izQZ3x+jFNkdL/zPsXjJFw= +-----END CERTIFICATE----- + diff --git a/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/files/subj-cert.pem b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/files/subj-cert.pem new file mode 100644 index 000000000..6d9ec39c7 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/files/subj-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIC0TCCAbkCCQC/MtOBa1UDpzANBgkqhkiG9w0BAQsFADAoMRkwFwYDVQQDDBBy +b290LmFuc2libGUuY29tMQswCQYDVQQGEwJVUzAgFw0xNzEyMTUwODU2MzBaGA8y +MDg2MDEwMjA4NTYzMFowKzEcMBoGA1UEAwwTc3ViamVjdC5hbnNpYmxlLmNvbTEL +MAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDszqdF +So3GlVP1xUnN4bSPrFRFiOl/Mqup0Zn5UJJUR9wLnRD+OLcq7kKin6hYqozSu7cC ++BnWQoq7vGSSNVqv7BqFMwzGJt9IBUQv0UqIQkA/duUdKdAiMn2PQRsNDnkWEbTj +4xsitItVNv84cDG0lkZBYyTgfyZlZLZWplkpUQkrZhoFCekZRJ+ODrqNW3W560rr +OUIh+HiQeBqocat6OdxgICBqpUh8EVo1iha3DXjGN08q5utg6gmbIl2VBaVJjfyd +wnUSqHylJwh6WCIEh+HXsn4ndfNWSN/fDqvi5I10V1j6Zos7yqQf8qAezUAm6eSq +hLgZz0odq9DsO4HHAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFK5mVIJ2D+kI0kk +sxnW4ibWFjzlYFYPYrZg+2JFIVTbKBg1YzyhuIKm0uztqRxQq5iLn/C/uponHoqF +7KDQI37KAJIQdgSva+mEuO9bZAXg/eegail2hN6np7HjOKlPu23s40dAbFrbcOWP +VbsBEPDP0HLv6OgbQWzNlE9HO1b7pX6ozk3q4ULO7IR85P6OHYsBBThL+qsOTzg/ +gVknuB9+n9hgNqZcAcXBLDetOM9aEmYJCGk0enYP5UGLYpseE+rTXFbRuHTPr1o6 +e8BetiSWS/wcrV4ZF5qr9NiYt5eD6JzTB5Rn5awxxj0FwMtrBu003lLQUWxsuTzz +35/RLY4= +-----END CERTIFICATE----- + diff --git a/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/tasks/main.yml new file mode 100644 index 000000000..a91b48108 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/tasks/main.yml @@ -0,0 +1,88 @@ +### keys in files/ have been generated with +# generate root private key +# openssl genrsa -aes256 -out enckey.pem 2048 +# openssl rsa -in envkey.pem -out root-key.pem +# +# generate root certificate +# openssl req -x509 -key root-key.pem -days 24855 -out root-vert.pem -subj "/CN=root.ansible.com/C=US" +# +# generate subject private key +# openssl genrsa -aes256 -out enckey.pem 2048 +# openssl rsa -in enckey.pem -out subj-key.pem +# +# generate subject certificate +# openssl req -new -key subj-key.pem -out cert.csr -subj "/CN=subject.ansible.com/C=US" +# openssl x509 -req -in cert.csr -CA root-cert.pem -CAkey root-key.pem -CAcreateserial -out subj-cert.pem -days 24855 +### +--- +- name: ensure test dir is present + ansible.windows.win_file: + path: '{{win_cert_dir}}\exported' + state: directory + +- name: copy across test cert files + ansible.windows.win_copy: + src: files/ + dest: '{{win_cert_dir}}' + +- name: subject cert imported to personal store + ansible.windows.win_certificate_store: + path: '{{win_cert_dir}}\subj-cert.pem' + state: present + store_name: My + +- name: root certificate imported to trusted root + ansible.windows.win_certificate_store: + path: '{{win_cert_dir}}\root-cert.pem' + store_name: Root + state: present + +- name: get raw root certificate + shell: 'cat root-cert.pem | grep "^[^-]"' + args: + chdir: '{{ role_path }}/files' + register: root_raw + delegate_to: localhost + +- name: get public key of root certificate + shell: 'openssl x509 -pubkey -noout -in root-cert.pem | grep "^[^-]"' + args: + chdir: '{{ role_path }}/files' + register: root_pub + delegate_to: localhost + +- name: get subject certificate + shell: 'cat subj-cert.pem | grep "^[^-]"' + args: + chdir: '{{ role_path }}/files' + register: subj_raw + delegate_to: localhost + +- name: get public key of subject certificate + shell: 'openssl x509 -pubkey -noout -in subj-cert.pem | grep "^[^-]"' + args: + chdir: '{{ role_path }}/files' + register: subj_pub + delegate_to: localhost + +- block: + - name: run tests + include_tasks: tests.yml + + always: + - name: ensure subject cert removed from personal store + ansible.windows.win_certificate_store: + thumbprint: '{{subj_thumbprint}}' + state: absent + store_name: My + + - name: ensure root cert removed from trusted root + ansible.windows.win_certificate_store: + thumbprint: '{{root_thumbprint}}' + state: absent + store_name: Root + + - name: ensure test dir is deleted + ansible.windows.win_file: + path: '{{win_cert_dir}}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/tasks/tests.yml new file mode 100644 index 000000000..90eb0870b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_certificate_info/tasks/tests.yml @@ -0,0 +1,90 @@ +--- + +- name: get stats on a store that doesn't exist + win_certificate_info: + store_name: teststore + register: test_store + +- name: ensure exists is false + assert: + that: + - test_store.exists == false + +- name: get stats on the root certificate store + win_certificate_info: + store_name: Root + register: root_store + +- name: at least one certificate is returned + assert: + that: + - "root_store.exists" + - "root_store.certificates | length > 0" + +- name: get stats on a certificate that doesn't exist + win_certificate_info: + thumbprint: ABC + register: actual + +- name: ensure exists is false + assert: + that: actual.exists == false + +- name: get stats on root certificate + win_certificate_info: + thumbprint: '{{ root_thumbprint }}' + store_name: Root + register: root_stats + +- name: root certificate stats returned are expected values + assert: + that: + - root_stats.exists + - root_stats.certificates[0].archived == false + - root_stats.certificates[0].dns_names == [ 'root.ansible.com' ] + - root_stats.certificates[0].extensions|count == 3 + - root_stats.certificates[0].has_private_key == false + - root_stats.certificates[0].issued_by == 'root.ansible.com' + - root_stats.certificates[0].issued_to == 'root.ansible.com' + - root_stats.certificates[0].issuer == 'C=US, CN=root.ansible.com' + - root_stats.certificates[0].path_length_constraint == 0 +# - root_stats.certificates[0].public_key == (root_pub.stdout_lines|join()) + - root_stats.certificates[0].raw == root_raw.stdout_lines|join() + - root_stats.certificates[0].serial_number == '00FD6F21D1A0309BFF' + - root_stats.certificates[0].signature_algorithm == 'sha256RSA' + - root_stats.certificates[0].ski == 'BA7D9382E63A7011E113ABAA16FE08575B936FA8' + - root_stats.certificates[0].subject == 'C=US, CN=root.ansible.com' + - root_stats.certificates[0].valid_from == 1513327172 + - root_stats.certificates[0].valid_from_iso8601 == '2017-12-15T08:39:32Z' + - root_stats.certificates[0].valid_to == 3660799172 + - root_stats.certificates[0].valid_to_iso8601 == '2086-01-02T08:39:32Z' + - root_stats.certificates[0].version == 3 + +- name: get stats on subject certificate + win_certificate_info: + thumbprint: '{{ subj_thumbprint }}' + register: subj_stats + +- name: subject certificate stats returned are expected values + assert: + that: + - subj_stats.exists + - subj_stats.certificates[0].archived == false + - subj_stats.certificates[0].dns_names == [ 'subject.ansible.com' ] + - subj_stats.certificates[0].extensions|count == 0 + - subj_stats.certificates[0].has_private_key == false + - subj_stats.certificates[0].issued_by == 'root.ansible.com' + - subj_stats.certificates[0].issued_to == 'subject.ansible.com' + - subj_stats.certificates[0].issuer == 'C=US, CN=root.ansible.com' + - subj_stats.certificates[0].path_length_constraint is undefined +# - subj_stats.certificates[0].public_key == subj_pub.stdout_lines|join() + - subj_stats.certificates[0].raw == subj_raw.stdout_lines|join() + - subj_stats.certificates[0].serial_number == '00BF32D3816B5503A7' + - subj_stats.certificates[0].signature_algorithm == 'sha256RSA' + - subj_stats.certificates[0].ski is undefined + - subj_stats.certificates[0].subject == 'C=US, CN=subject.ansible.com' + - subj_stats.certificates[0].valid_from == 1513328190 + - subj_stats.certificates[0].valid_from_iso8601 == '2017-12-15T08:56:30Z' + - subj_stats.certificates[0].valid_to == 3660800190 + - subj_stats.certificates[0].valid_to_iso8601 == '2086-01-02T08:56:30Z' + - subj_stats.certificates[0].version == 1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_computer_description/aliases b/ansible_collections/community/windows/tests/integration/targets/win_computer_description/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_computer_description/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_computer_description/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_computer_description/defaults/main.yml new file mode 100644 index 000000000..166a5248c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_computer_description/defaults/main.yml @@ -0,0 +1,6 @@ +test_description: This is my computer +test_organization: iddqd +test_owner: BFG +test_description2: This is not my computer +test_organization2: idkfa +test_owner2: CACODEMON diff --git a/ansible_collections/community/windows/tests/integration/targets/win_computer_description/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_computer_description/tasks/main.yml new file mode 100644 index 000000000..4c3ce3294 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_computer_description/tasks/main.yml @@ -0,0 +1,200 @@ +--- +- name: Blank out description, organization and owner + win_computer_description: + description: "" + organization: "" + owner: "" + register: blank_set + check_mode: no + +- name: Change description, organization and owner in check mode + win_computer_description: + description: "{{ test_description }}" + organization: "{{ test_organization }}" + owner: "{{ test_owner }}" + register: change1_checkmode + check_mode: yes + +- name: Change description, organization and owner + win_computer_description: + description: "{{ test_description }}" + organization: "{{ test_organization }}" + owner: "{{ test_owner }}" + register: change1_set + check_mode: no + +- name: Change description, organization and owner 2nd time, there should be no change happening + win_computer_description: + description: "{{ test_description }}" + organization: "{{ test_organization }}" + owner: "{{ test_owner }}" + register: change1_set2 + check_mode: no + +- name: Assert that the above tasks returned the expected results + assert: + that: + - change1_checkmode is changed + - change1_set is changed + - change1_set2 is not changed + +- name: Get machine description + ansible.windows.win_shell: (Get-CimInstance -class "Win32_OperatingSystem").description + register: description1_changed + +- name: Get organization name + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion + name: RegisteredOrganization + register: organization1_changed + +- name: Get owner + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion + name: RegisteredOwner + register: owner1_changed + +- name: Assert that retrieved values are equal to the values provided in the variables + assert: + that: + - description1_changed.stdout == "{{ test_description }}\r\n" + - organization1_changed.value == "{{ test_organization }}" + - owner1_changed.value == "{{ test_owner }}" + +- name: Change description and owner only in check mode + win_computer_description: + description: "{{ test_description2 }}" + owner: "{{ test_owner2 }}" + register: change2_checkmode + check_mode: yes + +- name: Change description and owner only + win_computer_description: + description: "{{ test_description2 }}" + owner: "{{ test_owner2 }}" + register: change2_set + check_mode: no + +- name: Change description and owner only 2nd time, there should be no change happening + win_computer_description: + description: "{{ test_description2 }}" + owner: "{{ test_owner2 }}" + register: change2_set2 + check_mode: no + +- name: Assert that the above tasks returned the expected results + assert: + that: + - change2_checkmode is changed + - change2_set is changed + - change2_set2 is not changed + +- name: Get machine description + ansible.windows.win_shell: (Get-CimInstance -class "Win32_OperatingSystem").description + register: description2_changed + +- name: Get organization name + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion + name: RegisteredOrganization + register: organization2_changed + +- name: Get owner + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion + name: RegisteredOwner + register: owner2_changed + +- name: Assert that retrieved values are equal to the desired values + assert: + that: + - description2_changed.stdout == "{{ test_description2 }}\r\n" + - organization2_changed.value == "{{ test_organization }}" + - owner2_changed.value == "{{ test_owner2 }}" + +- name: Change organization only in check mode + win_computer_description: + organization: "{{ test_organization2 }}" + register: change3_checkmode + check_mode: yes + +- name: Change organization only in check mode + win_computer_description: + organization: "{{ test_organization2 }}" + register: change3_set + check_mode: no + +- name: Change organization only in check mode + win_computer_description: + organization: "{{ test_organization2 }}" + register: change3_set2 + check_mode: no + +- name: Assert that the above tasks returned the expected results + assert: + that: + - change3_checkmode is changed + - change3_set is changed + - change3_set2 is not changed + +- name: Get machine description + ansible.windows.win_shell: (Get-CimInstance -class "Win32_OperatingSystem").description + register: description3_changed + +- name: Get organization name + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion + name: RegisteredOrganization + register: organization3_changed + +- name: Get owner + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion + name: RegisteredOwner + register: owner3_changed + +- name: Assert that retrieved values are equal to the desired values + assert: + that: + - description3_changed.stdout == "{{ test_description2 }}\r\n" + - organization3_changed.value == "{{ test_organization2 }}" + - owner3_changed.value == "{{ test_owner2 }}" + +- name: Try to apply the same values again in check mode, there should be no change + win_computer_description: + description: "{{ test_description2 }}" + organization: "{{ test_organization2 }}" + owner: "{{ test_owner2 }}" + register: change4_checkmode + check_mode: yes + +- name: Try to apply the same values again, there should be no change + win_computer_description: + description: "{{ test_description2 }}" + organization: "{{ test_organization2 }}" + owner: "{{ test_owner2 }}" + register: change4_set + check_mode: no + +- name: Try to apply the same values again for 2nd time, there should be no change + win_computer_description: + description: "{{ test_description2 }}" + organization: "{{ test_organization2 }}" + owner: "{{ test_owner2 }}" + register: change4_set2 + check_mode: no + +- name: Assert that the above tasks returned the expected results + assert: + that: + - change4_checkmode is not changed + - change4_set is not changed + - change4_set2 is not changed + +- name: Blank the test values + win_computer_description: + description: '' + organization: '' + owner: '' + register: blank2_set + check_mode: no diff --git a/ansible_collections/community/windows/tests/integration/targets/win_credential/aliases b/ansible_collections/community/windows/tests/integration/targets/win_credential/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_credential/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_credential/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_credential/defaults/main.yml new file mode 100644 index 000000000..c330762d4 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_credential/defaults/main.yml @@ -0,0 +1,19 @@ +# The certificate in files/cert.pfx was generated with the following commands +# +# cat > client.cnf <<EOL +# [ssl_client] +# basicConstraints = CA:FALSE +# nsCertType = client +# keyUsage = digitalSignature, keyEncipherment +# extendedKeyUsage = clientAuth +# EOL +# +# openssl genrsa -aes256 -passout pass:password1 -out cert.key 2048 +# openssl req -new -subj '/CN=ansible.domain.com' -key cert.key -out cert.req -passin pass:password1 +# openssl x509 -sha256 -req -in cert.req -days 24855 -signkey cert.key -out cert.crt -extensions ssl_client -extfile client.cnf -passin pass:password1 +# openssl pkcs12 -export -in cert.crt -inkey cert.key -out cert.pfx -passin pass:password1 -passout pass:password1 +--- +test_credential_dir: '{{ remote_tmp_dir }}\win_credential_manager' +test_hostname: ansible.domain.com +key_password: password1 +cert_thumbprint: 56841AAFDD19D7DF474BDA24D01D88BD8025A00A diff --git a/ansible_collections/community/windows/tests/integration/targets/win_credential/files/cert.pfx b/ansible_collections/community/windows/tests/integration/targets/win_credential/files/cert.pfx Binary files differnew file mode 100644 index 000000000..9cffb6696 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_credential/files/cert.pfx diff --git a/ansible_collections/community/windows/tests/integration/targets/win_credential/library/test_cred_facts.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_credential/library/test_cred_facts.ps1 new file mode 100644 index 000000000..59206638f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_credential/library/test_cred_facts.ps1 @@ -0,0 +1,501 @@ +#!powershell + +# Copyright: (c) 2018, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#Requires -Module Ansible.ModuleUtils.AddType + +$spec = @{ + options = @{ + name = @{ type = "str"; required = $true } + type = @{ type = "str"; required = $true; choices = @("domain_password", "domain_certificate", "generic_password", "generic_certificate") } + } + supports_check_mode = $true +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) + +$name = $module.Params.name +$type = $module.Params.type + +Add-CSharpType -AnsibleModule $module -References @' +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ansible.CredentialManager +{ + internal class NativeHelpers + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public class CREDENTIAL + { + public CredentialFlags Flags; + public CredentialType Type; + [MarshalAs(UnmanagedType.LPWStr)] public string TargetName; + [MarshalAs(UnmanagedType.LPWStr)] public string Comment; + public FILETIME LastWritten; + public UInt32 CredentialBlobSize; + public IntPtr CredentialBlob; + public CredentialPersist Persist; + public UInt32 AttributeCount; + public IntPtr Attributes; + [MarshalAs(UnmanagedType.LPWStr)] public string TargetAlias; + [MarshalAs(UnmanagedType.LPWStr)] public string UserName; + + public static explicit operator Credential(CREDENTIAL v) + { + byte[] secret = new byte[(int)v.CredentialBlobSize]; + if (v.CredentialBlob != IntPtr.Zero) + Marshal.Copy(v.CredentialBlob, secret, 0, secret.Length); + + List<CredentialAttribute> attributes = new List<CredentialAttribute>(); + if (v.AttributeCount > 0) + { + CREDENTIAL_ATTRIBUTE[] rawAttributes = new CREDENTIAL_ATTRIBUTE[v.AttributeCount]; + Credential.PtrToStructureArray(rawAttributes, v.Attributes); + attributes = rawAttributes.Select(x => (CredentialAttribute)x).ToList(); + } + + string userName = v.UserName; + if (v.Type == CredentialType.DomainCertificate || v.Type == CredentialType.GenericCertificate) + userName = Credential.UnmarshalCertificateCredential(userName); + + return new Credential + { + Type = v.Type, + TargetName = v.TargetName, + Comment = v.Comment, + LastWritten = (DateTimeOffset)v.LastWritten, + Secret = secret, + Persist = v.Persist, + Attributes = attributes, + TargetAlias = v.TargetAlias, + UserName = userName, + Loaded = true, + }; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct CREDENTIAL_ATTRIBUTE + { + [MarshalAs(UnmanagedType.LPWStr)] public string Keyword; + public UInt32 Flags; // Set to 0 and is reserved + public UInt32 ValueSize; + public IntPtr Value; + + public static explicit operator CredentialAttribute(CREDENTIAL_ATTRIBUTE v) + { + byte[] value = new byte[v.ValueSize]; + Marshal.Copy(v.Value, value, 0, (int)v.ValueSize); + + return new CredentialAttribute + { + Keyword = v.Keyword, + Flags = v.Flags, + Value = value, + }; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct FILETIME + { + internal UInt32 dwLowDateTime; + internal UInt32 dwHighDateTime; + + public static implicit operator long(FILETIME v) { return ((long)v.dwHighDateTime << 32) + v.dwLowDateTime; } + public static explicit operator DateTimeOffset(FILETIME v) { return DateTimeOffset.FromFileTime(v); } + public static explicit operator FILETIME(DateTimeOffset v) + { + return new FILETIME() + { + dwLowDateTime = (UInt32)v.ToFileTime(), + dwHighDateTime = ((UInt32)v.ToFileTime() >> 32), + }; + } + } + + [Flags] + public enum CredentialCreateFlags : uint + { + PreserveCredentialBlob = 1, + } + + [Flags] + public enum CredentialFlags + { + None = 0, + PromptNow = 2, + UsernameTarget = 4, + } + + public enum CredMarshalType : uint + { + CertCredential = 1, + UsernameTargetCredential, + BinaryBlobCredential, + UsernameForPackedCredential, + BinaryBlobForSystem, + } + } + + internal class NativeMethods + { + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CredDeleteW( + [MarshalAs(UnmanagedType.LPWStr)] string TargetName, + CredentialType Type, + UInt32 Flags); + + [DllImport("advapi32.dll")] + public static extern void CredFree( + IntPtr Buffer); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CredMarshalCredentialW( + NativeHelpers.CredMarshalType CredType, + SafeMemoryBuffer Credential, + out SafeCredentialBuffer MarshaledCredential); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CredReadW( + [MarshalAs(UnmanagedType.LPWStr)] string TargetName, + CredentialType Type, + UInt32 Flags, + out SafeCredentialBuffer Credential); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CredUnmarshalCredentialW( + [MarshalAs(UnmanagedType.LPWStr)] string MarshaledCredential, + out NativeHelpers.CredMarshalType CredType, + out SafeCredentialBuffer Credential); + + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CredWriteW( + NativeHelpers.CREDENTIAL Credential, + NativeHelpers.CredentialCreateFlags Flags); + } + + internal class SafeCredentialBuffer : SafeHandleZeroOrMinusOneIsInvalid + { + public SafeCredentialBuffer() : base(true) { } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + NativeMethods.CredFree(handle); + return true; + } + } + + internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid + { + public SafeMemoryBuffer() : base(true) { } + public SafeMemoryBuffer(int cb) : base(true) + { + base.SetHandle(Marshal.AllocHGlobal(cb)); + } + public SafeMemoryBuffer(IntPtr handle) : base(true) + { + base.SetHandle(handle); + } + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + Marshal.FreeHGlobal(handle); + return true; + } + } + + public class Win32Exception : System.ComponentModel.Win32Exception + { + private string _exception_msg; + public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { } + public Win32Exception(int errorCode, string message) : base(errorCode) + { + _exception_msg = String.Format("{0} - {1} (Win32 Error Code {2}: 0x{3})", message, base.Message, errorCode, errorCode.ToString("X8")); + } + public override string Message { get { return _exception_msg; } } + public static explicit operator Win32Exception(string message) { return new Win32Exception(message); } + } + + public enum CredentialPersist + { + Session = 1, + LocalMachine = 2, + Enterprise = 3, + } + + public enum CredentialType + { + Generic = 1, + DomainPassword = 2, + DomainCertificate = 3, + DomainVisiblePassword = 4, + GenericCertificate = 5, + DomainExtended = 6, + Maximum = 7, + MaximumEx = 1007, + } + + public class CredentialAttribute + { + public string Keyword; + public UInt32 Flags; + public byte[] Value; + } + + public class Credential + { + public CredentialType Type; + public string TargetName; + public string Comment; + public DateTimeOffset LastWritten; + public byte[] Secret; + public CredentialPersist Persist; + public List<CredentialAttribute> Attributes = new List<CredentialAttribute>(); + public string TargetAlias; + public string UserName; + + // Used to track whether the credential has been loaded into the store or not + public bool Loaded { get; internal set; } + + public void Delete() + { + if (!Loaded) + return; + + if (!NativeMethods.CredDeleteW(TargetName, Type, 0)) + throw new Win32Exception(String.Format("CredDeleteW({0}) failed", TargetName)); + Loaded = false; + } + + public void Write(bool preserveExisting) + { + string userName = UserName; + // Convert the certificate thumbprint to the string expected + if (Type == CredentialType.DomainCertificate || Type == CredentialType.GenericCertificate) + userName = Credential.MarshalCertificateCredential(userName); + + NativeHelpers.CREDENTIAL credential = new NativeHelpers.CREDENTIAL + { + Flags = NativeHelpers.CredentialFlags.None, + Type = Type, + TargetName = TargetName, + Comment = Comment, + LastWritten = new NativeHelpers.FILETIME(), + CredentialBlobSize = (UInt32)(Secret == null ? 0 : Secret.Length), + CredentialBlob = IntPtr.Zero, // Must be allocated and freed outside of this to ensure no memory leaks + Persist = Persist, + AttributeCount = (UInt32)(Attributes.Count), + Attributes = IntPtr.Zero, // Attributes must be allocated and freed outside of this to ensure no memory leaks + TargetAlias = TargetAlias, + UserName = userName, + }; + + using (SafeMemoryBuffer credentialBlob = new SafeMemoryBuffer((int)credential.CredentialBlobSize)) + { + if (Secret != null) + Marshal.Copy(Secret, 0, credentialBlob.DangerousGetHandle(), Secret.Length); + credential.CredentialBlob = credentialBlob.DangerousGetHandle(); + + // Store the CREDENTIAL_ATTRIBUTE value in a safe memory buffer and make sure we dispose in all cases + List<SafeMemoryBuffer> attributeBuffers = new List<SafeMemoryBuffer>(); + try + { + int attributeLength = Attributes.Sum(a => Marshal.SizeOf(typeof(NativeHelpers.CREDENTIAL_ATTRIBUTE))); + byte[] attributeBytes = new byte[attributeLength]; + int offset = 0; + foreach (CredentialAttribute attribute in Attributes) + { + SafeMemoryBuffer attributeBuffer = new SafeMemoryBuffer(attribute.Value.Length); + attributeBuffers.Add(attributeBuffer); + if (attribute.Value != null) + Marshal.Copy(attribute.Value, 0, attributeBuffer.DangerousGetHandle(), attribute.Value.Length); + + NativeHelpers.CREDENTIAL_ATTRIBUTE credentialAttribute = new NativeHelpers.CREDENTIAL_ATTRIBUTE + { + Keyword = attribute.Keyword, + Flags = attribute.Flags, + ValueSize = (UInt32)(attribute.Value == null ? 0 : attribute.Value.Length), + Value = attributeBuffer.DangerousGetHandle(), + }; + int attributeStructLength = Marshal.SizeOf(typeof(NativeHelpers.CREDENTIAL_ATTRIBUTE)); + + byte[] attrBytes = new byte[attributeStructLength]; + using (SafeMemoryBuffer tempBuffer = new SafeMemoryBuffer(attributeStructLength)) + { + Marshal.StructureToPtr(credentialAttribute, tempBuffer.DangerousGetHandle(), false); + Marshal.Copy(tempBuffer.DangerousGetHandle(), attrBytes, 0, attributeStructLength); + } + Buffer.BlockCopy(attrBytes, 0, attributeBytes, offset, attributeStructLength); + offset += attributeStructLength; + } + + using (SafeMemoryBuffer attributes = new SafeMemoryBuffer(attributeBytes.Length)) + { + if (attributeBytes.Length != 0) + Marshal.Copy(attributeBytes, 0, attributes.DangerousGetHandle(), attributeBytes.Length); + credential.Attributes = attributes.DangerousGetHandle(); + + NativeHelpers.CredentialCreateFlags createFlags = 0; + if (preserveExisting) + createFlags |= NativeHelpers.CredentialCreateFlags.PreserveCredentialBlob; + + if (!NativeMethods.CredWriteW(credential, createFlags)) + throw new Win32Exception(String.Format("CredWriteW({0}) failed", TargetName)); + } + } + finally + { + foreach (SafeMemoryBuffer attributeBuffer in attributeBuffers) + attributeBuffer.Dispose(); + } + } + Loaded = true; + } + + public static Credential GetCredential(string target, CredentialType type) + { + SafeCredentialBuffer buffer; + if (!NativeMethods.CredReadW(target, type, 0, out buffer)) + { + int lastErr = Marshal.GetLastWin32Error(); + + // Not running with CredSSP or Become so cannot manage the user's credentials + if (lastErr == 0x00000520) // ERROR_NO_SUCH_LOGON_SESSION + throw new InvalidOperationException("Failed to access the user's credential store, run the module with become or CredSSP"); + else if (lastErr == 0x00000490) // ERROR_NOT_FOUND + return null; + throw new Win32Exception(lastErr, "CredEnumerateW() failed"); + } + + using (buffer) + { + NativeHelpers.CREDENTIAL credential = (NativeHelpers.CREDENTIAL)Marshal.PtrToStructure( + buffer.DangerousGetHandle(), typeof(NativeHelpers.CREDENTIAL)); + return (Credential)credential; + } + } + + public static string MarshalCertificateCredential(string thumbprint) + { + // CredWriteW requires the UserName field to be the value of CredMarshalCredentialW() when writting a + // certificate auth. This converts the UserName property to the format required. + + // While CERT_CREDENTIAL_INFO is the correct structure, we manually marshal the data in order to + // support different cert hash lengths in the future. + // https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_cert_credential_info + int hexLength = thumbprint.Length; + byte[] credInfo = new byte[sizeof(UInt32) + (hexLength / 2)]; + + // First field is cbSize which is a UInt32 value denoting the size of the total structure + Array.Copy(BitConverter.GetBytes((UInt32)credInfo.Length), credInfo, sizeof(UInt32)); + + // Now copy the byte representation of the thumbprint to the rest of the struct bytes + for (int i = 0; i < hexLength; i += 2) + credInfo[sizeof(UInt32) + (i / 2)] = Convert.ToByte(thumbprint.Substring(i, 2), 16); + + IntPtr pCredInfo = Marshal.AllocHGlobal(credInfo.Length); + Marshal.Copy(credInfo, 0, pCredInfo, credInfo.Length); + SafeMemoryBuffer pCredential = new SafeMemoryBuffer(pCredInfo); + + NativeHelpers.CredMarshalType marshalType = NativeHelpers.CredMarshalType.CertCredential; + using (pCredential) + { + SafeCredentialBuffer marshaledCredential; + if (!NativeMethods.CredMarshalCredentialW(marshalType, pCredential, out marshaledCredential)) + throw new Win32Exception("CredMarshalCredentialW() failed"); + using (marshaledCredential) + return Marshal.PtrToStringUni(marshaledCredential.DangerousGetHandle()); + } + } + + public static string UnmarshalCertificateCredential(string value) + { + NativeHelpers.CredMarshalType credType; + SafeCredentialBuffer pCredInfo; + if (!NativeMethods.CredUnmarshalCredentialW(value, out credType, out pCredInfo)) + throw new Win32Exception("CredUnmarshalCredentialW() failed"); + + using (pCredInfo) + { + if (credType != NativeHelpers.CredMarshalType.CertCredential) + throw new InvalidOperationException(String.Format("Expected unmarshalled cred type of CertCredential, received {0}", credType)); + + byte[] structSizeBytes = new byte[sizeof(UInt32)]; + Marshal.Copy(pCredInfo.DangerousGetHandle(), structSizeBytes, 0, sizeof(UInt32)); + UInt32 structSize = BitConverter.ToUInt32(structSizeBytes, 0); + + byte[] certInfoBytes = new byte[structSize]; + Marshal.Copy(pCredInfo.DangerousGetHandle(), certInfoBytes, 0, certInfoBytes.Length); + + StringBuilder hex = new StringBuilder((certInfoBytes.Length - sizeof(UInt32)) * 2); + for (int i = 4; i < certInfoBytes.Length; i++) + hex.AppendFormat("{0:x2}", certInfoBytes[i]); + + return hex.ToString().ToUpperInvariant(); + } + } + + internal static void PtrToStructureArray<T>(T[] array, IntPtr ptr) + { + IntPtr ptrOffset = ptr; + for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T)))) + array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T)); + } + } +} +'@ + +$type = switch ($type) { + "domain_password" { [Ansible.CredentialManager.CredentialType]::DomainPassword } + "domain_certificate" { [Ansible.CredentialManager.CredentialType]::DomainCertificate } + "generic_password" { [Ansible.CredentialManager.CredentialType]::Generic } + "generic_certificate" { [Ansible.CredentialManager.CredentialType]::GenericCertificate } +} + +$credential = [Ansible.CredentialManager.Credential]::GetCredential($name, $type) +if ($null -ne $credential) { + $module.Result.exists = $true + $module.Result.alias = $credential.TargetAlias + $module.Result.attributes = [System.Collections.ArrayList]@() + $module.Result.comment = $credential.Comment + $module.Result.name = $credential.TargetName + $module.Result.persistence = $credential.Persist.ToString() + $module.Result.type = $credential.Type.ToString() + $module.Result.username = $credential.UserName + + if ($null -ne $credential.Secret) { + $module.Result.secret = [System.Convert]::ToBase64String($credential.Secret) + } + else { + $module.Result.secret = $null + } + + foreach ($attribute in $credential.Attributes) { + $attribute_info = @{ + name = $attribute.Keyword + } + if ($null -ne $attribute.Value) { + $attribute_info.data = [System.Convert]::ToBase64String($attribute.Value) + } + else { + $attribute_info.data = $null + } + $module.Result.attributes.Add($attribute_info) > $null + } +} +else { + $module.Result.exists = $false +} + +$module.ExitJson() + diff --git a/ansible_collections/community/windows/tests/integration/targets/win_credential/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_credential/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_credential/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_credential/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_credential/tasks/main.yml new file mode 100644 index 000000000..1ec3bbb3a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_credential/tasks/main.yml @@ -0,0 +1,64 @@ +--- +- name: ensure test dir is present + ansible.windows.win_file: + path: '{{ test_credential_dir }}' + state: directory + +- name: copy the pfx certificate + ansible.windows.win_copy: + src: cert.pfx + dest: '{{ test_credential_dir }}\cert.pfx' + +- name: import the pfx into the personal store + ansible.windows.win_certificate_store: + path: '{{ test_credential_dir }}\cert.pfx' + state: present + store_location: CurrentUser + store_name: My + password: '{{ key_password }}' + vars: &become_vars + ansible_become: True + ansible_become_method: runas + ansible_become_user: '{{ ansible_user }}' + ansible_become_pass: '{{ ansible_password }}' + +- name: ensure test credentials are removed before testing + win_credential: + name: '{{ test_hostname }}' + type: '{{ item }}' + state: absent + vars: *become_vars + with_items: + - domain_password + - domain_certificate + - generic_password + - generic_certificate + +- block: + - name: run tests + include_tasks: tests.yml + + always: + - name: remove the pfx from the personal store + ansible.windows.win_certificate_store: + state: absent + thumbprint: '{{ cert_thumbprint }}' + store_location: CurrentUser + store_name: My + + - name: remove test credentials + win_credential: + name: '{{ test_hostname }}' + type: '{{ item }}' + state: absent + vars: *become_vars + with_items: + - domain_password + - domain_certificate + - generic_password + - generic_certificate + + - name: remove test dir + ansible.windows.win_file: + path: '{{ test_credential_dir }}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_credential/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_credential/tasks/tests.yml new file mode 100644 index 000000000..cec2cf023 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_credential/tasks/tests.yml @@ -0,0 +1,638 @@ +--- +- name: fail to run the module without become + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username + secret: password + state: present + register: fail_no_become + failed_when: '"Failed to access the user''s credential store, run the module with become" not in fail_no_become.msg' + +- name: create domain user credential (check mode) + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username + secret: password + state: present + register: domain_user_check + check_mode: True + vars: &become_vars + ansible_become: True + ansible_become_method: runas + ansible_become_user: '{{ ansible_user }}' + ansible_become_pass: '{{ ansible_password }}' + +- name: get result of create domain user credential (check mode) + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: domain_user_actual_check + vars: *become_vars + +- name: asset create domain user credential (check mode) + assert: + that: + - domain_user_check is changed + - not domain_user_actual_check.exists + +- name: create domain user credential + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username + secret: password + state: present + register: domain_user + vars: *become_vars + +- name: get result of create domain user credential + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: domain_user_actual + vars: *become_vars + +- name: asset create domain user credential + assert: + that: + - domain_user is changed + - domain_user_actual.exists + - domain_user_actual.alias == None + - domain_user_actual.attributes == [] + - domain_user_actual.comment == None + - domain_user_actual.name == test_hostname + - domain_user_actual.persistence == "LocalMachine" + - domain_user_actual.secret == "" + - domain_user_actual.type == "DomainPassword" + - domain_user_actual.username == "DOMAIN\\username" + +- name: create domain user credential again always update + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username + secret: password + state: present + register: domain_user_again_always + vars: *become_vars + +- name: create domain user credential again on_create + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username + secret: password + update_secret: on_create + state: present + register: domain_user_again_on_create + vars: *become_vars + +- name: assert create domain user credential again + assert: + that: + - domain_user_again_always is changed + - not domain_user_again_on_create is changed + +- name: update credential (check mode) + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username2 + alias: ansible + attributes: + - name: attribute 1 + data: attribute 1 value + - name: attribute 2 + data: '{{ "attribute 2 value" | b64encode }}' + data_format: base64 + comment: Credential comment + persistence: enterprise + state: present + register: update_cred_check + check_mode: True + vars: *become_vars + +- name: get result of update credential (check mode) + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: update_cred_actual_check + vars: *become_vars + +- name: assert update credential (check mode) + assert: + that: + - update_cred_check is changed + - update_cred_actual_check.exists + - update_cred_actual_check.alias == None + - update_cred_actual_check.attributes == [] + - update_cred_actual_check.comment == None + - update_cred_actual_check.name == test_hostname + - update_cred_actual_check.persistence == "LocalMachine" + - update_cred_actual_check.secret == "" + - update_cred_actual_check.type == "DomainPassword" + - update_cred_actual_check.username == "DOMAIN\\username" + +- name: update credential + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username2 + alias: ansible + attributes: + - name: attribute 1 + data: attribute 1 value + - name: attribute 2 + data: '{{ "attribute 2 value" | b64encode }}' + data_format: base64 + comment: Credential comment + persistence: enterprise + state: present + register: update_cred + vars: *become_vars + +- name: get result of update credential + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: update_cred_actual + vars: *become_vars + +- name: assert update credential + assert: + that: + - update_cred is changed + - update_cred_actual.exists + - update_cred_actual.alias == "ansible" + - update_cred_actual.attributes|count == 2 + - update_cred_actual.attributes[0].name == "attribute 1" + - update_cred_actual.attributes[0].data == "attribute 1 value"|b64encode + - update_cred_actual.attributes[1].name == "attribute 2" + - update_cred_actual.attributes[1].data == "attribute 2 value"|b64encode + - update_cred_actual.comment == "Credential comment" + - update_cred_actual.name == test_hostname + - update_cred_actual.persistence == "Enterprise" + - update_cred_actual.secret == "" + - update_cred_actual.type == "DomainPassword" + - update_cred_actual.username == "DOMAIN\\username2" + +- name: update credential again + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username2 + alias: ansible + attributes: + - name: attribute 1 + data: attribute 1 value + - name: attribute 2 + data: '{{ "attribute 2 value" | b64encode }}' + data_format: base64 + comment: Credential comment + persistence: enterprise + state: present + register: update_cred_again + vars: *become_vars + +- name: assert update credential again + assert: + that: + - not update_cred_again is changed + +- name: add new attribute + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username2 + alias: ansible + attributes: + - name: attribute 1 + data: attribute 1 value + - name: attribute 2 + data: '{{ "attribute 2 value" | b64encode }}' + data_format: base64 + - name: attribute 3 + data: attribute 3 value + comment: Credential comment + persistence: enterprise + state: present + register: add_attribute + vars: *become_vars + +- name: get result of add new attribute + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: add_attribute_actual + vars: *become_vars + +- name: assert add new attribute + assert: + that: + - add_attribute is changed + - add_attribute_actual.attributes|count == 3 + - add_attribute_actual.attributes[0].name == "attribute 1" + - add_attribute_actual.attributes[0].data == "attribute 1 value"|b64encode + - add_attribute_actual.attributes[1].name == "attribute 2" + - add_attribute_actual.attributes[1].data == "attribute 2 value"|b64encode + - add_attribute_actual.attributes[2].name == "attribute 3" + - add_attribute_actual.attributes[2].data == "attribute 3 value"|b64encode + +- name: remove attribute + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username2 + alias: ansible + attributes: + - name: attribute 1 + data: attribute 1 value + - name: attribute 2 + data: '{{ "attribute 2 value" | b64encode }}' + data_format: base64 + comment: Credential comment + persistence: enterprise + state: present + register: remove_attribute + vars: *become_vars + +- name: get result of remove attribute + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: remove_attribute_actual + vars: *become_vars + +- name: assert remove attribute + assert: + that: + - remove_attribute is changed + - remove_attribute_actual.attributes|count == 2 + - remove_attribute_actual.attributes[0].name == "attribute 1" + - remove_attribute_actual.attributes[0].data == "attribute 1 value"|b64encode + - remove_attribute_actual.attributes[1].name == "attribute 2" + - remove_attribute_actual.attributes[1].data == "attribute 2 value"|b64encode + +- name: edit attribute + win_credential: + name: '{{ test_hostname }}' + type: domain_password + username: DOMAIN\username2 + alias: ansible + attributes: + - name: attribute 1 + data: attribute 1 value new + - name: attribute 2 + data: '{{ "attribute 2 value" | b64encode }}' + data_format: base64 + comment: Credential comment + persistence: enterprise + state: present + register: edit_attribute + vars: *become_vars + +- name: get result of edit attribute + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: edit_attribute_actual + vars: *become_vars + +- name: assert remove attribute + assert: + that: + - edit_attribute is changed + - edit_attribute_actual.attributes|count == 2 + - edit_attribute_actual.attributes[0].name == "attribute 1" + - edit_attribute_actual.attributes[0].data == "attribute 1 value new"|b64encode + - edit_attribute_actual.attributes[1].name == "attribute 2" + - edit_attribute_actual.attributes[1].data == "attribute 2 value"|b64encode + +- name: remove credential (check mode) + win_credential: + name: '{{ test_hostname }}' + type: domain_password + state: absent + register: remove_cred_check + check_mode: True + vars: *become_vars + +- name: get result of remove credential (check mode) + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: remove_cred_actual_check + vars: *become_vars + +- name: assert remove credential (check mode) + assert: + that: + - remove_cred_check is changed + - remove_cred_actual_check.exists + +- name: remove credential + win_credential: + name: '{{ test_hostname }}' + type: domain_password + state: absent + register: remove_cred + vars: *become_vars + +- name: get result of remove credential + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_password + register: remove_cred_actual + vars: *become_vars + +- name: assert remove credential + assert: + that: + - remove_cred is changed + - not remove_cred_actual.exists + +- name: remove credential again + win_credential: + name: '{{ test_hostname }}' + type: domain_password + state: absent + register: remove_cred_again + vars: *become_vars + +- name: assert remove credential again + assert: + that: + - not remove_cred_again is changed + +# https://github.com/ansible/ansible/issues/67278 +- name: create credential with wildcard + win_credential: + name: '*.{{ test_hostname }}' + type: domain_password + username: DOMAIN\username + secret: password + state: present + persistence: enterprise + register: wildcard_cred + vars: *become_vars + +- name: get result of create credential with wildcard + test_cred_facts: + name: '*.{{ test_hostname }}' + type: domain_password + register: wildcard_cred_actual + vars: *become_vars + +- name: assert create credential with wildcard + assert: + that: + - wildcard_cred is changed + - wildcard_cred_actual.name == '*.' ~ test_hostname + +- name: remove credential with wildcard + win_credential: + name: '*.{{ test_hostname }}' + type: domain_password + state: absent + register: wildcard_remove + vars: *become_vars + +- name: get result of remove credential with wildcard + test_cred_facts: + name: '*.{{ test_hostname }}' + type: domain_password + register: wildcard_remove_actual + vars: *become_vars + +- name: assert remove credential with wildcard + assert: + that: + - wildcard_remove is changed + - not wildcard_remove_actual.exists + +- name: create generic password (check mode) + win_credential: + name: '{{ test_hostname }}' + type: generic_password + persistence: enterprise + username: genericuser + secret: genericpass + state: present + register: generic_password_check + check_mode: True + vars: *become_vars + +- name: get result of create generic password (check mode) + test_cred_facts: + name: '{{ test_hostname }}' + type: generic_password + register: generic_password_actual_check + vars: *become_vars + +- name: assert result of create generic password (check mode) + assert: + that: + - generic_password_check is changed + - not generic_password_actual_check.exists + +- name: create generic password + win_credential: + name: '{{ test_hostname }}' + type: generic_password + persistence: enterprise + username: genericuser + secret: genericpass + state: present + register: generic_password + vars: *become_vars + +- name: get result of create generic password + test_cred_facts: + name: '{{ test_hostname }}' + type: generic_password + register: generic_password_actual + vars: *become_vars + +- name: set encoded password result + set_fact: + encoded_pass: '{{ "genericpass" | string | b64encode(encoding="utf-16-le") }}' + +- name: assert create generic password + assert: + that: + - generic_password is changed + - generic_password_actual.exists + - generic_password_actual.alias == None + - generic_password_actual.attributes == [] + - generic_password_actual.comment == None + - generic_password_actual.name == test_hostname + - generic_password_actual.persistence == "Enterprise" + - generic_password_actual.secret == encoded_pass + - generic_password_actual.type == "Generic" + - generic_password_actual.username == "genericuser" + +- name: create generic password again + win_credential: + name: '{{ test_hostname }}' + type: generic_password + persistence: enterprise + username: genericuser + secret: genericpass + state: present + register: generic_password_again + vars: *become_vars + +- name: assert create generic password again + assert: + that: + - not generic_password_again is changed + +- name: fail to create certificate cred with invalid thumbprint + win_credential: + name: '{{ test_hostname }}' + type: domain_certificate + username: 00112233445566778899AABBCCDDEEFF00112233 + state: present + register: fail_invalid_cert + failed_when: fail_invalid_cert.msg != "Failed to find certificate with the thumbprint 00112233445566778899AABBCCDDEEFF00112233 in the CurrentUser\\My store" + vars: *become_vars + +- name: create domain certificate cred (check mode) + win_credential: + name: '{{ test_hostname }}' + type: domain_certificate + username: '{{ cert_thumbprint }}' + state: present + register: domain_cert_check + check_mode: True + vars: *become_vars + +- name: get result of create domain certificate cred (check mode) + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_certificate + register: domain_cert_actual_check + vars: *become_vars + +- name: assert create domain certificate cred (check mode) + assert: + that: + - domain_cert_check is changed + - not domain_cert_actual_check.exists + +- name: create domain certificate cred + win_credential: + name: '{{ test_hostname }}' + type: domain_certificate + username: '{{ cert_thumbprint }}' + state: present + register: domain_cert + vars: *become_vars + +- name: get result of create domain certificate cred + test_cred_facts: + name: '{{ test_hostname }}' + type: domain_certificate + register: domain_cert_actual + vars: *become_vars + +- name: assert create domain certificate cred + assert: + that: + - domain_cert is changed + - domain_cert_actual.exists + - domain_cert_actual.alias == None + - domain_cert_actual.attributes == [] + - domain_cert_actual.comment == None + - domain_cert_actual.name == test_hostname + - domain_cert_actual.persistence == "LocalMachine" + - domain_cert_actual.secret == "" + - domain_cert_actual.type == "DomainCertificate" + - domain_cert_actual.username == cert_thumbprint + +- name: create domain certificate cred again + win_credential: + name: '{{ test_hostname }}' + type: domain_certificate + username: '{{ cert_thumbprint }}' + state: present + register: domain_cert_again + vars: *become_vars + +- name: assert create domain certificate cred again + assert: + that: + - not domain_cert_again is changed + +- name: create generic certificate cred (check mode) + win_credential: + name: '{{ test_hostname }}' + type: generic_certificate + username: '{{ cert_thumbprint }}' + secret: '{{ "pin code" | b64encode }}' + secret_format: base64 + state: present + register: generic_cert_check + check_mode: True + vars: *become_vars + +- name: get result of create generic certificate cred (check mode) + test_cred_facts: + name: '{{ test_hostname }}' + type: generic_certificate + register: generic_cert_actual_check + vars: *become_vars + +- name: assert create generic certificate cred (check mode) + assert: + that: + - generic_cert_check is changed + - not generic_cert_actual_check.exists + +- name: create generic certificate cred + win_credential: + name: '{{ test_hostname }}' + type: generic_certificate + username: '{{ cert_thumbprint }}' + secret: '{{ "pin code" | b64encode }}' + secret_format: base64 + state: present + register: generic_cert + vars: *become_vars + +- name: get result of create generic certificate cred + test_cred_facts: + name: '{{ test_hostname }}' + type: generic_certificate + register: generic_cert_actual + vars: *become_vars + +- name: assert create generic certificate cred + assert: + that: + - generic_cert is changed + - generic_cert_actual.exists + - generic_cert_actual.alias == None + - generic_cert_actual.attributes == [] + - generic_cert_actual.comment == None + - generic_cert_actual.name == test_hostname + - generic_cert_actual.persistence == "LocalMachine" + - generic_cert_actual.secret == "pin code" | b64encode + - generic_cert_actual.type == "GenericCertificate" + - generic_cert_actual.username == cert_thumbprint + +- name: create generic certificate cred again + win_credential: + name: '{{ test_hostname }}' + type: generic_certificate + username: '{{ cert_thumbprint }}' + state: present + register: generic_cert_again + vars: *become_vars + +- name: assert create generic certificate cred again + assert: + that: + - not generic_cert_again is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/aliases b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/aliases new file mode 100644 index 000000000..2a4f8cc66 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/aliases @@ -0,0 +1,2 @@ +shippable/windows/group3 +skip/windows/2012 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/main.yml new file mode 100644 index 000000000..ae6be90ec --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- include: pre_test.yml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/pre_test.yml b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/pre_test.yml new file mode 100644 index 000000000..d63468990 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/pre_test.yml @@ -0,0 +1,40 @@ +--- +- set_fact: + AnsibleVhdx: '{{ remote_tmp_dir }}\AnsiblePart.vhdx' + +- name: Install FS-Data-Deduplication + ansible.windows.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 + ansible.windows.win_reboot: + reboot_timeout: 3600 + when: + - data_dedup_feat_reg.success + - data_dedup_feat_reg.reboot_required + +- name: Copy VHDX scripts + ansible.windows.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 + ansible.windows.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 + ansible.windows.win_command: diskpart.exe /s {{ remote_tmp_dir }}\partition_deletion_script.txt diff --git a/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/tasks/tests.yml new file mode 100644 index 000000000..64a429271 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/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/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/templates/partition_creation_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/templates/partition_creation_script.j2 new file mode 100644 index 000000000..8e47fda95 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/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/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/templates/partition_deletion_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/templates/partition_deletion_script.j2 new file mode 100644 index 000000000..c2be9cd14 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_data_deduplication/templates/partition_deletion_script.j2 @@ -0,0 +1,3 @@ +select vdisk file="{{ AnsibleVhdx }}" + +detach vdisk diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/aliases b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/defaults/main.yml new file mode 100644 index 000000000..4eb43278e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/defaults/main.yml @@ -0,0 +1,8 @@ +--- +dhcp_lease_ip: 172.16.98.230 +dhcp_scope_id: 172.16.98.0 +dhcp_scope_start: 172.16.98.1 +dhcp_scope_end: 172.16.98.254 +dhcp_scope_subnet_mask: 255.255.255.0 +dhcp_lease_mac: 0A-0B-0C-04-05-AA +dhcp_lease_hostname: fancy-reservation diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/main.yml new file mode 100644 index 000000000..d9b36a287 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- block: + - name: Check DHCP Service/Role Install State + ansible.windows.win_feature: + name: DHCP + state: present + include_management_tools: yes + register: dhcp_role + + - name: Reboot if Necessary + ansible.windows.win_reboot: + when: dhcp_role.reboot_required + + - name: Add the DHCP scope + ansible.windows.win_shell: | + Add-DhcpServerv4Scope -Name "TestNetwork" -StartRange {{ dhcp_scope_start }} -EndRange {{dhcp_scope_end }} -SubnetMask {{ dhcp_scope_subnet_mask }} + + - name: Run tests without check mode + include_tasks: tests.yml + + - name: Run tests in check mode + include_tasks: tests_checkmode.yml + + always: + - name: Remove the DHCP scope + ansible.windows.win_shell: | + Remove-DhcpServerv4Scope -ScopeId {{ dhcp_scope_id }} -Force
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/tests.yml new file mode 100644 index 000000000..98bcc50ab --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/tests.yml @@ -0,0 +1,108 @@ +--- +- name: Remove DHCP Address by IP + win_dhcp_lease: + state: absent + ip: "{{ dhcp_lease_ip }}" + +- name: Remove DHCP Address by IP (Idempotentcy Check) - Changed should equal false + win_dhcp_lease: + state: absent + ip: "{{ dhcp_lease_ip }}" + register: remove_reservation_ip + failed_when: remove_reservation_ip.changed != false + +- name: Create New DHCP Lease + win_dhcp_lease: + type: lease + ip: "{{ dhcp_lease_ip }}" + scope_id: "{{ dhcp_scope_id }}" + mac: "{{ dhcp_lease_mac }}" + dns_hostname: "{{ dhcp_lease_hostname }}" + dns_regtype: noreg + description: This is a description! + +- name: Create New DHCP Lease (Idempotentcy Check) - Changed should equal false + win_dhcp_lease: + type: lease + ip: "{{ dhcp_lease_ip }}" + scope_id: "{{ dhcp_scope_id }}" + mac: "{{ dhcp_lease_mac }}" + dns_hostname: "{{ dhcp_lease_hostname }}" + dns_regtype: noreg + description: This is a description! + register: create_lease + failed_when: create_lease.changed != false + +- name: Validate the Lease + ansible.windows.win_shell: | + Get-DhcpServerv4Scope | Get-DhcpServerv4Lease | Where-Object IPAddress -eq {{ dhcp_lease_ip }} + register: validate_lease_out + failed_when: validate_lease_out.stdout == "" + +- name: Convert Lease to Reservation + win_dhcp_lease: + type: reservation + ip: "{{ dhcp_lease_ip }}" + +- name: Convert Lease to Reservation (Idempotentcy Check) - Changed should equal false + win_dhcp_lease: + type: reservation + ip: "{{ dhcp_lease_ip }}" + register: convert_lease_to_reservation + failed_when: convert_lease_to_reservation.changed != false + +- name: Validate the Reservation + ansible.windows.win_shell: | + Get-DhcpServerv4Scope | Get-DhcpServerv4Reservation | Where-Object IPAddress -eq {{ dhcp_lease_ip }} + register: validate_reservation_out + failed_when: validate_reservation_out.stdout == "" + +- name: Update Reservation Description + win_dhcp_lease: + type: reservation + mac: "{{ dhcp_lease_mac }}" + description: Changed Description! + +- name: Update Reservation Description (Idempotentcy Check) - Changed should equal false + win_dhcp_lease: + type: reservation + mac: "{{ dhcp_lease_mac }}" + description: Changed Description! + register: update_reservation_description + failed_when: update_reservation_description.changed != false + +- name: Validate the Description + ansible.windows.win_shell: | + Get-DhcpServerv4Scope | Get-DhcpServerv4Lease | Where-Object {($_.ClientId -eq "{{ dhcp_lease_mac }}") -and ($_.Description -eq "Changed Description!")} + register: validate_description_out + failed_when: validate_description_out.stdout == "" + +- name: Convert Reservation to Lease + win_dhcp_lease: + type: lease + ip: "{{ dhcp_lease_ip }}" + +- name: Convert Reservation to Lease (Idempotentcy Check) - Changed should equal false + win_dhcp_lease: + type: lease + ip: "{{ dhcp_lease_ip }}" + register: convert_reservation_to_lease + failed_when: convert_reservation_to_lease.changed != false + +- name: Remove DHCP Reservation + win_dhcp_lease: + state: absent + mac: "{{ dhcp_lease_mac }}" + +- name: Remove DHCP Reservation (Idempotentcy Check) - Changed should equal false + win_dhcp_lease: + state: absent + mac: "{{ dhcp_lease_mac }}" + register: remove_reservation + failed_when: remove_reservation.changed != false + +- name: Validate the State + ansible.windows.win_shell: | + Get-DhcpServerv4Scope | Get-DhcpServerv4Reservation | Where-Object IPAddress -eq {{ dhcp_lease_ip }} + register: validate_state_out + failed_when: validate_state_out.stdout != ""
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/tests_checkmode.yml b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/tests_checkmode.yml new file mode 100644 index 000000000..61f6ab5df --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dhcp_lease/tasks/tests_checkmode.yml @@ -0,0 +1,52 @@ +--- +- name: Check DHCP Service/Role Install State + ansible.windows.win_feature: + name: DHCP + state: present + include_management_tools: yes + register: dhcp_role + check_mode: true + +- name: Reboot if Necessary + ansible.windows.win_reboot: + when: dhcp_role.reboot_required + check_mode: true + +- name: Remove DHCP Address by IP + win_dhcp_lease: + state: absent + ip: "{{ dhcp_lease_ip }}" + check_mode: true + +- name: Create Lease + win_dhcp_lease: + type: lease + ip: "{{ dhcp_lease_ip }}" + scope_id: "{{ dhcp_scope_id }}" + mac: "{{ dhcp_lease_mac }}" + dns_hostname: "{{ dhcp_lease_hostname }}" + dns_regtype: noreg + description: This is a description! + check_mode: true + +- name: Create Reservation + win_dhcp_lease: + type: reservation + ip: "{{ dhcp_lease_ip }}" + mac: "{{ dhcp_lease_mac }}" + scope_id: "{{ dhcp_scope_id }}" + check_mode: true + +- name: Create Reservation w/Description + win_dhcp_lease: + type: reservation + ip: "{{ dhcp_lease_ip }}" + mac: "{{ dhcp_lease_mac }}" + scope_id: "{{ dhcp_scope_id }}" + description: This is a Description! + check_mode: true + +- name: Remove DHCP Reservation by MAC + win_dhcp_lease: + state: absent + mac: "{{ dhcp_lease_mac }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/aliases b/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/aliases new file mode 100644 index 000000000..e4adbabbe --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/aliases @@ -0,0 +1,3 @@ +shippable/windows/group2 +skip/windows/2008 # The Storage PowerShell module was introduced in W2K12 +skip/windows/2008-R2 # The Storage PowerShell module was introduced in W2K12 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/tasks/main.yml new file mode 100644 index 000000000..f1873efa7 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/tasks/main.yml @@ -0,0 +1,13 @@ +# NOTE: The win_disk_facts module only works on Win2012R2+ + +- name: check whether storage module is available (windows 2008 r2 or later) + ansible.windows.win_shell: '(Get-Module -Name Storage -ListAvailable | Measure-Object).Count -eq 1' + register: win_feature_has_storage_module + changed_when: false + +- name: Only run tests when Windows is capable + when: win_feature_has_storage_module.stdout | trim | bool == True + block: + + - name: Test in normal mode + include: tests.yml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/tasks/tests.yml new file mode 100644 index 000000000..36d9a39e6 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_disk_facts/tasks/tests.yml @@ -0,0 +1,89 @@ +- name: get disk facts on the target + win_disk_facts: + register: disks_found + +- name: assert disk facts + assert: + that: + - disks_found.changed == false + - disks_found.ansible_facts.ansible_disks[0].number is defined + - disks_found.ansible_facts.ansible_disks[0].guid is defined + - disks_found.ansible_facts.ansible_disks[0].location is defined + - disks_found.ansible_facts.ansible_disks[0].path is defined + - disks_found.ansible_facts.ansible_disks[0].read_only is defined + - disks_found.ansible_facts.ansible_disks[0].clustered is defined + - disks_found.ansible_facts.ansible_disks[0].bootable is defined + - disks_found.ansible_facts.ansible_disks[0].physical_disk.size is defined + - disks_found.ansible_facts.ansible_disks[0].physical_disk.operational_status is defined + - disks_found.ansible_facts.ansible_disks[0].win32_disk_drive is defined + - disks_found.ansible_facts.ansible_disks[0].partitions is defined + - disks_found.ansible_facts.ansible_disks[0].partitions[0].volumes is defined + +- name: get disk partition facts on the target + win_disk_facts: + filter: + - partitions + register: disks_partitions_found + +- name: assert partitions disk facts + assert: + that: + - disks_partitions_found.changed == false + - disks_partitions_found.ansible_facts.ansible_disks[0].partitions is defined + +- name: get disk volume and partition facts on the target + win_disk_facts: + filter: + - volumes + register: disks_volumes_found + +- name: assert volume and partition disk facts + assert: + that: + - disks_volumes_found.changed == false + - disks_volumes_found.ansible_facts.ansible_disks[0].partitions is defined + - disks_volumes_found.ansible_facts.ansible_disks[0].partitions[0].volumes is defined + + +- name: get disk physical_disk facts on the target + win_disk_facts: + filter: + - physical_disk + register: physical_disk_found + +- name: assert physical_disk disk facts + assert: + that: + - physical_disk_found.changed == false + - physical_disk_found.ansible_facts.ansible_disks[0].physical_disk is defined + +- name: get disk virtual_disk facts on the target + win_disk_facts: + filter: + - virtual_disk + register: virtual_disk_found + +- name: check if virtual_disk should be found + ansible.windows.win_shell: | + if (Get-VirtualDisk){$true}else{$false} + register: virtual_available + changed_when: false + +- name: assert virtual_disk disk facts + assert: + that: + - virtual_disk_found.changed == false + - virtual_disk_found.ansible_facts.ansible_disks[0].virtual_disk is defined + when: virtual_available.stdout == "True" + +- name: get disk win32_disk_drive facts on the target + win_disk_facts: + filter: + - win32_disk_drive + register: win32_disk_drive_found + +- name: assert win32_disk_drive disk facts + assert: + that: + - win32_disk_drive_found.changed == false + - win32_disk_drive_found.ansible_facts.ansible_disks[0].win32_disk_drive is defined diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/aliases b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/defaults/main.yml new file mode 100644 index 000000000..496102481 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/defaults/main.yml @@ -0,0 +1,3 @@ +win_dns_record_zone: test.ansible.local +win_dns_record_revzone: 0.0.255.in-addr.arpa +win_dns_record_revzone_network: 255.0.0.0/24 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/clean.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/clean.yml new file mode 100644 index 000000000..29dc1cd97 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/clean.yml @@ -0,0 +1,17 @@ +- name: Remove test zone, if present + ansible.windows.win_shell: | + $zone = '{{ item }}' + $fail_on_missing = '{{ fail_on_missing | default(true) }}' + + Trap { If (-not $fail_on_missing) { continue } } + Remove-DnsServerZone -Name $zone -Force + + # win_file could also do this, but it would need to know where the + # SystemRoot is located via fact gathering, which we cannot assume. + Trap { If (-not $fail_on_missing) { continue } } + Remove-Item -Path $env:SystemRoot\system32\dns\$zone.dns + + $true # so pipeline exits cleanly if an error was ignored above + loop: + - '{{ win_dns_record_zone }}' + - '{{ win_dns_record_revzone }}' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/main.yml new file mode 100644 index 000000000..9b06a65b0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/main.yml @@ -0,0 +1,12 @@ +# We do an explicit OS version check here *INSTEAD OF* the usual test for +# cmdlet existence. That's because a cmdlet test here won't work without first +# installing the DNS feature, but we don't want to install the feature on OS' +# that can't be supported anyway. Hence this fallback to an explicit OS version +# test. +- name: check OS version is supported + ansible.windows.win_shell: 'if ([Environment]::OSVersion.Version -ge [Version]"6.2") { $true } else { $false }' + register: os_supported + +- name: run tests on supported hosts + include: tests.yml + when: os_supported.stdout | trim | bool diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-A.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-A.yml new file mode 100644 index 000000000..dad1d82ea --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-A.yml @@ -0,0 +1,294 @@ +- name: 'TYPE=A - creation (check mode)' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, value: 1.2.3.4, type: A } + register: cmd_result + check_mode: yes + +- name: 'TYPE=A - creation get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - creation check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=A - creation' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, value: 1.2.3.4, type: A } + register: cmd_result + +- name: 'TYPE=A - creation get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv4Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - creation check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '1.2.3.4\r\n' + +- name: 'TYPE=A - creation (idempotent)' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, value: 1.2.3.4, type: A } + register: cmd_result + +- name: 'TYPE=A - creation get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv4Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - creation check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '1.2.3.4\r\n' + +- name: 'TYPE=A - update address (check mode)' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, value: 5.6.7.8, type: A } + register: cmd_result + check_mode: yes + +- name: 'TYPE=A - update address get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv4Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - update address check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '1.2.3.4\r\n' + +- name: 'TYPE=A - update address' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, value: 5.6.7.8, type: A } + register: cmd_result + +- name: 'TYPE=A - update address get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv4Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - update address check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '5.6.7.8\r\n' + +- name: 'TYPE=A - update address (idempotent)' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, value: 5.6.7.8, type: A } + register: cmd_result + +- name: 'TYPE=A - update address get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv4Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - update address check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '5.6.7.8\r\n' + +- name: 'TYPE=A - update TTL (check mode)' + win_dns_record: + { + zone: '{{ win_dns_record_zone }}', + name: test1, + value: 5.6.7.8, + ttl: 7200, + type: A, + } + register: cmd_result + check_mode: yes + +- name: 'TYPE=A - update TTL get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - update TTL check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: 'TYPE=A - update TTL' + win_dns_record: + { + zone: '{{ win_dns_record_zone }}', + name: test1, + value: 5.6.7.8, + ttl: 7200, + type: A, + } + register: cmd_result + +- name: 'TYPE=A - update TTL get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - update TTL check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=A - update TTL (idempotent)' + win_dns_record: + { + zone: '{{ win_dns_record_zone }}', + name: test1, + value: 5.6.7.8, + ttl: 7200, + type: A, + } + register: cmd_result + +- name: 'TYPE=A - update TTL get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - update TTL check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=A - remove record (check mode)' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, type: A, state: absent } + register: cmd_result + check_mode: yes + +- name: 'TYPE=A - remove record get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - remove record check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: 'TYPE=A - remove record' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, type: A, state: absent } + register: cmd_result + +- name: 'TYPE=A - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - remove record check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=A - remove record (idempotent)' + win_dns_record: + { zone: '{{ win_dns_record_zone }}', name: test1, type: A, state: absent } + register: cmd_result + +- name: 'TYPE=A - remove record get results (idempotent)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType A -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - remove record check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n' + +# tests for aging +# ----------------------------------------------------------------------------- +# create aging record +- name: 'TYPE=A - add aging record' + win_dns_record: + zone: '{{ win_dns_record_zone }}' + name: testaging + value: 1.2.3.4 + aging: true + type: A + state: present + register: cmd_result + +- name: 'Type=A - check if record is actually aging' + ansible.windows.win_command: powershell.exe "If ($null -ne (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testaging' -RRType A).Timestamp) {'aging'} else {'not aging'}" + register: cmd_result_actual + +- name: 'TYPE=A - check aging record results' + ansible.builtin.assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'aging\r\n' + +# check idempotency +- name: 'TYPE=A - check aging record (idempotency)' + win_dns_record: + zone: '{{ win_dns_record_zone }}' + name: testaging + value: 1.2.3.4 + aging: true + type: A + state: present + register: cmd_result + +- name: 'TYPE=A - check aging record' + ansible.builtin.assert: + that: + - cmd_result is not changed + +# change aging attribute +- name: 'TYPE=A - aging record (change aging)' + win_dns_record: + zone: '{{ win_dns_record_zone }}' + name: testaging + aging: false + value: 1.2.3.4 + type: A + state: present + register: cmd_result + +- name: Type=A - check if record is not aging + ansible.windows.win_command: powershell.exe "If ($null -ne (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testaging' -RRType A).Timestamp) {'aging'} else {'not aging'}" + register: cmd_result_actual + +- name: 'TYPE=A - check not aging record results' + ansible.builtin.assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'not aging\r\n' + +# remove record again +- name: 'TYPE=A - remove record' + win_dns_record: + zone: '{{ win_dns_record_zone }}' + name: testaging + type: A + state: absent + register: cmd_result + +- name: 'TYPE=A - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testaging' -RRType A -Node -ErrorAction:Ignore) {'exists'} else {'absent'}" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=A - remove record check results' + ansible.builtin.assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' +# ----------------------------------------------------------------------------- diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-AAAA.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-AAAA.yml new file mode 100644 index 000000000..9db8ffb64 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-AAAA.yml @@ -0,0 +1,186 @@ +- name: 'TYPE=AAAA - creation (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::1', type: AAAA} + register: cmd_result + check_mode: yes + +- name: 'TYPE=AAAA - creation get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - creation check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=AAAA - creation' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::1', type: AAAA} + register: cmd_result + +- name: 'TYPE=AAAA - creation get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv6Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - creation check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '2001:db8::1\r\n' + +- name: 'TYPE=AAAA - creation (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::1', type: AAAA} + register: cmd_result + +- name: 'TYPE=AAAA - creation get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv6Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - creation check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '2001:db8::1\r\n' + + +- name: 'TYPE=AAAA - update address (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::2', type: AAAA} + register: cmd_result + check_mode: yes + +- name: 'TYPE=AAAA - update address get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv6Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - update address check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '2001:db8::1\r\n' + +- name: 'TYPE=AAAA - update address' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::2', type: AAAA} + register: cmd_result + +- name: 'TYPE=AAAA - update address get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv6Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - update address check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '2001:db8::2\r\n' + +- name: 'TYPE=AAAA - update address (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::2', type: AAAA} + register: cmd_result + +- name: 'TYPE=AAAA - update address get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty IPv6Address | Select -ExpandProperty IPAddressToString" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - update address check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '2001:db8::2\r\n' + + +- name: 'TYPE=AAAA - update TTL (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::2', ttl: 7200, type: AAAA} + register: cmd_result + check_mode: yes + +- name: 'TYPE=AAAA - update TTL get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - update TTL check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: 'TYPE=AAAA - update TTL' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::2', ttl: 7200, type: AAAA} + register: cmd_result + +- name: 'TYPE=AAAA - update TTL get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - update TTL check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=AAAA - update address (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: '2001:db8::2', ttl: 7200, type: AAAA} + register: cmd_result + +- name: 'TYPE=AAAA - update address get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - update address check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + + +- name: 'TYPE=AAAA - remove record (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, type: AAAA, state: absent} + register: cmd_result + check_mode: yes + +- name: 'TYPE=AAAA - remove record get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - remove record check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: 'TYPE=AAAA - remove record' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, type: AAAA, state: absent} + register: cmd_result + +- name: 'TYPE=AAAA - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - remove record check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=AAAA - remove record (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, type: AAAA, state: absent} + register: cmd_result + +- name: 'TYPE=AAAA - remove record get results (idempotent)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType AAAA -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=AAAA - remove record check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-CNAME.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-CNAME.yml new file mode 100644 index 000000000..d806b0a4e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-CNAME.yml @@ -0,0 +1,205 @@ +- name: 'TYPE=CNAME - creation (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: www.ansible.com, type: CNAME} + register: cmd_result + check_mode: yes + +- name: 'TYPE=CNAME - creation get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - creation check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=CNAME - creation' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: www.ansible.com, type: CNAME} + register: cmd_result + +- name: 'TYPE=CNAME - creation get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty HostNameAlias" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - creation check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'www.ansible.com.\r\n' + +- name: 'TYPE=CNAME - creation (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: www.ansible.com, type: CNAME} + register: cmd_result + +- name: 'TYPE=CNAME - creation get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty HostNameAlias" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - creation check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'www.ansible.com.\r\n' + + +- name: 'TYPE=CNAME - update address (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: docs.ansible.com, type: CNAME} + register: cmd_result + check_mode: yes + +- name: 'TYPE=CNAME - update address get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty HostNameAlias" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - update address check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'www.ansible.com.\r\n' + +- name: 'TYPE=CNAME - update address' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: docs.ansible.com, type: CNAME} + register: cmd_result + +- name: 'TYPE=CNAME - update address get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty HostNameAlias" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - update address check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'docs.ansible.com.\r\n' + +- name: 'TYPE=CNAME - update address (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: docs.ansible.com, type: CNAME} + register: cmd_result + +- name: 'TYPE=CNAME - update address get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty HostNameAlias" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - update address check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'docs.ansible.com.\r\n' + + +- name: 'TYPE=CNAME - update TTL (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: docs.ansible.com, ttl: 7200, type: CNAME} + register: cmd_result + check_mode: yes + +- name: 'TYPE=CNAME - update TTL get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - update TTL check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: 'TYPE=CNAME - update TTL' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: docs.ansible.com, ttl: 7200, type: CNAME} + register: cmd_result + +- name: 'TYPE=CNAME - update TTL get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - update TTL check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=CNAME - update TTL (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: docs.ansible.com, ttl: 7200, type: CNAME} + register: cmd_result + +- name: 'TYPE=CNAME - update TTL get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - update TTL check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + + +- name: 'TYPE=CNAME - remove record (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, type: CNAME, state: absent} + register: cmd_result + check_mode: yes + +- name: 'TYPE=CNAME - remove record get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - remove record check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: 'TYPE=CNAME - remove record' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, type: CNAME, state: absent} + register: cmd_result + +- name: 'TYPE=CNAME - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - remove record check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=CNAME - remove record (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, type: CNAME, state: absent} + register: cmd_result + +- name: 'TYPE=CNAME - remove record get results (idempotent)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType CNAME -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=CNAME - remove record check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: Issue 429 - creation + win_dns_record: + name: helloworld.test + type: CNAME + value: myserver.example.com + zone: "{{ win_dns_record_zone }}" + register: cmd_result + +- name: Issue 429 - get results + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'helloworld.test' -RRType CNAME -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty HostNameAlias" + register: cmd_result_actual + changed_when: false + +- name: Issue 429 - creation check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'myserver.example.com.\r\n' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-DHCID.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-DHCID.yml new file mode 100644 index 000000000..2c7323ad8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-DHCID.yml @@ -0,0 +1,234 @@ +- name: "TYPE=DHCID - creation (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=" + type: DHCID + register: cmd_result + check_mode: yes + +- name: "TYPE=DHCID - creation get results (check mode)" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - creation check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: "TYPE=DHCID - creation" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=" + type: DHCID + register: cmd_result + +- name: "TYPE=DHCID - creation get results" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DhcId" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - creation check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=\r\n' + +- name: "TYPE=DHCID - creation (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=" + type: DHCID + register: cmd_result + +- name: "TYPE=DHCID - creation get results (idempotent)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DhcId" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - creation check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=\r\n' + +- name: "TYPE=DHCID - update value (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=" + type: DHCID + register: cmd_result + check_mode: yes + +- name: "TYPE=DHCID - update value get results (check mode)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DhcId" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - update value check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=\r\n' + +- name: "TYPE=DHCID - update value" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=" + type: DHCID + register: cmd_result + +- name: "TYPE=DHCID - update value get results" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DhcId" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - update value check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=\r\n' + +- name: "TYPE=DHCID - update value (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=" + type: DHCID + register: cmd_result + +- name: "TYPE=DHCID - update value get results (idempotent)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DhcId" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - update value check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=\r\n' + +- name: "TYPE=DHCID - update TTL (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=" + ttl: 7200 + type: DHCID + register: cmd_result + check_mode: yes + +- name: "TYPE=DHCID - update TTL get results (check mode)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty TimeToLive | Select-Object -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - update TTL check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: "TYPE=DHCID - update TTL" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=" + ttl: 7200 + type: DHCID + register: cmd_result + +- name: "TYPE=DHCID - update TTL get results" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty TimeToLive | Select-Object -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - update TTL check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: "TYPE=DHCID - update TTL (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + value: "AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQdWL3b/NaiUDlW2No=" + ttl: 7200 + type: DHCID + register: cmd_result + +- name: "TYPE=DHCID - update TTL get results (idempotent)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore | Select-Object -ExpandProperty TimeToLive | Select-Object -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - update TTL check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: "TYPE=DHCID - remove record (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + type: DHCID + state: absent + register: cmd_result + check_mode: yes + +- name: "TYPE=DHCID - remove record get results (check mode)" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - remove record check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: "TYPE=DHCID - remove record" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + type: DHCID + state: absent + register: cmd_result + +- name: "TYPE=DHCID - remove record get results" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - remove record check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: "TYPE=DHCID - remove record (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testdhcid + type: DHCID + state: absent + register: cmd_result + +- name: "TYPE=DHCID - remove record get results (idempotent)" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testdhcid' -RRType DHCID -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=DHCID - remove record check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-NS.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-NS.yml new file mode 100644 index 000000000..23a41da9c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-NS.yml @@ -0,0 +1,277 @@ +- name: 'TYPE=NS - creation (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-mirror.example.com, type: NS} + register: cmd_result + check_mode: yes + +- name: 'TYPE=NS - creation get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - creation check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=NS- creation' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-mirror.example.com, type: NS} + register: cmd_result + +- name: 'TYPE=NS - creation get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty NameServer" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - creation check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\n' + +- name: 'TYPE=NS - creation (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-mirror.example.com, type: NS} + register: cmd_result + +- name: 'TYPE=NS - creation get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty NameServer" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - creation check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\n' + + +- name: 'TYPE=NS - update address (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-altmirror.example.com, type: NS} + register: cmd_result + check_mode: yes + +- name: 'TYPE=NS - update address get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty NameServer" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - update address check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\n' + +- name: 'TYPE=NS - update address' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-altmirror.example.com, type: NS} + register: cmd_result + +- name: 'TYPE=NS - update address get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty NameServer" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - update address check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible-altmirror.example.com.\r\n' + +- name: 'TYPE=NS - update address (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-altmirror.example.com, type: NS} + register: cmd_result + +- name: 'TYPE=NS - update address get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty NameServer" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - update address check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'ansible-altmirror.example.com.\r\n' + + +- name: 'TYPE=NS - update TTL (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-altmirror.example.com, ttl: 7200, type: NS} + register: cmd_result + check_mode: yes + +- name: 'TYPE=NS - update TTL get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - update TTL check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: 'TYPE=NS - update TTL' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-altmirror.example.com, ttl: 7200, type: NS} + register: cmd_result + +- name: 'TYPE=NS - update TTL get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - update TTL check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=NS - update TTL (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: ansible-altmirror.example.com, ttl: 7200, type: NS} + register: cmd_result + +- name: 'TYPE=NS - update TTL get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - update TTL check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=NS - remove record (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, type: NS, state: absent} + register: cmd_result + check_mode: yes + +- name: 'TYPE=NS - remove record get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - remove record check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: 'TYPE=NS - remove record' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, type: NS, state: absent} + register: cmd_result + +- name: 'TYPE=NS - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - remove record check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=NS - remove record (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, type: NS, state: absent} + register: cmd_result + +- name: 'TYPE=NS - remove record get results (idempotent)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - remove record check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=NS - creation with multiple values (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: [ansible-mirror.example.com, ansible-altmirror.example.com, ansiblull-mirror.example.com], type: NS} + register: cmd_result + check_mode: yes + +- name: 'TYPE=NS - creation get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - creation check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=NS - creation with multiple values' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: [ansible-mirror.example.com, ansible-altmirror.example.com, ansiblull-mirror.example.com], type: NS} + register: cmd_result + +- name: 'TYPE=NS - creation get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty NameServer" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - creation check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\nansible-altmirror.example.com.\r\nansiblull-mirror.example.com.\r\n' + +- name: 'TYPE=NS - creation with multiple values (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, value: [ansible-mirror.example.com, ansible-altmirror.example.com, ansiblull-mirror.example.com], type: NS} + register: cmd_result + +- name: 'TYPE=NS - creation get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty NameServer" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - creation check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\nansible-altmirror.example.com.\r\nansiblull-mirror.example.com.\r\n' + +- name: 'TYPE=NS - remove record (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, type: NS, state: absent} + register: cmd_result + check_mode: yes + +- name: 'TYPE=NS - remove record get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - remove record check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: 'TYPE=NS - remove record' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, type: NS, state: absent} + register: cmd_result + +- name: 'TYPE=NS - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - remove record check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=NS - remove record (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: z, type: NS, state: absent} + register: cmd_result + +- name: 'TYPE=NS - remove record get results (idempotent)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'z' -RRType NS -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=NS - remove record check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n'
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-PTR.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-PTR.yml new file mode 100644 index 000000000..796846f23 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-PTR.yml @@ -0,0 +1,186 @@ +- name: 'TYPE=PTR - creation (check mode)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-mirror.example.com, type: PTR} + register: cmd_result + check_mode: yes + +- name: 'TYPE=PTR - creation get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - creation check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=PTR - creation' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-mirror.example.com, type: PTR} + register: cmd_result + +- name: 'TYPE=PTR - creation get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty PtrDomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - creation check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\n' + +- name: 'TYPE=PTR - creation (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-mirror.example.com, type: PTR} + register: cmd_result + +- name: 'TYPE=PTR - creation get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty PtrDomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - creation check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\n' + + +- name: 'TYPE=PTR - update address (check mode)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-altmirror.example.com, type: PTR} + register: cmd_result + check_mode: yes + +- name: 'TYPE=PTR - update address get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty PtrDomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - update address check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible-mirror.example.com.\r\n' + +- name: 'TYPE=PTR - update address' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-altmirror.example.com, type: PTR} + register: cmd_result + +- name: 'TYPE=PTR - update address get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty PtrDomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - update address check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible-altmirror.example.com.\r\n' + +- name: 'TYPE=PTR - update address (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-altmirror.example.com, type: PTR} + register: cmd_result + +- name: 'TYPE=PTR - update address get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty PtrDomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - update address check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'ansible-altmirror.example.com.\r\n' + + +- name: 'TYPE=PTR - update TTL (check mode)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-altmirror.example.com, ttl: 7200, type: PTR} + register: cmd_result + check_mode: yes + +- name: 'TYPE=PTR - update TTL get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - update TTL check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: 'TYPE=PTR - update TTL' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-altmirror.example.com, ttl: 7200, type: PTR} + register: cmd_result + +- name: 'TYPE=PTR - update TTL get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - update TTL check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=PTR - update TTL (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, value: ansible-altmirror.example.com, ttl: 7200, type: PTR} + register: cmd_result + +- name: 'TYPE=PTR - update TTL get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - update TTL check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + + +- name: 'TYPE=PTR - remove record (check mode)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, type: PTR, state: absent} + register: cmd_result + check_mode: yes + +- name: 'TYPE=PTR - remove record get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - remove record check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: 'TYPE=PTR - remove record' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, type: PTR, state: absent} + register: cmd_result + +- name: 'TYPE=PTR - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - remove record check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=PTR - remove record (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_revzone }}', name: 7, type: PTR, state: absent} + register: cmd_result + +- name: 'TYPE=PTR - remove record get results (idempotent)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_revzone }}' -Name '7' -RRType PTR -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=PTR - remove record check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-SRV.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-SRV.yml new file mode 100644 index 000000000..bad563565 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-SRV.yml @@ -0,0 +1,321 @@ +- name: 'TYPE=SRV - creation (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: ansible.example.com, weight: 5, priority: 2, port: 755, type: SRV} + register: cmd_result + check_mode: yes + +- name: 'TYPE=SRV - creation get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - creation check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=SRV - creation' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: ansible.example.com, weight: 5, priority: 2, port: 755, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - creation get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty DomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - creation check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible.example.com.\r\n' + +- name: 'TYPE=SRV - creation (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: ansible.example.com, weight: 5, priority: 2, port: 755, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - creation get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty DomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - creation check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'ansible.example.com.\r\n' + +- name: 'TYPE=SRV - update address (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 5, priority: 2, port: 755, type: SRV} + register: cmd_result + check_mode: yes + +- name: 'TYPE=SRV - update address get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty DomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update address check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'ansible.example.com.\r\n' + +- name: 'TYPE=SRV - update address' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 5, priority: 2, port: 755, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update address get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty DomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update address check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'altansible.example.com.\r\n' + +- name: 'TYPE=SRV - update address (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 5, priority: 2, port: 755, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update address get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty DomainName" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update address check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'altansible.example.com.\r\n' + +- name: 'TYPE=SRV - update TTL (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 5, priority: 2, port: 755, ttl: 7200, type: SRV} + register: cmd_result + check_mode: yes + +- name: 'TYPE=SRV - update TTL get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update TTL check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: 'TYPE=SRV - update TTL' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 5, priority: 2, port: 755, ttl: 7200, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update TTL get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update TTL check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=SRV - update TTL (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 5, priority: 2, port: 755, ttl: 7200, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update TTL get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty TimeToLive | Select -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update TTL check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: 'TYPE=SRV - update weight (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 1, priority: 2, port: 755, type: SRV} + register: cmd_result + check_mode: yes + +- name: 'TYPE=SRV - update weight get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Weight" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update weight check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '5\r\n' + +- name: 'TYPE=SRV - update weight' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, weight: 1, priority: 2, port: 755, value: altansible.example.com, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update weight get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Weight" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update weight check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '1\r\n' + +- name: 'TYPE=SRV - update weight (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 1, priority: 2, port: 755, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update weight get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Weight" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update weight check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '1\r\n' + +- name: 'TYPE=SRV - update port (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 1, priority: 2, port: 355, type: SRV} + register: cmd_result + check_mode: yes + +- name: 'TYPE=SRV - update port get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Port" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update port check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '755\r\n' + +- name: 'TYPE=SRV - update port' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, weight: 1, priority: 2, port: 355, value: altansible.example.com, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update port get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Port" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update port check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '355\r\n' + +- name: 'TYPE=SRV - update port (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 1, priority: 2, port: 355, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update port get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Port" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update port check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '355\r\n' + +- name: 'TYPE=SRV - update priority (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 1, priority: 7, port: 355, type: SRV} + register: cmd_result + check_mode: yes + +- name: 'TYPE=SRV - update priority get results (check mode)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Priority" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update priority check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '2\r\n' + +- name: 'TYPE=SRV - update priority' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, weight: 1, priority: 7, port: 355, value: altansible.example.com, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update priority get results' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Priority" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update priority check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7\r\n' + +- name: 'TYPE=SRV - update priority (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, value: altansible.example.com, weight: 1, priority: 7, port: 355, type: SRV} + register: cmd_result + +- name: 'TYPE=SRV - update priority get results (idempotent)' + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty Priority" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - update priority check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7\r\n' + +- name: 'TYPE=SRV - remove record (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, weight: 1, priority: 7, port: 355, type: SRV, state: absent} + register: cmd_result + check_mode: yes + +- name: 'TYPE=SRV - remove record get results (check mode)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - remove record check results (check mode)' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: 'TYPE=SRV - remove record' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, weight: 1, priority: 7, port: 355, type: SRV, state: absent} + register: cmd_result + +- name: 'TYPE=SRV - remove record get results' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - remove record check results' + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: 'TYPE=SRV - remove record (idempotent)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: test1, weight: 1, priority: 7, port: 355, type: SRV, state: absent} + register: cmd_result + +- name: 'TYPE=SRV - remove record get results (idempotent)' + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'test1' -RRType SRV -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: 'TYPE=SRV - remove record check results (idempotent)' + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n'
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-TXT.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-TXT.yml new file mode 100644 index 000000000..9ca6bb7fa --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-TXT.yml @@ -0,0 +1,234 @@ +- name: "TYPE=TXT - creation (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: txtrecordvalue + type: TXT + register: cmd_result + check_mode: yes + +- name: "TYPE=TXT - creation get results (check mode)" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - creation check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: "TYPE=TXT - creation" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: txtrecordvalue + type: TXT + register: cmd_result + +- name: "TYPE=TXT - creation get results" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty DescriptiveText" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - creation check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'txtrecordvalue\r\n' + +- name: "TYPE=TXT - creation (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: txtrecordvalue + type: TXT + register: cmd_result + +- name: "TYPE=TXT - creation get results (idempotent)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select -ExpandProperty RecordData | Select -ExpandProperty DescriptiveText" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - creation check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'txtrecordvalue\r\n' + +- name: "TYPE=TXT - update value (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: updated txt record value + type: TXT + register: cmd_result + check_mode: yes + +- name: "TYPE=TXT - update value get results (check mode)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DescriptiveText" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - update value check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'txtrecordvalue\r\n' + +- name: "TYPE=TXT - update value" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: updated txt record value + type: TXT + register: cmd_result + +- name: "TYPE=TXT - update value get results" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DescriptiveText" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - update value check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'updated txt record value\r\n' + +- name: "TYPE=TXT - update value (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: updated txt record value + type: TXT + register: cmd_result + +- name: "TYPE=TXT - update value get results (idempotent)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select-Object -ExpandProperty RecordData | Select-Object -ExpandProperty DescriptiveText" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - update value check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'updated txt record value\r\n' + +- name: "TYPE=TXT - update TTL (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: updated txt record value + ttl: 7200 + type: TXT + register: cmd_result + check_mode: true + +- name: "TYPE=TXT - update TTL get results (check mode)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select-Object -ExpandProperty TimeToLive | Select-Object -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - update TTL check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '3600\r\n' + +- name: "TYPE=TXT - update TTL" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: updated txt record value + ttl: 7200 + type: TXT + register: cmd_result + +- name: "TYPE=TXT - update TTL get results" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select-Object -ExpandProperty TimeToLive | Select-Object -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - update TTL check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: "TYPE=TXT - update TTL (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + value: updated txt record value + ttl: 7200 + type: TXT + register: cmd_result + +- name: "TYPE=TXT - update TTL get results (idempotent)" + ansible.windows.win_command: powershell.exe "Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore | Select-Object -ExpandProperty TimeToLive | Select-Object -ExpandProperty TotalSeconds" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - update TTL check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == '7200\r\n' + +- name: "TYPE=TXT - remove record (check mode)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + type: TXT + state: absent + register: cmd_result + check_mode: yes + +- name: "TYPE=TXT - remove record get results (check mode)" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - remove record check results (check mode)" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'exists\r\n' + +- name: "TYPE=TXT - remove record" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + type: TXT + state: absent + register: cmd_result + +- name: "TYPE=TXT - remove record get results" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - remove record check results" + assert: + that: + - cmd_result is changed + - cmd_result_actual.stdout == 'absent\r\n' + +- name: "TYPE=TXT - remove record (idempotent)" + win_dns_record: + zone: "{{ win_dns_record_zone }}" + name: testtxt + type: TXT + state: absent + register: cmd_result + +- name: "TYPE=TXT - remove record get results (idempotent)" + ansible.windows.win_command: powershell.exe "If (Get-DnsServerResourceRecord -ZoneName '{{ win_dns_record_zone }}' -Name 'testtxt' -RRType TXT -Node -ErrorAction:Ignore) { 'exists' } else { 'absent' }" + register: cmd_result_actual + changed_when: false + +- name: "TYPE=TXT - remove record check results (idempotent)" + assert: + that: + - cmd_result is not changed + - cmd_result_actual.stdout == 'absent\r\n' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-diff.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-diff.yml new file mode 100644 index 000000000..f5adaf369 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests-diff.yml @@ -0,0 +1,63 @@ +# Diff tests are present because those records have to be created MANUALLY by +# the win_dns_record module when in check mode, as there is otherwise no way in +# Windows DNS to *simulate* a record or change. + + +- name: 'Diff test - creation (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: diff_host, value: 1.2.3.4, type: A} + register: create_check + check_mode: yes + diff: yes + +- name: 'Diff test - creation' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: diff_host, value: 1.2.3.4, type: A} + register: create_do + diff: yes + +- name: 'Diff test - creation check results' + assert: + that: + - create_check.diff.before == create_do.diff.before + - create_check.diff.before == '' + - create_check.diff.after == create_do.diff.after + - create_check.diff.after == "[{{ win_dns_record_zone }}] diff_host 3600 IN A 1.2.3.4\n" + + +- name: 'Diff test - update TTL (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: diff_host, value: 1.2.3.4, type: A, ttl: 7200} + register: update_check + check_mode: yes + diff: yes + +- name: 'Diff test - update TTL' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: diff_host, value: 1.2.3.4, type: A, ttl: 7200} + register: update_do + diff: yes + +- name: 'Diff test - update TTL check results' + assert: + that: + - update_check.diff.before == update_do.diff.before + - update_check.diff.before == "[{{ win_dns_record_zone }}] diff_host 3600 IN A 1.2.3.4\n" + - update_check.diff.after == update_do.diff.after + - update_check.diff.after == "[{{ win_dns_record_zone }}] diff_host 7200 IN A 1.2.3.4\n" + + +- name: 'Diff test - deletion (check mode)' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: diff_host, type: A, state: absent} + register: delete_check + check_mode: yes + diff: yes + +- name: 'Diff test - deletion' + win_dns_record: {zone: '{{ win_dns_record_zone }}', name: diff_host, type: A, state: absent} + register: delete_do + diff: yes + +- name: 'Diff test - deletion check results' + assert: + that: + - delete_check.diff.before == delete_do.diff.before + - delete_check.diff.before == "[{{ win_dns_record_zone }}] diff_host 7200 IN A 1.2.3.4\n" + - delete_check.diff.after == delete_do.diff.after + - delete_check.diff.after == '' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests.yml new file mode 100644 index 000000000..bdf40799b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_record/tasks/tests.yml @@ -0,0 +1,36 @@ +- name: ensure DNS services are installed + ansible.windows.win_feature: + name: DNS + state: present + register: dns_install + +- name: reboot server if needed + ansible.windows.win_reboot: + when: dns_install.reboot_required + +- name: Clean slate + import_tasks: clean.yml + vars: + fail_on_missing: false + +- block: + - name: Create the forward zone + ansible.windows.win_shell: Add-DnsServerPrimaryZone -Name '{{ win_dns_record_zone }}' -ZoneFile '{{ win_dns_record_zone}}.dns' + - name: Create the reverse zone + ansible.windows.win_shell: Add-DnsServerPrimaryZone -NetworkID '{{ win_dns_record_revzone_network }}' -ZoneFile '{{ win_dns_record_revzone}}.dns' + + - import_tasks: tests-A.yml + - import_tasks: tests-AAAA.yml + - import_tasks: tests-NS.yml + - import_tasks: tests-SRV.yml + - import_tasks: tests-CNAME.yml + - import_tasks: tests-DHCID.yml + - import_tasks: tests-PTR.yml + - import_tasks: tests-TXT.yml + - import_tasks: tests-diff.yml + + always: + - name: Clean slate + import_tasks: clean.yml + vars: + fail_on_missing: true diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/aliases b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/aliases new file mode 100644 index 000000000..df7c2d121 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/aliases @@ -0,0 +1,2 @@ +shippable/windows/group1 +skip/windows/2012
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/defaults/main.yml new file mode 100644 index 000000000..73afd8e1b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# Defines the test to run, possible values: 'standalone' or 'activedirectory' +# 'standalone' installs a mock Windows DNS server and runs tests that only +# include file backed DNS zones. +# 'activedirectory' installs a mock AD/DNS server and runs tests that include +# AD integrated and file backed DNS zones. +win_dns_zone_test_type: standalone
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/activedirectory.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/activedirectory.yml new file mode 100644 index 000000000..fa75cf59e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/activedirectory.yml @@ -0,0 +1,306 @@ +--- +- name: Ensure AD/DNS roles are installed + ansible.windows.win_feature: + name: + - AD-Domain-Services + - DNS + include_management_tools: true + include_sub_features: true + state: present + +- name: Ensure domain is present + ansible.windows.win_domain: + dns_domain_name: ansible.test + safe_mode_password: password123! + +- name: Reboot + ansible.windows.win_reboot: + +- name: Ensure loopback address is set to DNS + ansible.windows.win_dns_client: + adapter_names: '*' + ipv4_addresses: 127.0.0.1 + +- name: Reboot Again to Avoid DNS Bug + ansible.windows.win_reboot: + +- name: Ensure DNS zones are absent + win_dns_zone: + name: "{{ item }}" + state: absent + loop: + - jamals.euc.vmware.com + - dgemzer.euc.vmware.com + - wpinner.euc.vmware.com + - marshallb.euc.vmware.com + - basavaraju.euc.vmware.com + - virajp.euc.vmware.com + +- name: Ensure primary DNS zone is present + win_dns_zone: + name: wpinner.euc.vmware.com + replication: domain + type: primary + state: present + register: test1a + failed_when: test1a.changed == false + +- name: Ensure primary DNS zone is present (idempotence check) + win_dns_zone: + name: wpinner.euc.vmware.com + replication: domain + type: primary + state: present + register: test1 + failed_when: test1 is changed + +- name: Ensure Active Directory integrated primary zone is present with secure updates + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + replication: forest + dynamic_update: secure + register: test9a + failed_when: test9a.changed == false + +- name: Ensure Active Directory integrated primary zone is present with secure updates (idempotence check) + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + replication: forest + dynamic_update: secure + register: test9 + failed_when: test9 is changed + +- name: Ensure primary DNS zone is present, change replication + win_dns_zone: + name: wpinner.euc.vmware.com + replication: forest + type: primary + state: present + register: test2a + failed_when: test2a.changed == false + +- name: Ensure primary DNS zone is present, change replication (idempotence check) + win_dns_zone: + name: wpinner.euc.vmware.com + replication: forest + type: primary + state: present + register: test2 + failed_when: test2 is changed + +- name: Ensure DNS zone is absent + win_dns_zone: + name: wpinner.euc.vmware.com + state: absent + register: test3a + failed_when: test3a.changed == false + +- name: Ensure DNS zone is absent (idempotence check) + win_dns_zone: + name: wpinner.euc.vmware.com + state: absent + register: test3 + failed_when: test3 is changed + +- name: Ensure forwarder has specific DNS servers + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + replication: forest + forwarder_timeout: 2 + dns_servers: + - 10.245.51.100 + - 10.245.51.101 + - 10.245.51.102 + register: test4a + failed_when: test4a.changed == false + +- name: Ensure forwarder has specific DNS servers (idempotence check) + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + dns_servers: + - 10.245.51.100 + - 10.245.51.101 + - 10.245.51.102 + register: test4 + failed_when: test4 is changed + +- name: Ensure stub zone is configured + win_dns_zone: + name: dgemzer.euc.vmware.com + type: stub + replication: none + dns_servers: + - 10.19.20.1 + - 10.19.20.2 + register: test5a + failed_when: test5a.changed == false + +- name: Ensure stub zone is configured (idempotence check) + win_dns_zone: + name: dgemzer.euc.vmware.com + type: stub + replication: none + dns_servers: + - 10.19.20.1 + - 10.19.20.2 + register: test5 + failed_when: test5 is changed + +- name: Ensure forwarder zone has updated DNS servers + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + dns_servers: + - 10.10.1.150 + - 10.10.1.151 + register: test6a + failed_when: test6a.changed == false + +- name: Ensure forwarder zone has updated DNS servers (idempotence check) + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + dns_servers: + - 10.10.1.150 + - 10.10.1.151 + register: test6 + failed_when: test6 is changed + +- name: Ensure Active Directory integrated secondary zone is present + win_dns_zone: + name: virajp.euc.vmware.com + type: primary + replication: forest + dynamic_update: none + register: test7a + failed_when: test7a.changed == false + +- name: Ensure Active Directory integrated secondary zone is present (idempotence check) + win_dns_zone: + name: virajp.euc.vmware.com + type: primary + replication: forest + dynamic_update: none + register: test7 + failed_when: test7 is changed + +- name: Ensure file backed primary zone is present + win_dns_zone: + name: marshallb.euc.vmware.com + type: primary + replication: none + register: test8a + failed_when: test8a.changed == false + +- name: Ensure file backed primary zone is present (idempotence check) + win_dns_zone: + name: marshallb.euc.vmware.com + type: primary + replication: none + register: test8 + failed_when: test8 is changed + +- name: Ensure Active Directory integrated dynamic updates set to nonsecureandsecure + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + dynamic_update: nonsecureandsecure + register: test10a + failed_when: test10a.changed == false + +- name: Ensure file backed primary zone has dynamic updates set to nonsecureandsecure (idempotence check) + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + dynamic_update: nonsecureandsecure + register: test10 + failed_when: test10 is changed + +- name: Ensure zone has dynamic update set to secure and replication set to domain + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + dynamic_update: secure + replication: domain + register: test11a + failed_when: test11a.changed == false + +- name: Ensure zone has dynamic update set to secure and replication set to domain (idempotence check) + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + dynamic_update: secure + replication: domain + register: test11 + failed_when: test11 is changed + +- name: Ensure primary DNS zones are present (check mode) + win_dns_zone: + name: mehmoodkhap.euc.vmware.com + replication: domain + type: primary + check_mode: true + register: cm_test1 + failed_when: cm_test1 is changed + +- name: Ensure primary DNS zones replicate to forest (check mode) + win_dns_zone: + name: chall.euc.vmware.com + replication: forest + type: primary + check_mode: true + register: cm_test2 + failed_when: cm_test2 is changed + +- name: Ensure forwarder is present (check mode) + win_dns_zone: + name: nkini.euc.vmware.com + type: forwarder + dns_servers: + - 10.245.51.100 + - 10.245.51.101 + - 10.245.51.102 + check_mode: true + register: cm_test3 + failed_when: cm_test3 is changed + +- name: Ensure forwarder zone has specific DNS servers (check mode) + win_dns_zone: + name: ssanthanagopalan.euc.vmware.com + type: forwarder + dns_servers: + - 10.205.1.219 + - 10.205.1.220 + check_mode: true + register: cm_test4 + failed_when: cm_test4 is changed + +- name: Ensure Active Directory integrated secondary zone is present (check mode) + win_dns_zone: + name: rrounsaville.euc.vmware.com + type: secondary + dns_servers: + - 10.205.1.219 + - 10.205.1.220 + replication: forest + dynamic_update: none + check_mode: true + register: cm_test5 + failed_when: cm_test5 is changed + +- name: Ensure file backed stub zone is present (check mode) + win_dns_zone: + name: anup.euc.vmware.com + type: stub + dns_servers: + - 10.205.1.219 + - 10.205.1.220 + replication: none + dynamic_update: none + check_mode: true + register: cm_test6 + failed_when: cm_test6 is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/main.yml new file mode 100644 index 000000000..e9bebdfcb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/main.yml @@ -0,0 +1,3 @@ +--- +- name: Run Defined Test Flow + include_tasks: "{{ win_dns_zone_test_type }}.yml" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/standalone.yml b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/standalone.yml new file mode 100644 index 000000000..ed52cb6d4 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dns_zone/tasks/standalone.yml @@ -0,0 +1,251 @@ +--- +- name: Ensure AD/DNS roles are installed + ansible.windows.win_feature: + name: + - DNS + include_management_tools: true + include_sub_features: true + state: present + register: ensure_addns_roles + +- name: Ensure loopback address is set to DNS + ansible.windows.win_dns_client: + adapter_names: '*' + ipv4_addresses: 127.0.0.1 + +- name: Reboot + ansible.windows.win_reboot: + when: ensure_addns_roles.reboot_required + +- name: Ensure DNS zones are absent + win_dns_zone: + name: "{{ item }}" + state: absent + loop: + - jamals.euc.vmware.com + - dgemzer.euc.vmware.com + - wpinner.euc.vmware.com + - marshallb.euc.vmware.com + - basavaraju.euc.vmware.com + - virajp.euc.vmware.com + +- name: Ensure file-backed primary DNS zone is present + win_dns_zone: + name: wpinner.euc.vmware.com + replication: none + type: primary + state: present + register: test1a + failed_when: test1a.changed == false + +- name: Ensure file-backed primary DNS zone is present (idempotence check) + win_dns_zone: + name: wpinner.euc.vmware.com + replication: none + type: primary + state: present + register: test1 + failed_when: test1 is changed + +- name: Ensure file-backed primary zone is present with secure updates, generates warning + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + replication: none + dynamic_update: secure + register: test2a + failed_when: test2a.changed == false + +- name: Ensure file-backed primary zone is present with secure updates, generates warning (idempotence check) + win_dns_zone: + name: basavaraju.euc.vmware.com + type: primary + replication: none + dynamic_update: secure + register: test2 + failed_when: test2 is changed + +- name: Ensure DNS zone is absent + win_dns_zone: + name: wpinner.euc.vmware.com + state: absent + register: test3a + failed_when: test3a.changed == false + +- name: Ensure DNS zone is absent (idempotence check) + win_dns_zone: + name: wpinner.euc.vmware.com + state: absent + register: test3 + failed_when: test3 is changed + +- name: Ensure file-backed forwarder has specific DNS servers + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + replication: none + forwarder_timeout: 2 + dns_servers: + - 10.245.51.100 + - 10.245.51.101 + - 10.245.51.102 + register: test4a + failed_when: test4a.changed == false + +- name: Ensure file-backed forwarder has specific DNS servers (idempotence check) + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + replication: none + dns_servers: + - 10.245.51.100 + - 10.245.51.101 + - 10.245.51.102 + register: test4 + failed_when: test4 is changed + +- name: Ensure file-backed stub zone is configured + win_dns_zone: + name: dgemzer.euc.vmware.com + type: stub + replication: none + dns_servers: + - 10.19.20.1 + - 10.19.20.2 + register: test5a + failed_when: test5a.changed == false + +- name: Ensure file-backed stub zone is configured (idempotence check) + win_dns_zone: + name: dgemzer.euc.vmware.com + type: stub + replication: none + dns_servers: + - 10.19.20.1 + - 10.19.20.2 + register: test5 + failed_when: test5 is changed + +- name: Ensure file-backed forwarder zone has updated DNS servers + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + replication: none + dns_servers: + - 10.10.1.150 + - 10.10.1.151 + register: test6a + failed_when: test6a.changed == false + +- name: Ensure file-backed forwarder zone has updated DNS servers (idempotence check) + win_dns_zone: + name: jamals.euc.vmware.com + type: forwarder + replication: none + dns_servers: + - 10.10.1.150 + - 10.10.1.151 + register: test6 + failed_when: test6 is changed + +- name: Ensure file backed primary zone is present + win_dns_zone: + name: marshallb.euc.vmware.com + type: primary + replication: none + register: test7a + failed_when: test7a.changed == false + +- name: Ensure file backed primary zone is present (idempotence check) + win_dns_zone: + name: marshallb.euc.vmware.com + type: primary + replication: none + register: test7 + failed_when: test7 is changed + +- name: Ensure file backed integrated dynamic updates set to none + win_dns_zone: + name: virajp.euc.vmware.com + type: primary + replication: none + dynamic_update: none + register: test8a + failed_when: test8a.changed == false + +- name: Ensure file backed primary zone has dynamic updates set to none (idempotence check) + win_dns_zone: + name: virajp.euc.vmware.com + type: primary + replication: none + dynamic_update: none + register: test8 + failed_when: test8 is changed + +- name: Start Check Mode Tests + block: + - name: Ensure primary DNS zones are present (check mode) + win_dns_zone: + name: mehmoodkhap.euc.vmware.com + replication: none + type: primary + dynamic_update: none + register: cm_test1 + failed_when: cm_test1 is changed + + - name: Ensure file-backed primary DNS zone is present (check mode) + win_dns_zone: + name: chall.euc.vmware.com + replication: none + type: primary + + register: cm_test2 + failed_when: cm_test2 is changed + + - name: Ensure file-backed forwarder is present (check mode) + win_dns_zone: + name: nkini.euc.vmware.com + replication: none + type: forwarder + dns_servers: + - 10.245.51.100 + - 10.245.51.101 + - 10.245.51.102 + register: cm_test3 + failed_when: cm_test3 is changed + + - name: Ensure file-backed forwarder zone has specific DNS servers (check mode) + win_dns_zone: + name: ssanthanagopalan.euc.vmware.com + replication: none + type: forwarder + dns_servers: + - 10.205.1.219 + - 10.205.1.220 + register: cm_test4 + failed_when: test4 is changed + + - name: Ensure file-backed integrated secondary zone is present (check mode) + win_dns_zone: + name: rrounsaville.euc.vmware.com + type: secondary + dns_servers: + - 10.205.1.219 + - 10.205.1.220 + replication: none + dynamic_update: none + register: cm_test5 + failed_when: cm_test5 is changed + + - name: Ensure file-backed stub zone is present (check mode) + win_dns_zone: + name: anup.euc.vmware.com + type: stub + dns_servers: + - 10.205.1.219 + - 10.205.1.220 + replication: none + dynamic_update: none + register: cm_test6 + failed_when: cm_test6 is changed + check_mode: true diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_computer/aliases b/ansible_collections/community/windows/tests/integration/targets/win_domain_computer/aliases new file mode 100644 index 000000000..ad7ccf7ad --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_computer/aliases @@ -0,0 +1 @@ +unsupported diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_computer/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_computer/tasks/main.yml new file mode 100644 index 000000000..b8fe35022 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_computer/tasks/main.yml @@ -0,0 +1,478 @@ +# this won't run in Ansible's integration tests until we get a domain set up +# these are here if someone wants to run the module tests locally on their own +# domain. +# Requirements: +# Set the names in vars: on the command line, or set the following: +# test_domain_name: The DNS name of the domain like ansible.local +# test_ad_domain_dn: The DN of the domain like DC=ansible,DC=local +# test_ad_computer_ou: The DN of the OU where computers will be created like OU=ou1,DC=ansible,DC=local +# +# This is not a traditional role, and can't be used with ansible-test. This is a playbook. To run ensure: +# - your collections are set up and Ansible knows where to find them ($ANSIBLE_COLLECTIONS_PATHS for example) +# - your inventory contains a host where this can run, like a domain controller +# - connection keywords/options/vars are set properly to connect to the host +# - the variable win_domain_computer_testing_host contains the name of the host or the group that contains it +# +# then call this file with ansible-playbook and any extra vars or other params you need +--- +- name: run win_domain_users test + hosts: "{{ win_domain_computer_testing_host }}" + gather_facts: no + collections: + - community.windows + vars: + test_win_domain_computer_ldap_base: "{{ test_ad_domain_dn }}" + test_win_domain_computer_ou_path: "{{ test_ad_computer_ou }}" + test_win_domain_computer_name: "test_computer" + test_win_domain_domain_name: "{{ test_domain_name }}" + test_win_domain_computer_dns_hostname: "{{ test_win_domain_computer_name }}.{{ test_domain_name }}" + test_win_domain_computer_description: "{{ test_computer_description | default('description') }}" + tasks: + + - name: ensure the computer is deleted before the test + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + state: absent + tags: always + + # -------------------------------------------------------------------------- + + - name: Test computer with long name and distinct sam_account_name + tags: + - long_name + vars: + test_win_domain_computer_long_name: '{{ test_win_domain_computer_name }}_with_long_name' + test_win_domain_computer_sam_account_name: '{{ test_win_domain_computer_name }}$' + test_win_domain_computer_dns_hostname: "{{ test_win_domain_computer_long_name }}.{{ test_domain_name }}" + block: + + # ---------------------------------------------------------------------- + - name: create computer with long name and distinct sam_account_name (check mode) + win_domain_computer: + name: '{{ test_win_domain_computer_long_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + enabled: yes + state: present + register: create_distinct_sam_account_name + check_mode: yes + + - name: assert create computer with long name and distinct sam_account_name (check mode) + assert: + that: + - create_distinct_sam_account_name is changed + + - name: create computer with long name and distinct sam_account_name + win_domain_computer: + name: '{{ test_win_domain_computer_long_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + enabled: yes + state: present + register: create_distinct_sam_account_name + + - name: get actual computer with long name and distinct sam_account_name + ansible.windows.win_shell: | + Import-Module ActiveDirectory + $c = Get-ADComputer -Identity '{{ test_win_domain_computer_sam_account_name }}' -ErrorAction Stop + if ($c.Name -ne '{{ test_win_domain_computer_long_name }}') { + throw 'Wrong computer name in relation to sAMAccountName' + } + register: create_distinct_sam_account_name_check + + - name: (Idempotence) create computer with long name and distinct sam_account_name + win_domain_computer: + name: '{{ test_win_domain_computer_long_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + enabled: yes + state: present + register: create_distinct_sam_account_name_idempotence + check_mode: yes + + - name: (Idempotence) assert create computer with long name and distinct sam_account_name + assert: + that: + - create_distinct_sam_account_name_idempotence is not changed + + always: + - name: ensure the test computer is deleted after the test + win_domain_computer: + name: '{{ test_win_domain_computer_long_name }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + state: absent + + # ---------------------------------------------------------------------- + + - name: Test offline domain join + tags: + - djoin + vars: + test_win_domain_computer_sam_account_name: '{{ test_win_domain_computer_name }}$' + block: + - name: No file with blob return + block: + - name: Create computer with offline domain join and blob return (check mode) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: output + register: odj_result + check_mode: yes + + - name: assert odj (check mode) + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob == '' + - odj_result.odj_blob_path is not defined + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + + - name: Create computer with offline domain join and blob return + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: output + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob != '' + - odj_result.odj_blob_path is not defined + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + - odj_result.djoin.rc is defined + - odj_result.djoin.rc == 0 + - odj_result.djoin.stdout is defined + - odj_result.djoin.stderr is defined + + - name: Create computer with offline domain join and blob return (idempotence) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: output + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is not changed + - odj_result.odj_blob is not defined + - odj_result.odj_blob_path is not defined + - odj_result.djoin is not defined + + always: + - name: ensure the test computer is deleted after the test + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + state: absent + + + - name: File and blob return + vars: + blob_path: 'C:\Windows\Temp\blob.txt' + block: + - name: Create computer with offline domain join and blob file with return (check mode) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: output + odj_blob_path: "{{ blob_path }}" + register: odj_result + check_mode: yes + + - name: assert odj (check mode) + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob == '' + - odj_result.odj_blob_path is not defined + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + + - name: Create computer with offline domain join and blob file with return + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: output + odj_blob_path: "{{ blob_path }}" + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob != '' + - odj_result.odj_blob_path is not defined + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + - odj_result.djoin.rc is defined + - odj_result.djoin.rc == 0 + - odj_result.djoin.stdout is defined + - odj_result.djoin.stderr is defined + + - name: Create computer with offline domain join and blob file with return (idempotence) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: output + odj_blob_path: "{{ blob_path }}" + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is not changed + - odj_result.odj_blob is not defined + - odj_result.odj_blob_path is not defined + - odj_result.djoin is not defined + + always: + - name: ensure the test computer is deleted after the test + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + state: absent + + - name: ensure the blob file is deleted + win_shell: | + Remove-Item -LiteralPath '{{ blob_path }}' -Force -ErrorAction SilentlyContinue + exit 0 + + - name: Specified file return + vars: + blob_path: 'C:\Windows\Temp\blob.txt' + block: + - name: Create computer with offline domain join and blob file return with specified path (check mode) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: path + odj_blob_path: "{{ blob_path }}" + register: odj_result + check_mode: yes + + - name: assert odj (check mode) + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob == '' + - odj_result.odj_blob_path is defined + - odj_result.odj_blob_path == blob_path + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + + - name: Create computer with offline domain join and blob file return with specified path + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: path + odj_blob_path: "{{ blob_path }}" + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob == '' + - odj_result.odj_blob_path is defined + - odj_result.odj_blob_path == blob_path + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + - odj_result.djoin.rc is defined + - odj_result.djoin.rc == 0 + - odj_result.djoin.stdout is defined + - odj_result.djoin.stderr is defined + + - name: Test ODJ File + ansible.windows.win_shell: | + $ErrorActionPreference = 'Stop' + $file = '{{ odj_result.odj_blob_path }}' + $content = Get-Content -LiteralPath $file -Raw -Encoding Unicode + $trimmed = $content.TrimEnd("`0") + if ($content.Length -eq $trimmed.Length) { throw 'No terminating null found' } + # try a base64 decode to validate it is the kind of data we expect + $bytes = [Convert]::FromBase64String($trimmed) + + - name: Create computer with offline domain join and blob file return with specified path (idempotence) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: path + odj_blob_path: "{{ blob_path }}" + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is not changed + - odj_result.odj_blob is not defined + - odj_result.odj_blob_path is not defined + - odj_result.djoin is not defined + + always: + - name: ensure the test computer is deleted after the test + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + state: absent + + - name: ensure the blob file is deleted + win_shell: | + Remove-Item -LiteralPath '{{ blob_path }}' -Force -ErrorAction SilentlyContinue + exit 0 + + - name: Random file return + block: + - name: Create computer with offline domain join and random blob file return (check mode) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: path + register: odj_result + check_mode: yes + + - name: assert odj (check mode) + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob == '' + - odj_result.odj_blob_path is defined + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + + - name: Create computer with offline domain join and random blob file return + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: path + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is changed + - odj_result.odj_blob is defined + - odj_result.odj_blob == '' + - odj_result.odj_blob_path is defined + - odj_result.djoin is defined + - odj_result.djoin.invocation is defined + - odj_result.djoin.rc is defined + - odj_result.djoin.rc == 0 + - odj_result.djoin.stdout is defined + - odj_result.djoin.stderr is defined + + - name: This file needs to be deleted later + set_fact: + returned_file: "{{ odj_result.odj_blob_path }}" + + - name: Test ODJ File + ansible.windows.win_shell: | + $ErrorActionPreference = 'Stop' + $file = '{{ odj_result.odj_blob_path }}' + $content = Get-Content -LiteralPath $file -Raw -Encoding Unicode + $trimmed = $content.TrimEnd("`0") + if ($content.Length -eq $trimmed.Length) { throw 'No terminating null found' } + # try a base64 decode to validate it is the kind of data we expect + $bytes = [Convert]::FromBase64String($trimmed) + + - name: Create computer with offline domain join and random blob file return (idempotence) + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + dns_hostname: '{{ test_win_domain_computer_dns_hostname }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + ou: "{{ test_win_domain_computer_ou_path }}" + description: "{{ test_computer_description }}" + enabled: yes + state: present + offline_domain_join: path + register: odj_result + + - name: assert odj + assert: + that: + - odj_result is not changed + - odj_result.odj_blob is not defined + - odj_result.odj_blob_path is not defined + - odj_result.djoin is not defined + + always: + - name: ensure the test computer is deleted after the test + win_domain_computer: + name: '{{ test_win_domain_computer_name }}' + sam_account_name: '{{ test_win_domain_computer_sam_account_name }}' + state: absent + + - name: ensure the blob file is deleted + win_shell: | + Remove-Item -LiteralPath '{{ returned_file }}' -Force -ErrorAction SilentlyContinue + exit 0 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_group/aliases b/ansible_collections/community/windows/tests/integration/targets/win_domain_group/aliases new file mode 100644 index 000000000..ad7ccf7ad --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_group/aliases @@ -0,0 +1 @@ +unsupported diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_group/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_group/defaults/main.yml new file mode 100644 index 000000000..b02643ee0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_group/defaults/main.yml @@ -0,0 +1,3 @@ +test_win_domain_group_ldap_base: DC=ansible,DC=local +test_win_domain_group_ou_path: OU=ou1,DC=ansible,DC=local +test_win_domain_group_name: Moo Cow diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_group/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_group/tasks/main.yml new file mode 100644 index 000000000..1624928ea --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_group/tasks/main.yml @@ -0,0 +1,353 @@ +# this won't run in Ansible's integration tests until we get a domain set up +# these are here if someone wants to run the module tests locally on their own +# domain. +# Requirements: +# LDAP Base path set in defaults/main.yml like DC=ansible,DC=local +# Custom OU path set in defaults/main.yml like OU=ou1,DC=ansible,DC=local +--- +- name: ensure the test group is deleted before the test + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: absent + ignore_protection: True + +- name: fail pass in an invalid path + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + organizational_unit: OU=fakeou,{{test_win_domain_group_ldap_base}} + register: fail_invalid_path + failed_when: fail_invalid_path.msg != 'the group path OU=fakeou,' + test_win_domain_group_ldap_base + ' does not exist, please specify a valid LDAP path' + +- name: create group with defaults check + win_domain_group: + name: '{{test_win_domain_group_name}}' + scope: global + state: present + register: create_default_check + check_mode: yes + +- name: get actual group with defaults check + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'" + register: create_default_actual_check + ignore_errors: True + +- name: assert create group with defaults checl + assert: + that: + - create_default_check is changed + - create_default_actual_check.rc == 1 + +- name: create group with defaults + win_domain_group: + name: '{{test_win_domain_group_name}}' + scope: global + state: present + register: create_default + +- name: get actual group with defaults + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'" + register: create_default_actual + +- name: assert create group with defaults + assert: + that: + - create_default is created + - create_default is changed + - create_default.category == 'Security' + - create_default.description == None + - create_default.display_name == None + - create_default.distinguished_name == 'CN=' + test_win_domain_group_name + ',CN=Users,' + test_win_domain_group_ldap_base + - create_default.group_scope == 'Global' + - create_default.guid is defined + - create_default.managed_by == None + - create_default.name == test_win_domain_group_name + - create_default.protected_from_accidental_deletion == False + - create_default.sid is defined + - create_default_actual.rc == 0 + +- name: create group with defaults again + win_domain_group: + name: '{{test_win_domain_group_name}}' + scope: global + state: present + register: create_default_again + +- name: assert create group with defaults again + assert: + that: + - create_default_again is not changed + - create_default_again is not created + +- name: remove group check + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: absent + register: remove_group_check + check_mode: yes + +- name: get actual remove group check + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'" + register: remove_group_actual_check + +- name: assert remove group check + assert: + that: + - remove_group_check is changed + - remove_group_actual_check.rc == 0 + +- name: remove group + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: absent + register: remove_group + +- name: get actual remove group + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'" + register: remove_group_actual + ignore_errors: True + +- name: assert remove group + assert: + that: + - remove_group is changed + - remove_group is not created + - remove_group_actual.rc == 1 + +- name: remove group again + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: absent + register: remove_group_again + +- name: assert remove group again + assert: + that: + - remove_group_again is not changed + - remove_group_again is not created + +- name: create non default group check + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + description: Group Description + display_name: Group Display Name + managed_by: Domain Admins + organizational_unit: '{{test_win_domain_group_ou_path}}' + category: distribution + scope: domainlocal + attributes: + mail: test@email.com + wWWHomePage: www.google.com + protect: True + register: create_non_default_check + check_mode: yes + +- name: get actual create non default group check + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'" + register: create_non_default_actual_check + ignore_errors: True + +- name: assert create non default group check + assert: + that: + - create_non_default_check is changed + - create_non_default_check is created + - create_non_default_actual_check.rc == 1 + +- name: create non default group + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + description: Group Description + display_name: Group Display Name + managed_by: Domain Admins + organizational_unit: '{{test_win_domain_group_ou_path}}' + category: distribution + scope: domainlocal + attributes: + mail: test@email.com + wWWHomePage: www.google.com + protect: True + register: create_non_default + +- name: get actual create non default group + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'" + register: create_non_default_actual + ignore_errors: True + +- name: assert create non default group + assert: + that: + - create_non_default is changed + - create_non_default is created + - create_non_default.category == 'Distribution' + - create_non_default.description == 'Group Description' + - create_non_default.display_name == 'Group Display Name' + - create_non_default.distinguished_name == 'CN=' + test_win_domain_group_name + ',' + test_win_domain_group_ou_path + - create_non_default.group_scope == 'DomainLocal' + - create_non_default.guid is defined + - create_non_default.managed_by == 'CN=Domain Admins,CN=Users,' + test_win_domain_group_ldap_base + - create_non_default.name == test_win_domain_group_name + - create_non_default.protected_from_accidental_deletion == True + - create_non_default.sid is defined + - create_non_default.attributes.mail == 'test@email.com' + - create_non_default.attributes.wWWHomePage == 'www.google.com' + - create_non_default_actual.rc == 0 + +- name: create non default group again + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + description: Group Description + display_name: Group Display Name + managed_by: Domain Admins + organizational_unit: '{{test_win_domain_group_ou_path}}' + category: distribution + scope: domainlocal + attributes: + mail: test@email.com + wWWHomePage: www.google.com + register: create_non_default_again + +- name: assert create non default group again + assert: + that: + - create_non_default_again is not changed + - create_non_default_again is not created + +- name: try and move group with protection mode on + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + organizational_unit: CN=Users,{{test_win_domain_group_ldap_base}} + register: fail_move_with_protection + failed_when: fail_move_with_protection.msg != 'cannot move group ' + test_win_domain_group_name + ' when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this' + +- name: modify existing group check + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + description: New Description + display_name: New Display Name + managed_by: Administrator + organizational_unit: 'CN=Users,{{test_win_domain_group_ldap_base}}' + category: security + scope: global + attributes: + mail: anothertest@email.com + ignore_protection: True + register: modify_existing_check + check_mode: yes + +- name: get actual of modify existing group check + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; (Get-ADGroup -Identity '{{test_win_domain_group_name}}').DistinguishedName" + register: modify_existing_actual_check + +- name: assert modify existing group check + assert: + that: + - modify_existing_check is changed + - modify_existing_check is not created + - modify_existing_actual_check.stdout == 'CN=' + test_win_domain_group_name + ',' + test_win_domain_group_ou_path + '\r\n' + +- name: modify existing group + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + description: New Description + display_name: New Display Name + managed_by: Administrator + organizational_unit: CN=Users,{{test_win_domain_group_ldap_base}} + category: security + scope: global + attributes: + mail: anothertest@email.com + protect: True + ignore_protection: True + register: modify_existing + +- name: get actual of modify existing group + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; (Get-ADGroup -Identity '{{test_win_domain_group_name}}').DistinguishedName" + register: modify_existing_actual + +- name: assert modify existing group + assert: + that: + - modify_existing is changed + - modify_existing is not created + - modify_existing.category == 'Security' + - modify_existing.description == 'New Description' + - modify_existing.display_name == 'New Display Name' + - modify_existing.distinguished_name == 'CN=' + test_win_domain_group_name + ',CN=Users,' + test_win_domain_group_ldap_base + - modify_existing.group_scope == 'Global' + - modify_existing.guid is defined + - modify_existing.managed_by == 'CN=Administrator,CN=Users,' + test_win_domain_group_ldap_base + - modify_existing.name == test_win_domain_group_name + - modify_existing.protected_from_accidental_deletion == True + - modify_existing.sid is defined + - modify_existing.attributes.mail == 'anothertest@email.com' + - modify_existing_actual.stdout == 'CN=' + test_win_domain_group_name + ',CN=Users,' + test_win_domain_group_ldap_base + '\r\n' + +- name: modify existing group again + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + description: New Description + display_name: New Display Name + managed_by: Administrator + organizational_unit: CN=Users,{{test_win_domain_group_ldap_base}} + category: Security + scope: global + attributes: + mail: anothertest@email.com + protect: True + ignore_protection: True + register: modify_existing_again + +- name: assert modify existing group again + assert: + that: + - modify_existing_again is not changed + - modify_existing_again is not created + +- name: fail change managed_by to invalid user + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: present + scope: global + managed_by: fake user + register: fail_invalid_managed_by_user + failed_when: fail_invalid_managed_by_user.msg != 'failed to find managed_by user or group fake user to be used for comparison' + +- name: fail delete group with protection mode on + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: absent + register: fail_delete_with_protection + failed_when: fail_delete_with_protection.msg != 'cannot delete group ' + test_win_domain_group_name + ' when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this' + +- name: delete group with protection mode on + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: absent + ignore_protection: True + register: delete_with_force + +- name: get actual delete group with protection mode on + ansible.windows.win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'" + register: delete_with_force_actual + ignore_errors: True + +- name: assert delete group with protection mode on + assert: + that: + - delete_with_force is changed + - delete_with_force is not created + - delete_with_force_actual.rc == 1 + +- name: ensure the test group is deleted after the test + win_domain_group: + name: '{{test_win_domain_group_name}}' + state: absent + ignore_protection: True diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/aliases b/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/aliases new file mode 100644 index 000000000..ad7ccf7ad --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/aliases @@ -0,0 +1 @@ +unsupported diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/handlers/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/handlers/main.yml new file mode 100644 index 000000000..76a2a0f76 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: remove test domain user + win_domain_user: + name: '{{ test_user.distinguished_name }}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/tasks/main.yml new file mode 100644 index 000000000..89c977bfb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_object_info/tasks/main.yml @@ -0,0 +1,125 @@ +# These tests can't run in CI, this is really just a basic smoke tests for local runs. +--- +- name: assert better error message on auth failure + win_domain_object_info: + identity: id + register: fail_auth + failed_when: '"Failed to contact the AD server, this could be caused by the double hop problem" not in fail_auth.msg' + vars: + ansible_winrm_transport: ntlm + ansible_psrp_auth: ntlm + +- name: create test ad user + win_domain_user: + name: Ansible Test + firstname: Ansible + surname: Test + company: Contoso R Us + password: Password01 + state: present + password_never_expires: yes + groups: + - Domain Users + enabled: false + register: test_user + notify: remove test domain user + +- name: set a binary attribute and return other useful info missing from above + ansible.windows.win_shell: | + Set-ADUser -Identity '{{ test_user.sid }}' -Replace @{ audio = @([byte[]]@(1, 2, 3, 4), [byte[]]@(5, 6, 7, 8)) } + + $user = Get-ADUser -Identity '{{ test_user.sid }}' -Properties modifyTimestamp, ObjectGUID + + [TimeZoneInfo]::ConvertTimeToUtc($user.modifyTimestamp).ToString('o') + $user.ObjectGUID.ToString() + ([System.Security.Principal.SecurityIdentifier]'{{ test_user.sid }}').Translate([System.Security.Principal.NTAccount]).Value + register: test_user_extras + +- name: set other test info for easier access + set_fact: + test_user_mod_date: '{{ test_user_extras.stdout_lines[0] }}' + test_user_id: '{{ test_user_extras.stdout_lines[1] }}' + test_user_name: '{{ test_user_extras.stdout_lines[2] }}' + +- name: get properties for single user by DN + win_domain_object_info: + identity: '{{ test_user.distinguished_name }}' + register: by_identity + check_mode: yes # Just verifies it runs in check mode + +- name: assert get properties for single user by DN + assert: + that: + - not by_identity is changed + - by_identity.objects | length == 1 + - by_identity.objects[0].keys() | list | length == 4 + - by_identity.objects[0].DistinguishedName == test_user.distinguished_name + - by_identity.objects[0].Name == 'Ansible Test' + - by_identity.objects[0].ObjectClass == 'user' + - by_identity.objects[0].ObjectGUID == test_user_id + +- name: get specific properties by GUID + win_domain_object_info: + identity: '{{ test_user_id }}' + properties: + - audio # byte[] + - company # string + - department # not set + - logonCount # int + - modifyTimestamp # DateTime + - nTSecurityDescriptor # SecurityDescriptor as SDDL + - objectSID # SID + - ProtectedFromAccidentalDeletion # bool + - sAMAccountType # Test out the enum string attribute that we add + - userAccountControl # Test ou the enum string attribute that we add + register: by_guid_custom_props + +- name: assert get specific properties by GUID + assert: + that: + - not by_guid_custom_props is changed + - by_guid_custom_props.objects | length == 1 + - by_guid_custom_props.objects[0].DistinguishedName == test_user.distinguished_name + - by_guid_custom_props.objects[0].Name == 'Ansible Test' + - by_guid_custom_props.objects[0].ObjectClass == 'user' + - by_guid_custom_props.objects[0].ObjectGUID == test_user_id + - not by_guid_custom_props.objects[0].ProtectedFromAccidentalDeletion + - by_guid_custom_props.objects[0].audio == ['BQYHCA==', 'AQIDBA=='] + - by_guid_custom_props.objects[0].company == 'Contoso R Us' + - by_guid_custom_props.objects[0].department == None + - by_guid_custom_props.objects[0].logonCount == 0 + - by_guid_custom_props.objects[0].modifyTimestamp == test_user_mod_date + - by_guid_custom_props.objects[0].nTSecurityDescriptor.startswith('O:DAG:DAD:AI(') + - by_guid_custom_props.objects[0].objectSID.Name == test_user_name + - by_guid_custom_props.objects[0].objectSID.Sid == test_user.sid + - by_guid_custom_props.objects[0].sAMAccountType == 805306368 + - by_guid_custom_props.objects[0].sAMAccountType_AnsibleFlags == ['SAM_USER_OBJECT'] + - by_guid_custom_props.objects[0].userAccountControl == 66050 + - by_guid_custom_props.objects[0].userAccountControl_AnsibleFlags == ['ADS_UF_ACCOUNTDISABLE', 'ADS_UF_NORMAL_ACCOUNT', 'ADS_UF_DONT_EXPIRE_PASSWD'] + +- name: get invalid property + win_domain_object_info: + filter: sAMAccountName -eq 'Ansible Test' + properties: + - FakeProperty + register: invalid_prop_warning + +- name: assert get invalid property + assert: + that: + - not invalid_prop_warning is changed + - invalid_prop_warning.objects | length == 0 + - invalid_prop_warning.warnings | length == 1 + - '"Failed to retrieve properties for AD object" not in invalid_prop_warning.warnings[0]' + +- name: get by ldap filter returning multiple + win_domain_object_info: + ldap_filter: (&(objectClass=computer)(objectCategory=computer)) + properties: '*' + register: multiple_ldap + +- name: assert get by ldap filter returning multiple + assert: + that: + - not multiple_ldap is changed + - multiple_ldap.objects | length > 1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/aliases b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/aliases new file mode 100644 index 000000000..22f581bfd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/aliases @@ -0,0 +1,2 @@ +shippable/windows/group2 +skip/windows/2012 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/defaults/main.yml new file mode 100644 index 000000000..6892b03a6 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/defaults/main.yml @@ -0,0 +1,22 @@ +--- +win_domain_ou_test_type: default +win_domain_ou_root_path: DC=ansible,DC=test +win_domain_ou_structure: + - path: "{{ win_domain_ou_root_path }}" + name: VMware + - path: "OU=VMware,{{ win_domain_ou_root_path }}" + name: End User Computing + - path: "OU=End User Computing,OU=VMware,{{ win_domain_ou_root_path }}" + name: Workspace ONE Cloud Services + - path: "OU=Workspace ONE Cloud Services,OU=End User Computing,OU=VMware,{{ win_domain_ou_root_path }}" + name: SaaS Development and Enablement + +win_domain_ou_structure_check_mode: + - path: "{{ win_domain_ou_root_path }}" + name: VMware_check + - path: "OU=VMware_check,{{ win_domain_ou_root_path }}" + name: End User Computing + - path: "OU=End User Computing,OU=VMware_check,{{ win_domain_ou_root_path }}" + name: Workspace ONE Cloud Services + - path: "OU=Workspace ONE Cloud Services,OU=End User Computing,OU=VMware_check,{{ win_domain_ou_root_path }}" + name: SaaS Development and Enablement diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/meta/main.yml new file mode 100644 index 000000000..da6e52e2f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_domain_tests diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/check_mode_test.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/check_mode_test.yml new file mode 100644 index 000000000..730298e40 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/check_mode_test.yml @@ -0,0 +1,116 @@ +--- +- name: Setup structure for checkmode + block: + - name: Ensure OU structure is present + community.windows.win_domain_ou: + name: "{{ item.name }}" + protected: false + path: "{{ item.path }}" + loop: "{{ win_domain_ou_structure }}" + register: test_setup_checkmode + failed_when: test_setup_checkmode is not changed + +- name: Run Check Mode Tests + block: + - name: Ensure OU is present (check_mode) + community.windows.win_domain_ou: + name: ansible_checkmode + register: test_check_mode_1 + failed_when: test_check_mode_1 is not changed + + - name: Ensure OU has updated properties (check_mode) + community.windows.win_domain_ou: + name: End User Computing + protected: true + path: "{{ win_domain_ou_root_path }}" + properties: + city: Sandy Springs + state: Georgia + streetaddress: 1155 Perimeter Center West + country: US + description: EUC Business Unit + postalcode: 30189 + register: test_check_mode_2 + failed_when: test_check_mode_2 is not changed + + - name: Ensure OU structure win_domain_ou_structure_check_mode is present (check_mode) + community.windows.win_domain_ou: + name: "{{ item.name }}" + protected: false + path: "{{ item.path }}" + loop: "{{ win_domain_ou_structure_check_mode }}" + register: test_check_mode_3 + failed_when: test_check_mode_3 is not changed + + - name: Ensure OU structure win_domain_ou_structure is present (check_mode) + community.windows.win_domain_ou: + name: "{{ item.name }}" + protected: false + path: "{{ item.path }}" + loop: "{{ win_domain_ou_structure }}" + register: test_check_mode_4 + failed_when: test_check_mode_4 is changed + + - name: Ensure OU structure is absent, recursive (check_mode) + community.windows.win_domain_ou: + name: VMware + path: "{{ win_domain_ou_root_path }}" + state: absent + recursive: true + register: test_check_mode_5 + failed_when: test_check_mode_5 is not changed + + - name: Ensure OU is present with specific properties (check_mode) + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + properties: + city: Sandy Springs + state: Georgia + streetaddress: 1155 Perimeter Center West + register: test_check_mode_6 + failed_when: test_check_mode_6 is not changed + + - name: Ensure OU is present with specific properties added (check_mode) + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + properties: + country: US + description: EUC Business Unit + postalcode: 30189 + register: test_check_mode_7 + failed_when: test_check_mode_7 is not changed + + - name: Ensure existing ou 'End User Computing' is absent (check_mode) + community.windows.win_domain_ou: + name: End User Computing + path: "OU=VMware,{{ win_domain_ou_root_path }}" + state: absent + register: test_check_mode_8 + failed_when: test_check_mode_8 is not changed + + - name: Ensure NonExisting OU 'VMW Atlanta' is absent (check_mode) + community.windows.win_domain_ou: + name: "VMW Atlanta" + path: "{{ win_domain_ou_root_path }}" + state: absent + register: test_check_mode_9 + failed_when: test_check_mode_9 is changed + check_mode: true + +- name: sanity check on check_mode + ansible.windows.win_shell: | + get-adorganizationalunit -Identity ansible_checkmode + register: test_sanity + failed_when: "'ansible_checkmode' in test_sanity.stdout" + changed_when: false + +- name: Teardown structure used for checkmode + community.windows.win_domain_ou: + name: VMware + path: "{{ win_domain_ou_root_path }}" + state: absent + recursive: true + register: test_teardown + failed_when: test_teardown is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/main.yml new file mode 100644 index 000000000..1ea02d5ee --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: Run Tests + import_tasks: tests.yml + +- name: Run Check Mode Tests + import_tasks: check_mode_test.yml
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/tests.yml new file mode 100644 index 000000000..f5fe8d576 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_ou/tasks/tests.yml @@ -0,0 +1,190 @@ +--- +- name: Ensure OU is present + community.windows.win_domain_ou: + name: AnsibleFest + register: test1 + failed_when: test1 is not changed + +- name: Ensure OU is present (idempotence check) + community.windows.win_domain_ou: + name: AnsibleFest + register: test1a + failed_when: test1a is changed + +- name: Ensure OU is absent + community.windows.win_domain_ou: + name: AnsibleFest + state: absent + register: test1_clean + failed_when: test1_clean is not changed + +- name: Ensure OU is absent (idempotence check) + community.windows.win_domain_ou: + name: AnsibleFest + state: absent + register: test1_clean_idempotent + failed_when: test1_clean_idempotent is changed + +- name: Ensure OU is present with path + community.windows.win_domain_ou: + name: End User Computing + path: "{{ win_domain_ou_root_path }}" + register: test2 + failed_when: test2 is not changed + +- name: Ensure OU is present with path (idempotence check) + community.windows.win_domain_ou: + name: End User Computing + path: "{{ win_domain_ou_root_path }}" + register: test2a + failed_when: test2a is changed + +- name: Ensure OU has updated properties + community.windows.win_domain_ou: + name: End User Computing + protected: true + path: "{{ win_domain_ou_root_path }}" + properties: + city: Sandy Springs + state: Georgia + streetaddress: 1155 Perimeter Center West + country: US + description: EUC Business Unit + postalcode: 30189 + register: test3 + failed_when: test3 is not changed + +- name: Ensure OU has updated properties (idempotence check) + community.windows.win_domain_ou: + name: End User Computing + protected: true + path: "{{ win_domain_ou_root_path }}" + properties: + city: Sandy Springs + state: Georgia + streetaddress: 1155 Perimeter Center West + country: US + description: EUC Business Unit + postalcode: 30189 + register: test3a + failed_when: test3a is changed + +- name: Ensure OU structure is present + community.windows.win_domain_ou: + name: "{{ item.name }}" + protected: false + path: "{{ item.path }}" + loop: "{{ win_domain_ou_structure }}" + register: test4 + failed_when: test4 is not changed + +- name: Ensure OU structure is present (idempotence check) + community.windows.win_domain_ou: + name: "{{ item.name }}" + protected: false + path: "{{ item.path }}" + loop: "{{ win_domain_ou_structure }}" + register: test4a + failed_when: test4a is changed + +- name: Ensure OU structure is absent, recursive + community.windows.win_domain_ou: + name: VMware + path: "{{ win_domain_ou_root_path }}" + state: absent + recursive: true + register: test5 + failed_when: test5 is not changed + +- name: Ensure OU structure is absent, recursive (idempotence check) + community.windows.win_domain_ou: + name: VMware + path: "{{ win_domain_ou_root_path }}" + state: absent + recursive: true + register: test5a + failed_when: test5a is changed + +- name: Ensure OU is present with specific properties + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + properties: + city: Sandy Springs + state: Georgia + streetaddress: 1155 Perimeter Center West + register: test6 + failed_when: test6 is not changed + +- name: Ensure OU is present with specific properties (idempotence check) + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + properties: + city: Sandy Springs + state: Georgia + streetaddress: 1155 Perimeter Center West + register: test6a + failed_when: test6a is changed + +- name: Ensure OU is present with specific properties added + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + properties: + country: US + description: EUC Business Unit + postalcode: 30189 + register: test7 + failed_when: test7 is not changed + +- name: Ensure OU is present with specific properties added (idempotence check) + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + properties: + country: US + description: EUC Business Unit + postalcode: 30189 + register: test7a + failed_when: test7a is changed + +- name: Ensure OU is absent + community.windows.win_domain_ou: + name: End User Computing + path: "{{ win_domain_ou_root_path }}" + state: absent + register: test8 + failed_when: test8 is not changed + +- name: Ensure OU is absent (idempotence check) + community.windows.win_domain_ou: + name: End User Computing + path: "{{ win_domain_ou_root_path }}" + state: absent + register: test8a + failed_when: test8a is changed + +- name: Ensure OU is absent + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + state: absent + register: test9 + failed_when: test9 is not changed + +- name: Ensure OU is absent (idempotence check) + community.windows.win_domain_ou: + name: VMW Atlanta + path: "{{ win_domain_ou_root_path }}" + state: absent + register: test9a + failed_when: test9a is changed + +- name: Assertions + assert: + that: + - test1a.ou.Name == "AnsibleFest" + - test3a.ou.StreetAddress == "1155 Perimeter Center West" + - test7a.ou.Country == "US" + - test6a.ou.City == "Sandy Springs" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_user/aliases b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/aliases new file mode 100644 index 000000000..22f581bfd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/aliases @@ -0,0 +1,2 @@ +shippable/windows/group2 +skip/windows/2012 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_user/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/meta/main.yml new file mode 100644 index 000000000..da6e52e2f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_domain_tests diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/check_mode_test.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/check_mode_test.yml new file mode 100644 index 000000000..755135421 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/check_mode_test.yml @@ -0,0 +1,27 @@ +--- +- name: Create Justi (check_mode) + community.windows.win_domain_user: + name: Justi + password: J@n3P4ssw0rd# + state: present + update_password: on_create + account_locked: false + password_never_expires: false + enabled: true + register: new_user_check_mode + failed_when: + - not new_user_check_mode.changed + - not new_user_check_mode.created + check_mode: true + +- name: Sanity check on Check Mode + ansible.windows.win_powershell: + script: | + try { + Get-AdUser -Identity Justi + $Ansible.Failed = $true + } catch { + $Ansible.Failed = $false + } + register: sanity_check + changed_when: false diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/main.yml new file mode 100644 index 000000000..2edc6ce0e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: Remove Users + win_domain_user: + name: "{{ item }}" + state: absent + loop: + - justi + - hana + - katie + +- name: Run Test Suite 1 + import_tasks: test1.yml + +- name: Run Test Suite 2 + import_tasks: test2.yml + +- name: Run Check Mode Tests + import_tasks: check_mode_test.yml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/test1.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/test1.yml new file mode 100644 index 000000000..a5ba7095c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/test1.yml @@ -0,0 +1,76 @@ +--- +- name: Justi | Create User + win_domain_user: + name: Justi + upn: justi@ansible.test + password: c0dinGwithKI@ + state: present + update_password: on_create + password_never_expires: false + enabled: true + spn: + - MSSQLSvc/US99DBSVR1 + - MSSQLSvc/US99DBSVR1.vmware.com + - MSSQLSvc/US99DBSVR1.vmware.com:1433 + register: new_user_test + failed_when: new_user_test is not success + +- name: Justi | Create User (idempotence check) + win_domain_user: + name: Justi + upn: justi@ansible.test + password: c0dinGwithKI@ + state: present + update_password: on_create + password_never_expires: false + enabled: true + spn: + - MSSQLSvc/US99DBSVR1 + - MSSQLSvc/US99DBSVR1.vmware.com + - MSSQLSvc/US99DBSVR1.vmware.com:1433 + register: new_user_test_idempotent + failed_when: new_user_test_idempotent is changed + +- name: Justi | Update Password + win_domain_user: + name: Justi + password: al3x@ndriastEch! + state: present + update_password: always + password_never_expires: false + enabled: true + register: password_changed + failed_when: not password_changed.changed + +- name: Justi | Replace SPNs + win_domain_user: + name: Justi + state: present + spn: + - MSSQLSvc/ + - MSSQLSvc/US99DBSVR1.vmware.com + register: spn_changed + failed_when: not spn_changed.changed + +- name: Justi | Add SPN + win_domain_user: + name: Justi + state: present + spn_action: add + spn: + - MSSQLSvc/US99DBSVR1.vmware.com:2433 + register: add_spn_changed + failed_when: add_spn_changed is not changed + +- name: Assertions + assert: + that: + - new_user_test.changed + - new_user_test.created + - not new_user_test.password_never_expires + - not new_user_test_idempotent.changed + - new_user_test_idempotent.distinguished_name == "CN=Justi,CN=Users,DC=ansible,DC=test" + - password_changed.changed + - password_changed.password_updated + - spn_changed.changed + - add_spn_changed.changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/test2.yml b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/test2.yml new file mode 100644 index 000000000..767ed538b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_domain_user/tasks/test2.yml @@ -0,0 +1,171 @@ +--- +- name: Hana | Create User w/Invalid Password + win_domain_user: + name: hana + upn: hana@ansible.test + firstname: Hana + surname: Lytx + display_name: Hana Lytx + company: HelpMeExitVi Inc. + password: 123 + state: present + groups: + - Domain Admins + street: 123 TechTok St. + city: Sysengineer + state_province: OH + postal_code: 12345 + country: US + attributes: + telephoneNumber: 555-123456 + update_password: when_changed + password_never_expires: true + register: bad_password_test + failed_when: bad_password_test is success + +- name: Hana | Create User Again w/Valid Password + win_domain_user: + name: hana + upn: hana@ansible.test + firstname: Hana + surname: Lytx + display_name: Hana Lytx + company: HelpMeExitVi Inc. + password: h@nAlyTx18!X + state: present + groups: + - Domain Admins + street: 123 TechTok St. + city: Sysengineer + state_province: OH + postal_code: 12345 + country: US + attributes: + telephoneNumber: 555-123456 + update_password: when_changed + password_never_expires: true + register: good_password_test + failed_when: good_password_test is not success + +- name: Katie | Create User with Delegates + win_domain_user: + name: katie + firstname: Katie + surname: Kickscancer + display_name: Katie Kickscancer + password: SyNs@tI0N + update_password: on_create + state: present + delegates: + - CN=justi,CN=Users,DC=ansible,DC=test + spn: + - HTTPSvc/judge-svc1:80 + - HTTPSvc/gabrielle-svc1.vmware.com + register: delegates_test + failed_when: delegates_test is not success + +- name: Katie | Create User with Delegates (idempotence check) + win_domain_user: + name: katie + firstname: Katie + surname: Kickscancer + display_name: Katie Kickscancer + password: SyNs@tI0N + update_password: on_create + state: present + delegates: + - CN=justi,CN=Users,DC=ansible,DC=test + spn: + - HTTPSvc/judge-svc1:80 + - HTTPSvc/gabrielle-svc1.vmware.com + register: delegates_test_idempotent + failed_when: delegates_test_idempotent is changed + +- name: Katie | Remove SPN + win_domain_user: + name: katie + state: present + spn_action: remove + spn: + - HTTPSvc/gabrielle-svc1.vmware.com + register: remove_spn_test + failed_when: remove_spn_test is not changed + +- name: Katie | Remove SPN (idempotence check) + win_domain_user: + name: katie + state: present + spn_action: remove + spn: + - HTTPSvc/gabrielle-svc1.vmware.com + register: remove_spn_test_idempotent + failed_when: remove_spn_test_idempotent is changed + +- name: Katie | Add to groups that are missing - fail + win_domain_user: + name: katie + state: present + groups: + - Missing Group + register: add_invalid_group_fail + failed_when: add_invalid_group_fail is success + +- name: Katie | Add to groups that are missing - warn + win_domain_user: + name: katie + state: present + groups: + - Missing Group + groups_missing_behaviour: warn + register: add_invalid_group_warn + failed_when: not add_invalid_group_warn.warnings[0].startswith("Failed to locate group Missing Group but continuing on") + +- name: Katie | Add to groups that are missing - ignore + win_domain_user: + name: katie + state: present + groups: + - Missing Group + groups_missing_behaviour: ignore + register: add_invalid_group_ignore + failed_when: (add_invalid_group_ignore.warnings | default([]) | length) != 0 + +- name: Hana | Remove User + win_domain_user: + name: hana + state: absent + register: user_removed + failed_when: user_removed is not changed + +- name: Hana | Remove User (idempotence check) + win_domain_user: + name: hana + state: absent + register: user_removed_idempotent + failed_when: user_removed_idempotent is changed + +- name: Remove Justi + win_domain_user: + name: justi + state: absent + +- name: Remove Katie + win_domain_user: + name: katie + state: absent + +- name: Assertions + assert: + that: + - delegates_test is success + - not delegates_test_idempotent.changed + - not bad_password_test.changed + - good_password_test.changed + - good_password_test.upn == "hana@ansible.test" + - good_password_test.password_never_expires + - good_password_test.company == "HelpMeExitVi Inc." + - not good_password_test.created + - good_password_test.password_updated + - user_removed.state == "absent" + - not user_removed_idempotent.changed + - remove_spn_test.spn == ['HTTPSvc/judge-svc1:80'] diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dotnet_ngen/aliases b/ansible_collections/community/windows/tests/integration/targets/win_dotnet_ngen/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dotnet_ngen/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_dotnet_ngen/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_dotnet_ngen/tasks/main.yml new file mode 100644 index 000000000..146eeb3c5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_dotnet_ngen/tasks/main.yml @@ -0,0 +1,20 @@ +# this only tests check mode as the full run can take several minutes to +# complete, this way we at least verify the script is parsable +--- +- name: run in check mode + win_dotnet_ngen: + register: result_check + check_mode: yes + +- name: assert run in check mode + assert: + that: + - result_check is changed + - result_check.dotnet_ngen_update_exit_code == 0 + - result_check.dotnet_ngen_update_output == "check mode output for C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ngen.exe update /force" + - result_check.dotnet_ngen_eqi_exit_code == 0 + - result_check.dotnet_ngen_eqi_output == "check mode output for C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ngen.exe executeQueuedItems" + - result_check.dotnet_ngen64_update_exit_code == 0 + - result_check.dotnet_ngen64_update_output == "check mode output for C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe update /force" + - result_check.dotnet_ngen64_eqi_exit_code == 0 + - result_check.dotnet_ngen64_eqi_output == "check mode output for C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ngen.exe executeQueuedItems" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog/aliases b/ansible_collections/community/windows/tests/integration/targets/win_eventlog/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_eventlog/tasks/main.yml new file mode 100644 index 000000000..dcc075fcc --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog/tasks/main.yml @@ -0,0 +1,10 @@ +- name: Run tests for win_eventlog in normal mode + import_tasks: tests.yml + vars: + in_check_mode: no + +- name: Run tests for win_eventlog in check-mode + import_tasks: tests.yml + vars: + in_check_mode: yes + check_mode: yes diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_eventlog/tasks/tests.yml new file mode 100644 index 000000000..94c231a4b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog/tasks/tests.yml @@ -0,0 +1,447 @@ +# Test code for win_eventlog + +# (c) 2017, Andrew Saraceni <andrew.saraceni@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: Remove potentially leftover logs + win_eventlog: + name: "{{ item }}" + state: absent + with_items: + - WinEventLogTest + - NewWinEventLogTest + + +- name: Add log without sources + win_eventlog: + name: WinEventLogTest + state: present + register: add_log_without_sources + failed_when: add_log_without_sources.changed != false or add_log_without_sources.msg != "You must specify one or more sources when creating a log for the first time" + + +- name: Add log + win_eventlog: &wel_present + name: WinEventLogTest + sources: + - WinEventLogSource1 + - WinEventLogSource2 + state: present + register: add_log + +- name: Test add_log (normal mode) + assert: + that: + - add_log.changed == true + - add_log.exists == true + - add_log.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - add_log.sources_changed == ["WinEventLogSource1", "WinEventLogSource2"] + when: not in_check_mode + +- name: Test add_log (check-mode) + assert: + that: + - add_log.changed == true + - add_log.exists == false + - add_log.sources_changed == [] + when: in_check_mode + + +- name: Add log (again) + win_eventlog: *wel_present + register: add_log_again + +- name: Test add_log_again (normal mode) + assert: + that: + - add_log_again.changed == false + - add_log_again.exists == true + - add_log_again.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - add_log_again.sources_changed == [] + when: not in_check_mode + + +- name: Run tests for normal mode only (expects event log) + when: not in_check_mode + block: + + - name: Change default source + win_eventlog: + <<: *wel_present + sources: + - WinEventLogTest + category_file: C:\TestApp\AppCategories.dll + register: change_default_source + failed_when: change_default_source.changed != false or change_default_source.msg != "Cannot modify default source WinEventLogTest of log WinEventLogTest - you must remove the log" + + + - name: Change source category + win_eventlog: &welc_present + <<: *wel_present + sources: + - WinEventLogSource1 + category_file: C:\TestApp\AppCategories.dll + register: change_source_category + + - name: Test change_source_category + assert: + that: + - change_source_category.changed == true + - change_source_category.exists == true + - change_source_category.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - change_source_category.sources_changed == ["WinEventLogSource1"] + + + - name: Change source category (again) + win_eventlog: *welc_present + register: change_source_category_again + + - name: Test change_source_category_again + assert: + that: + - change_source_category_again.changed == false + - change_source_category_again.exists == true + - change_source_category_again.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - change_source_category_again.sources_changed == [] + + + - name: Change source message + win_eventlog: &welm_present + <<: *welc_present + message_file: C:\TestApp\AppMessages.dll + register: change_source_message + + - name: Test change_source_message + assert: + that: + - change_source_message.changed == true + - change_source_message.exists == true + - change_source_message.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - change_source_message.sources_changed == ["WinEventLogSource1"] + + + - name: Change source message (again) + win_eventlog: *welm_present + register: change_source_message_again + + - name: Test change_source_message_again + assert: + that: + - change_source_message_again.changed == false + - change_source_message_again.exists == true + - change_source_message_again.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - change_source_message_again.sources_changed == [] + + + - name: Change source parameter + win_eventlog: &welp_present + <<: *welm_present + parameter_file: C:\TestApp\AppParameters.dll + register: change_source_parameter + + - name: Test change_source_parameter + assert: + that: + - change_source_parameter.changed == true + - change_source_parameter.exists == true + - change_source_parameter.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - change_source_parameter.sources_changed == ["WinEventLogSource1"] + + + - name: Change source parameter (again) + win_eventlog: *welp_present + register: change_source_parameter_again + + - name: Test change_source_parameter_again + assert: + that: + - change_source_parameter_again.changed == false + - change_source_parameter_again.exists == true + - change_source_parameter_again.sources == ["WinEventLogSource1", "WinEventLogSource2", "WinEventLogTest"] + - change_source_parameter_again.sources_changed == [] + + + - name: Change log maximum size + win_eventlog: &wels_present + <<: *wel_present + maximum_size: 256MB + register: change_log_maximum_size + + - name: Test change_log_maximum_size + assert: + that: + - change_log_maximum_size.changed == true + - change_log_maximum_size.exists == true + - change_log_maximum_size.maximum_size_kb == 262144 + + + - name: Change log maximum size (again) + win_eventlog: *wels_present + register: change_log_maximum_size_again + + - name: Test change_log_maximum_size_again + assert: + that: + - change_log_maximum_size_again.changed == false + - change_log_maximum_size_again.exists == true + - change_log_maximum_size_again.maximum_size_kb == 262144 + + + - name: Change log invalid maximum size 1 + win_eventlog: + <<: *wel_present + maximum_size: 256 MB + register: change_log_invalid_maximum_size_1 + failed_when: change_log_invalid_maximum_size_1.changed != false or change_log_invalid_maximum_size_1.msg != "Maximum size 256 MB is not properly specified" + + + - name: Change log invalid maximum size 2 + win_eventlog: + <<: *wel_present + maximum_size: 5GB + register: change_log_invalid_maximum_size_2 + failed_when: change_log_invalid_maximum_size_2.changed != false or change_log_invalid_maximum_size_2.msg != "Maximum size must be between 64KB and 4GB" + + + - name: Change log invalid maximum size 3 + win_eventlog: + <<: *wel_present + maximum_size: 129KB + register: change_log_invalid_maximum_size_3 + failed_when: change_log_invalid_maximum_size_3.changed != false or change_log_invalid_maximum_size_3.msg != "Maximum size must be divisible by 64KB" + + + - name: Change log retention days + win_eventlog: &welr_present + <<: *wels_present + retention_days: 128 + register: change_log_retention_days + + - name: Test change_log_retention_days + assert: + that: + - change_log_retention_days.changed == true + - change_log_retention_days.exists == true + - change_log_retention_days.retention_days == 128 + + + - name: Change log retention days (again) + win_eventlog: *welr_present + register: change_log_retention_days_again + + - name: Test change_log_retention_days_again + assert: + that: + - change_log_retention_days_again.changed == false + - change_log_retention_days_again.exists == true + - change_log_retention_days_again.retention_days == 128 + + + - name: Change log overflow action + win_eventlog: &welo_present + <<: *wels_present + overflow_action: OverwriteAsNeeded + register: change_log_overflow_action + + - name: Test change_log_overflow_action + assert: + that: + - change_log_overflow_action.changed == true + - change_log_overflow_action.exists == true + - change_log_overflow_action.overflow_action == "OverwriteAsNeeded" + + + - name: Change log overflow action (again) + win_eventlog: *welo_present + register: change_log_overflow_action_again + + - name: Test change_log_overflow_action_again + assert: + that: + - change_log_overflow_action_again.changed == false + - change_log_overflow_action_again.exists == true + - change_log_overflow_action_again.overflow_action == "OverwriteAsNeeded" + + + - name: Add log with existing source + win_eventlog: &wele_present + name: NewWinEventLogTest + sources: + - WinEventLogSource1 + state: present + register: add_log_with_existing_source + failed_when: add_log_with_existing_source.changed != false or add_log_with_existing_source.msg != "Source WinEventLogSource1 already exists and cannot be created" + + + - name: Add new log + win_eventlog: + <<: *wele_present + sources: + - NewWinEventLogSource1 + + - name: Change source for different log + win_eventlog: + <<: *wele_present + sources: + - WinEventLogSource1 + category_file: C:\TestApp\AppCategories.dll + register: change_source_for_different_log + failed_when: change_source_for_different_log.changed != false or change_source_for_different_log.msg != "Source WinEventLogSource1 does not belong to log NewWinEventLogTest and cannot be modified" + + - name: Remove new log + win_eventlog: + name: NewWinEventLogTest + state: absent + + + - name: Add entry to log + ansible.windows.win_shell: Write-EventLog -LogName WinEventLogTest -Source WinEventLogSource1 -EntryType Information -EventId 12345 -Message "Test message" + + - name: Verify add entry + win_eventlog: + name: WinEventLogTest + state: present + register: verify_add_entry + + - name: Test verify_add_entry + assert: + that: + - verify_add_entry.changed == false + - verify_add_entry.exists == true + - verify_add_entry.entries == 1 + + + - name: Clear log + win_eventlog: &wel_clear + name: WinEventLogTest + state: clear + register: clear_log + + - name: Test clear_log + assert: + that: + - clear_log.changed == true + - clear_log.exists == true + - clear_log.entries == 0 + when: not in_check_mode + + + - name: Clear log (again) + win_eventlog: *wel_clear + register: clear_log_again + + - name: Test clear_log_again + assert: + that: + - clear_log_again.changed == false + - clear_log_again.exists == true + - clear_log_again.entries == 0 + when: in_check_mode + + +- name: Clear absent log + win_eventlog: + name: WinEventLogTest + state: clear + register: clear_absent_log + when: in_check_mode + failed_when: clear_absent_log.changed != false or clear_absent_log.msg != "Cannot clear log WinEventLogTest as it does not exist" + + +- name: Remove default source + win_eventlog: &weld_absent + name: WinEventLogTest + sources: + - WinEventLogTest + state: absent + register: remove_default_source + failed_when: remove_default_source.changed != false or remove_default_source.msg != "Cannot remove default source WinEventLogTest from log WinEventLogTest - you must remove the log" + + +- name: Remove source + win_eventlog: &wels_absent + <<: *weld_absent + sources: + - WinEventLogSource1 + register: remove_source + +- name: Test remove_source (normal mode) + assert: + that: + - remove_source.changed == true + - remove_source.exists == true + - remove_source.sources == ["WinEventLogSource2", "WinEventLogTest"] + - remove_source.sources_changed == ["WinEventLogSource1"] + when: not in_check_mode + +- name: Test remove_source (check-mode) + assert: + that: + - remove_source.changed == false + - remove_source.exists == false + - remove_source.sources_changed == [] + when: in_check_mode + + +- name: Remove source (again) + win_eventlog: *wels_absent + register: remove_source_again + +- name: Test remove_source_again (normal mode) + assert: + that: + - remove_source_again.changed == false + - remove_source_again.exists == true + - remove_source.sources == ["WinEventLogSource2", "WinEventLogTest"] + - remove_source_again.sources_changed == [] + when: not in_check_mode + + +- name: Remove log + win_eventlog: &wel_absent + name: WinEventLogTest + state: absent + register: remove_log + +- name: Test remove_log (normal mode) + assert: + that: + - remove_log.changed == true + - remove_log.exists == false + - remove_log.sources_changed == ["WinEventLogSource2", "WinEventLogTest"] + when: not in_check_mode + +- name: Test remove_log (check-mode) + assert: + that: + - remove_log.changed == false + - remove_log.exists == false + - remove_log.sources_changed == [] + when: in_check_mode + + +- name: Remove log (again) + win_eventlog: *wel_absent + register: remove_log_again + +- name: Test remove_log_again (normal mode) + assert: + that: + - remove_log_again.changed == false + - remove_log_again.exists == false + - remove_log_again.sources_changed == [] + when: not in_check_mode diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/aliases b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/defaults/main.yml new file mode 100644 index 000000000..611d16ec0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/defaults/main.yml @@ -0,0 +1,6 @@ +win_test_log_source: + log: WinEventLogEntryTest + source: WinEventLogEntrySource +win_test_log_source_extra: + log: ExtraWinEventLogEntryTest + source: ExtraWinEventLogEntrySource diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/library/test_win_eventlog_entry.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/library/test_win_eventlog_entry.ps1 new file mode 100644 index 000000000..2af179b5f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/library/test_win_eventlog_entry.ps1 @@ -0,0 +1,33 @@ +#!powershell + +# (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#Requires -Module Ansible.ModuleUtils.Legacy + +# Test module used to grab the latest entry from an event log and output its properties + +$ErrorActionPreference = "Stop" + +$params = Parse-Args $args -supports_check_mode $true +$log = Get-AnsibleParam -obj $params -name "log" -type "str" -failifempty $true + +$result = @{ + changed = $false +} + +try { + $log_entry = Get-EventLog -LogName $log | Select-Object -First 1 -Property * +} +catch { + Fail-Json -obj $result -message "Could not find any entries for log $log" +} + +$result.source = $log_entry.Source +$result.event_id = $log_entry.EventID +$result.message = $log_entry.Message +$result.entry_type = $log_entry.EntryType.ToString() +$result.category = $log_entry.CategoryNumber +$result.raw_data = $log_entry.Data -join "," + +Exit-Json -obj $result diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/tasks/main.yml new file mode 100644 index 000000000..9f416598e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/tasks/main.yml @@ -0,0 +1,33 @@ +# win_shell invocations can eventually be replaced with win_eventlog +- name: Remove potentially leftover test logs and sources + ansible.windows.win_shell: Remove-EventLog -LogName "{{ item.log }}" -ErrorAction SilentlyContinue + with_items: + - "{{ win_test_log_source }}" + - "{{ win_test_log_source_extra }}" + failed_when: no + +- name: Add new test logs and sources + ansible.windows.win_shell: New-EventLog -LogName "{{ item.log }}" -Source "{{ item.source }}" + with_items: + - "{{ win_test_log_source }}" + - "{{ win_test_log_source_extra }}" + +- name: Run tests for win_eventlog_entry + block: + + - name: Test in normal mode + import_tasks: tests.yml + vars: + in_check_mode: no + + - name: Test in check-mode + import_tasks: tests.yml + vars: + in_check_mode: yes + check_mode: yes + +- name: Remove test logs and sources + ansible.windows.win_shell: Remove-EventLog -LogName "{{ item.log }}" + with_items: + - "{{ win_test_log_source }}" + - "{{ win_test_log_source_extra }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/tasks/tests.yml new file mode 100644 index 000000000..688a4b532 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_eventlog_entry/tasks/tests.yml @@ -0,0 +1,159 @@ +# Test code for win_eventlog_entry + +# (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Add entry to fake log + win_eventlog_entry: + log: FakeLogName + source: "{{ win_test_log_source.source }}" + event_id: 12345 + message: This is a test log entry message + register: add_entry_to_fake_log + failed_when: add_entry_to_fake_log.changed != false or add_entry_to_fake_log.msg != "Log FakeLogName does not exist and cannot be written to" + + +- name: Add entry from fake source + win_eventlog_entry: + log: "{{ win_test_log_source.log }}" + source: FakeSourceName + event_id: 12345 + message: This is a test log entry message + register: add_entry_from_fake_source + failed_when: add_entry_from_fake_source.changed != false or add_entry_from_fake_source.msg != "Source FakeSourceName does not exist" + + +- name: Add entry with invalid event_id + win_eventlog_entry: + log: "{{ win_test_log_source.log }}" + source: "{{ win_test_log_source.source }}" + event_id: 67000 + message: This is a test log entry message + register: add_entry_with_invalid_event_id + failed_when: add_entry_with_invalid_event_id.changed != false or add_entry_with_invalid_event_id.msg != "Event ID must be between 0 and 65535" + + +- name: Add entry from other log source + win_eventlog_entry: + log: "{{ win_test_log_source.log }}" + source: "{{ win_test_log_source_extra.source }}" + event_id: 12345 + message: This is a test log entry message + register: add_entry_from_other_log_source + failed_when: add_entry_from_other_log_source.changed != false or add_entry_from_other_log_source.msg != "Source {{ win_test_log_source_extra.source }} does not belong to log {{ win_test_log_source.log }} and cannot be written to" + + +- name: Add entry + win_eventlog_entry: &wele + log: "{{ win_test_log_source.log }}" + source: "{{ win_test_log_source.source }}" + event_id: 12345 + message: This is a test log entry message + register: add_entry + +- name: Test add_entry + assert: + that: + - add_entry.changed == true + - add_entry.msg == "Entry added to log {{ win_test_log_source.log }} from source {{ win_test_log_source.source }}" + +- name: Test add_entry count (normal mode) + ansible.windows.win_shell: (Get-EventLog -LogName "{{ win_test_log_source.log }}").Count + register: add_entry_count + failed_when: add_entry_count.stdout_lines[0] != "1" + when: not in_check_mode + +- name: Test add_entry result (normal mode) + test_win_eventlog_entry: + log: "{{ win_test_log_source.log }}" + register: add_entry_result + when: not in_check_mode + +- name: Test add_entry_result (normal mode) + assert: + that: + - add_entry_result.source == win_test_log_source.source + - add_entry_result.event_id == 12345 + - add_entry_result.message == "This is a test log entry message" + when: not in_check_mode + + +- name: Add entry (again) + win_eventlog_entry: *wele + register: add_entry_again + +- name: Test add_entry_again (normal mode) + assert: + that: + - add_entry_again.changed == true + - add_entry_again.msg == "Entry added to log {{ win_test_log_source.log }} from source {{ win_test_log_source.source }}" + when: not in_check_mode + +- name: Test add_entry_again count (normal mode) + ansible.windows.win_shell: (Get-EventLog -LogName "{{ win_test_log_source.log }}").Count + register: add_entry_again_count + failed_when: add_entry_again_count.stdout_lines[0] != "2" + when: not in_check_mode + + +- name: Add entry all options + win_eventlog_entry: &wele_ao + <<: *wele + event_id: 500 + message: This is a test error message + entry_type: Error + category: 5 + raw_data: 10,20 + register: add_entry_all_options + +- name: Test add_entry_all_options + assert: + that: + - add_entry_all_options.changed == true + - add_entry_all_options.msg == "Entry added to log {{ win_test_log_source.log }} from source {{ win_test_log_source.source }}" + +- name: Test add_entry_all_options count (normal mode) + ansible.windows.win_shell: (Get-EventLog -LogName "{{ win_test_log_source.log }}").Count + register: add_entry_all_options_count + failed_when: add_entry_all_options_count.stdout_lines[0] != "3" + when: not in_check_mode + +- name: Test add_entry_all_options result (normal mode) + test_win_eventlog_entry: + log: "{{ win_test_log_source.log }}" + register: add_entry_all_options_result + when: not in_check_mode + +- name: Test add_entry_all_options_result (normal mode) + assert: + that: + - add_entry_all_options_result.source == win_test_log_source.source + - add_entry_all_options_result.event_id == 500 + - add_entry_all_options_result.message == "This is a test error message" + - add_entry_all_options_result.entry_type == "Error" + - add_entry_all_options_result.category == 5 + - add_entry_all_options_result.raw_data == "10,20" + when: not in_check_mode + + +- name: Add entry all options (again) + win_eventlog_entry: *wele_ao + register: add_entry_all_options_again + +- name: Test add_entry_all_options_again (normal mode) + assert: + that: + - add_entry_all_options_again.changed == true + - add_entry_all_options_again.msg == "Entry added to log {{ win_test_log_source.log }} from source {{ win_test_log_source.source }}" + when: not in_check_mode + +- name: Test add_entry_all_options_again count (normal mode) + ansible.windows.win_shell: (Get-EventLog -LogName "{{ win_test_log_source.log }}").Count + register: add_entry_all_options_again_count + failed_when: add_entry_all_options_again_count.stdout_lines[0] != "4" + when: not in_check_mode + + +- name: Clear event log entries + ansible.windows.win_shell: Clear-EventLog -LogName "{{ win_test_log_source.log }}" + when: not in_check_mode diff --git a/ansible_collections/community/windows/tests/integration/targets/win_feature_info/aliases b/ansible_collections/community/windows/tests/integration/targets/win_feature_info/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_feature_info/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_feature_info/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_feature_info/defaults/main.yml new file mode 100644 index 000000000..01cc5ee55 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_feature_info/defaults/main.yml @@ -0,0 +1,2 @@ +--- +test_feature: Telnet-Client diff --git a/ansible_collections/community/windows/tests/integration/targets/win_feature_info/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_feature_info/tasks/main.yml new file mode 100644 index 000000000..a6fdb28f6 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_feature_info/tasks/main.yml @@ -0,0 +1,58 @@ +--- +- name: test we can get info for all features + win_feature_info: + register: all_actual + check_mode: yes # tests that this will run in check mode + +- name: assert test we can get info for all features + assert: + that: + - all_actual.exists + - not all_actual is changed + +- name: test info on a missing feature + win_feature_info: + name: ansible_feature_info_missing + register: missing_feature + +- name: assert test info on a missing feature + assert: + that: + - not missing_feature is changed + - not missing_feature.exists + +- name: Install Test Feature + ansible.windows.win_feature: + name: "{{ test_feature }}" + state: present + +- name: test info on a single Feature + win_feature_info: + name: '{{ test_feature }}' + register: specific_feature_present + +- name: assert test info on single feature + assert: + that: + - not specific_feature_present is changed + - specific_feature_present.exists + - specific_feature_present.features | length == 1 + - specific_feature_present.features[0].install_state == "Installed" + +- name: Uninstall Test Feature + ansible.windows.win_feature: + name: "{{ test_feature }}" + state: absent + +- name: test info on a single Feature + win_feature_info: + name: '{{ test_feature }}' + register: specific_feature_absent + +- name: assert test info on single feature + assert: + that: + - not specific_feature_absent is changed + - specific_feature_absent.exists + - specific_feature_absent.features | length == 1 + - specific_feature_absent.features[0].install_state == "Available" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_file_compression/aliases b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_file_compression/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/defaults/main.yml new file mode 100644 index 000000000..ae24afe7c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/defaults/main.yml @@ -0,0 +1,5 @@ +test_win_file_compression_suffix: win_file_compression .ÅÑŚÌβŁÈ [$!@^&test(;)] +test_win_file_compression_sub_directories: + - 'a' + - 'b' +test_win_file_compression_filename: 'foo.bar' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_file_compression/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_file_compression/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/tasks/main.yml new file mode 100644 index 000000000..542728bd7 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_file_compression/tasks/main.yml @@ -0,0 +1,224 @@ +--- +- name: set fact of special testing dir + set_fact: + test_directory: '{{ remote_tmp_dir }}\{{ test_win_file_compression_suffix }}' + +- name: create sub directories + ansible.windows.win_file: + state: directory + path: "{{ test_directory }}\\{{ item }}" + loop: "{{ test_win_file_compression_sub_directories }}" + +- name: set main directory as hidden to test out edge cases + ansible.windows.win_shell: (Get-Item -LiteralPath '{{ test_directory }}').Attributes = [System.IO.FileAttributes]::Hidden + +- name: Compress parent directory + win_file_compression: + path: "{{ test_directory }}" + state: present + register: result + +- name: Get actual attributes for parent directory + ansible.windows.win_stat: + path: "{{ test_directory }}" + register: folder_info + +- assert: + that: + - "'Compressed' in folder_info.stat.attributes" + - "result.changed == true" + +- name: Get actual attributes for sub directories + ansible.windows.win_stat: + path: "{{ test_directory }}\\{{ item }}" + register: subfolder_info + loop: "{{ test_win_file_compression_sub_directories }}" + +- assert: + that: + - "'Compressed' not in item.stat.attributes" + loop: "{{ subfolder_info.results }}" + +- name: Compress parent directory (idempotent) + win_file_compression: + path: "{{ test_directory }}" + state: present + register: result + +- assert: + that: + - "result.changed == false" + +- name: Compress parent directory and all subdirectories + win_file_compression: + path: "{{ test_directory }}" + state: present + recurse: yes + register: result + +- name: Get actual attributes for parent directory + ansible.windows.win_stat: + path: "{{ test_directory }}" + register: folder_info + +- assert: + that: + - "'Compressed' in folder_info.stat.attributes" + - "result.changed == true" + +- name: Get actual attributes for sub directories + ansible.windows.win_stat: + path: "{{ test_directory }}\\{{ item }}" + register: subfolder_info + loop: "{{ test_win_file_compression_sub_directories }}" + +- assert: + that: + - "'Compressed' in item.stat.attributes" + loop: "{{ subfolder_info.results }}" + +- name: Compress parent directory and all subdirectories (idempotent) + win_file_compression: + path: "{{ test_directory }}" + state: present + recurse: yes + register: result + +- assert: + that: + - "result.changed == false" + +- name: Uncompress parent directory + win_file_compression: + path: "{{ test_directory }}" + state: absent + recurse: no + register: result + +- name: Get actual attributes for parent directory + ansible.windows.win_stat: + path: "{{ test_directory }}" + register: folder_info + +- assert: + that: + - "'Compressed' not in folder_info.stat.attributes" + - "result.changed == true" + +- name: Get actual attributes for sub directories + ansible.windows.win_stat: + path: "{{ test_directory }}\\{{ item }}" + register: subfolder_info + loop: "{{ test_win_file_compression_sub_directories }}" + +- assert: + that: + - "'Compressed' in item.stat.attributes" + loop: "{{ subfolder_info.results }}" + +- name: Uncompress parent directory (idempotent) + win_file_compression: + path: "{{ test_directory }}" + state: absent + recurse: no + register: result + +- assert: + that: + - "result.changed == false" + +- name: Uncompress parent directory and all subdirectories + win_file_compression: + path: "{{ test_directory }}" + state: absent + recurse: yes + register: result + +- name: Get actual attributes for parent directory + ansible.windows.win_stat: + path: "{{ test_directory }}" + register: folder_info + +- assert: + that: + - "'Compressed' not in folder_info.stat.attributes" + - "result.changed == true" + +- name: Get actual attributes for sub directories + ansible.windows.win_stat: + path: "{{ test_directory }}\\{{ item }}" + register: subfolder_info + loop: "{{ test_win_file_compression_sub_directories }}" + +- assert: + that: + - "'Compressed' not in item.stat.attributes" + loop: "{{ subfolder_info.results }}" + +- name: Uncompress parent directory and all subdirectories (idempotent) + win_file_compression: + path: "{{ test_directory }}" + state: absent + recurse: yes + register: result + +- assert: + that: + - "result.changed == false" + +- name: Create test file + ansible.windows.win_file: + state: touch + path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}" + +- name: Compress specific file + win_file_compression: + path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}" + state: present + register: result + +- name: Get actual attributes of file + ansible.windows.win_stat: + path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}" + register: testfile_info + +- assert: + that: + - "result.changed == true" + - "'Compressed' in testfile_info.stat.attributes" + +- name: Compress specific file (idempotent) + win_file_compression: + path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}" + state: present + register: result + +- assert: + that: + - "result.changed == false" + +- name: Uncompress specific file + win_file_compression: + path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}" + state: absent + register: result + +- name: Get actual attributes of file + ansible.windows.win_stat: + path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}" + register: testfile_info + +- assert: + that: + - "result.changed == true" + - "'Compressed' not in testfile_info.stat.attributes" + +- name: Uncompress specific file (idempotent) + win_file_compression: + path: "{{ test_directory }}\\{{ test_win_file_compression_filename }}" + state: absent + register: result + +- assert: + that: + - "result.changed == false" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_firewall/aliases b/ansible_collections/community/windows/tests/integration/targets/win_firewall/aliases new file mode 100644 index 000000000..c8fd90a1f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_firewall/aliases @@ -0,0 +1,3 @@ +shippable/windows/group5 +skip/windows/2012 +skip/windows/2012-R2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_firewall/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_firewall/tasks/main.yml new file mode 100644 index 000000000..d1e4d89c4 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_firewall/tasks/main.yml @@ -0,0 +1,52 @@ +# NOTE: The win_firewall module only works on WMF 5+ + +- ansible.windows.setup: + +- name: Test Windows capabilities + raw: Get-Command Get-NetFirewallProfile -ErrorAction SilentlyContinue; return $? + failed_when: no + register: get_netfirewallprofile + +- name: Only run tests when Windows is capable + when: get_netfirewallprofile.rc == 0 and ansible_powershell_version >= 5 + block: + - name: Turn off Windows Firewall (begin) + win_firewall: + profiles: [ Domain, Private, Public ] + state: disabled + register: firewall_off + + - name: Test firewall_off + assert: + that: + - not firewall_off.Domain.enabled + - not firewall_off.Private.enabled + - not firewall_off.Public.enabled + + + - name: Test in normal mode + import_tasks: tests.yml + vars: + in_check_mode: no + + + - name: Test in check-mode + import_tasks: tests.yml + vars: + in_check_mode: yes + check_mode: yes + + + - name: Turn on Windows Firewall (end) + win_firewall: + profiles: [ Domain, Private, Public ] + state: enabled + register: firewall_on + + - name: Test firewall_on + assert: + that: + - firewall_on is changed + - firewall_on.Domain.enabled + - firewall_on.Private.enabled + - firewall_on.Public.enabled diff --git a/ansible_collections/community/windows/tests/integration/targets/win_firewall/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_firewall/tasks/tests.yml new file mode 100644 index 000000000..80b5f1553 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_firewall/tasks/tests.yml @@ -0,0 +1,268 @@ +# We start with firewall turned off + +- name: Turn off Windows Firewall again + win_firewall: + profiles: [ Domain, Private, Public ] + state: disabled + register: firewall_off_again + +- name: Test firewall_off_again + assert: + that: + - firewall_off_again is not changed + - not firewall_off_again.Domain.enabled + - not firewall_off_again.Private.enabled + - not firewall_off_again.Public.enabled + +- name: Turn on Windows Firewall on Public + win_firewall: + profiles: [ Public ] + state: enabled + register: firewall_public_on + +- name: Test firewall_public_on + assert: + that: + - firewall_public_on is changed + - not firewall_public_on.Domain.enabled + - not firewall_public_on.Private.enabled + - firewall_public_on.Public.enabled + + +- name: Turn on Windows Firewall on Public again + win_firewall: + profiles: [ Public ] + state: enabled + register: firewall_public_on_again + +- name: Test firewall_public_on_again (normal mode) + assert: + that: + - firewall_public_on_again is not changed + - not firewall_public_on_again.Domain.enabled + - not firewall_public_on_again.Private.enabled + - firewall_public_on_again.Public.enabled + when: not in_check_mode + +- name: Test firewall_public_on_again (check-mode) + assert: + that: + - firewall_public_on_again is changed + - not firewall_public_on_again.Domain.enabled + - not firewall_public_on_again.Private.enabled + - firewall_public_on_again.Public.enabled + when: in_check_mode + + +# On purpose not a list +- name: Turn on Windows Firewall on Domain + win_firewall: + profiles: Domain + state: enabled + register: firewall_domain_on + +- name: Test firewall_domain_on (normal mode) + assert: + that: + - firewall_domain_on is changed + - firewall_domain_on.Domain.enabled + - not firewall_domain_on.Private.enabled + - firewall_domain_on.Public.enabled + when: not in_check_mode + +- name: Test firewall_domain_on (check-mode) + assert: + that: + - firewall_domain_on is changed + - firewall_domain_on.Domain.enabled + - not firewall_domain_on.Private.enabled + - not firewall_domain_on.Public.enabled + when: in_check_mode + + +- name: Turn on Windows Firewall on Domain again + win_firewall: + profiles: [ Domain ] + state: enabled + register: firewall_domain_on_again + +- name: Test firewall_domain_on_again (normal mode) + assert: + that: + - firewall_domain_on_again is not changed + - firewall_domain_on.Domain.enabled + - not firewall_domain_on.Private.enabled + - firewall_domain_on.Public.enabled + when: not in_check_mode + +- name: Test firewall_domain_on_again (check-mode) + assert: + that: + - firewall_domain_on_again is changed + - firewall_domain_on.Domain.enabled + - not firewall_domain_on.Private.enabled + - not firewall_domain_on.Public.enabled + when: in_check_mode + + +- name: Turn on Windows Firewall + win_firewall: + profiles: [ Domain, Private, Public ] + state: enabled + register: firewall_on + +- name: Test firewall_on + assert: + that: + - firewall_on is changed + - firewall_on.Domain.enabled + - firewall_on.Private.enabled + - firewall_on.Public.enabled + +- name: Turn on Windows Firewall on Domain with allow inbound connection + win_firewall: + profiles: Domain + state: enabled + inbound_action: allow + register: firewall_domain_on + +- name: Test firewall_domain_on (normal mode) + assert: + that: + - firewall_domain_on is changed + - firewall_domain_on.Domain.enabled + when: not in_check_mode + +- name: Test firewall_domain_on (check-mode) + assert: + that: + - firewall_domain_on is changed + - firewall_domain_on.Domain.enabled + when: in_check_mode + +- name: Turn on Windows Firewall on Domain again with allow inbound + win_firewall: + profiles: [ Domain ] + state: enabled + inbound_action: allow + register: firewall_domain_on_again + +- name: Test firewall_domain_on_again (normal mode) + assert: + that: + - firewall_domain_on_again is not changed + - firewall_domain_on.Domain.enabled + when: not in_check_mode + +- name: Test firewall_domain_on_again (check-mode) + assert: + that: + - firewall_domain_on_again is changed + - firewall_domain_on.Domain.enabled + when: in_check_mode + +- name: Turn on Windows Firewall on Domain with block outbound connection + win_firewall: + profiles: Domain + state: enabled + outbound_action: block + register: firewall_domain_on + +- name: Test firewall_domain_on (normal mode) + assert: + that: + - firewall_domain_on is changed + - firewall_domain_on.Domain.enabled + when: not in_check_mode + +- name: Test firewall_domain_on (check-mode) + assert: + that: + - firewall_domain_on is changed + - firewall_domain_on.Domain.enabled + when: in_check_mode + +- name: Turn on Windows Firewall on Domain again with block outbound connection + win_firewall: + profiles: [ Domain ] + state: enabled + outbound_action: block + register: firewall_domain_on_again + +- name: Test firewall_domain_on_again (normal mode) + assert: + that: + - firewall_domain_on_again is not changed + - firewall_domain_on.Domain.enabled + when: not in_check_mode + +- name: Test firewall_domain_on_again (check-mode) + assert: + that: + - firewall_domain_on_again is changed + - firewall_domain_on.Domain.enabled + when: in_check_mode + +# On purpose no profiles added +- name: Turn on Windows Firewall again + win_firewall: + state: enabled + register: firewall_on_again + +- name: Test firewall_on_again (normal mode) + assert: + that: + - firewall_on_again is not changed + - firewall_on_again.Domain.enabled + - firewall_on_again.Private.enabled + - firewall_on_again.Public.enabled + when: not in_check_mode + +- name: Test firewall_on_again (check-mode) + assert: + that: + - firewall_on_again is changed + - firewall_on_again.Domain.enabled + - firewall_on_again.Private.enabled + - firewall_on_again.Public.enabled + when: in_check_mode + + +# On purpose no profiles added +- name: Turn off Windows Firewall + win_firewall: + state: disabled + register: firewall_off2 + +- name: Test firewall_off2 (normal mode) + assert: + that: + - firewall_off2 is changed + - not firewall_off2.Domain.enabled + - not firewall_off2.Private.enabled + - not firewall_off2.Public.enabled + when: not in_check_mode + +- name: Test firewall_off2 (check-mode) + assert: + that: + - firewall_off2 is not changed + - not firewall_off2.Domain.enabled + - not firewall_off2.Private.enabled + - not firewall_off2.Public.enabled + when: in_check_mode + + +- name: Turn off Windows Firewall again + win_firewall: + profiles: [ Domain, Private, Public ] + state: disabled + register: firewall_off2_again + +- name: Test firewall_off2_again (normal mode) + assert: + that: + - firewall_off2_again is not changed + - not firewall_off2_again.Domain.enabled + - not firewall_off2_again.Private.enabled + - not firewall_off2_again.Public.enabled diff --git a/ansible_collections/community/windows/tests/integration/targets/win_firewall_rule/aliases b/ansible_collections/community/windows/tests/integration/targets/win_firewall_rule/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_firewall_rule/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_firewall_rule/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_firewall_rule/tasks/main.yml new file mode 100644 index 000000000..21fe38196 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_firewall_rule/tasks/main.yml @@ -0,0 +1,609 @@ +- name: Remove potentially leftover firewall rule + win_firewall_rule: + name: http + state: absent + action: allow + direction: in + +- name: Add firewall rule + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + register: add_firewall_rule + +- name: Check that creating new firewall rule succeeds with a change + assert: + that: + - add_firewall_rule.changed == true + +- name: Add same firewall rule (again) + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + register: add_firewall_rule_again + +- name: Check that creating same firewall rule succeeds without a change + assert: + that: + - add_firewall_rule_again.changed == false + +- name: Remove firewall rule + win_firewall_rule: + name: http + enabled: yes + state: absent + localport: 80 + action: allow + direction: in + protocol: tcp + register: remove_firewall_rule + +- name: Check that removing existing firewall rule succeeds with a change + assert: + that: + - remove_firewall_rule.changed == true + +- name: Remove absent firewall rule + win_firewall_rule: + name: http + enabled: yes + state: absent + localport: 80 + action: allow + direction: in + protocol: tcp + register: remove_absent_firewall_rule + +- name: Check that removing non existing firewall rule succeeds without a change + assert: + that: + - remove_absent_firewall_rule.changed == false + +- name: Add firewall rule + win_firewall_rule: + name: http + enabled: no + state: present + group: application + localport: 80 + action: allow + direction: in + protocol: tcp + register: add_firewall_rule + +- name: Check that creating new firewall rule succeeds with a change + assert: + that: + - add_firewall_rule.changed == true + +- name: Enable all the rules in application group + win_firewall_rule: + group: application + enabled: yes + register: change_firewall_rule + +- name: Check if the rules are enabled in application group + assert: + that: + - change_firewall_rule.changed == true + +- name: Enable all the rules in application group (again) + win_firewall_rule: + group: application + enabled: yes + register: change_firewall_rule + +- name: Check if the rules are enabled without a change + assert: + that: + - change_firewall_rule.changed == false + +- name: Disable all the rules in application group + win_firewall_rule: + group: application + enabled: no + register: change_firewall_rule + +- name: Check if the rules are disabled + assert: + that: + - change_firewall_rule.changed == true + +- name: Disable all the rules in application group (again) + win_firewall_rule: + group: application + enabled: no + register: change_firewall_rule + +- name: Check if the rules are disabled without a change + assert: + that: + - change_firewall_rule.changed == false + +- name: Remove firewall rule + win_firewall_rule: + name: http + enabled: no + state: absent + localport: 80 + action: allow + group: application + direction: in + protocol: tcp + register: remove_firewall_rule + +- name: Check that removing existing firewall rule succeeds with a change + assert: + that: + - remove_firewall_rule.changed == true + +- name: Add firewall rule + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + +- name: Change firewall rule + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: block + direction: in + protocol: tcp + register: change_firewall_rule + +- name: Check that changing firewall rule succeeds + assert: + that: + - change_firewall_rule.changed == true + +- name: Disable firewall rule + win_firewall_rule: + name: http + enabled: no + +- name: Get the actual values from the changed firewall rule + ansible.windows.win_shell: '(New-Object -ComObject HNetCfg.FwPolicy2).Rules | Where-Object { $_.Name -eq "http" } | Foreach-Object { $_.LocalPorts; $_.Enabled; $_.Action; $_.Direction; $_.Protocol }' + register: firewall_rule_actual + +- name: Ensure that disabling the rule did not change the previous values + assert: + that: + - "firewall_rule_actual.stdout_lines[0] == '80'" # LocalPorts = 80 + - "firewall_rule_actual.stdout_lines[1] == 'False'" # Enabled = False + - "firewall_rule_actual.stdout_lines[2] == '0'" # Action = block + - "firewall_rule_actual.stdout_lines[3] == '1'" # Direction = in + - "firewall_rule_actual.stdout_lines[4] == '6'" # Protocol = tcp + +- name: Add firewall rule when remoteip is range + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.0.1-192.168.0.5 + action: allow + direction: in + protocol: tcp + +- name: Add same firewall rule when remoteip is range (again) + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.0.1-192.168.0.5 + action: allow + direction: in + protocol: tcp + register: add_firewall_rule_with_range_remoteip_again + +- name: Check that creating same firewall rule when remoteip is range succeeds without a change + assert: + that: + - add_firewall_rule_with_range_remoteip_again.changed == false + +- name: Add firewall rule when remoteip in CIDR notation + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.0.0/24 + action: allow + direction: in + protocol: tcp + +- name: Add same firewall rule when remoteip in CIDR notation (again) + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.0.0/24 + action: allow + direction: in + protocol: tcp + register: add_firewall_rule_with_cidr_remoteip_again + +- name: Check that creating same firewall rule succeeds without a change when remoteip in CIDR notation + assert: + that: + - add_firewall_rule_with_cidr_remoteip_again.changed == false + +- name: Add firewall rule when remoteip contains a netmask + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.1.0/255.255.255.0 + action: allow + direction: in + protocol: tcp + +- name: Add same firewall rule when remoteip contains a netmask (again) + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.1.0/255.255.255.0 + action: allow + direction: in + protocol: tcp + register: add_firewall_rule_remoteip_contains_netmask_again + +- name: Check that creating same firewall rule succeeds without a change when remoteip contains a netmask + assert: + that: + - add_firewall_rule_remoteip_contains_netmask_again.changed == false + +- name: Add firewall rule when remoteip is IPv4 + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.0.1 + action: allow + direction: in + protocol: tcp + +- name: Add same firewall rule when remoteip is IPv4 (again) + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.0.1 + action: allow + direction: in + protocol: tcp + register: add_firewall_rule_with_ipv4_remoteip_again + +- name: Check that creating same firewall rule when remoteip is IPv4 succeeds without a change + assert: + that: + - add_firewall_rule_with_ipv4_remoteip_again.changed == false + +- name: Add firewall rule when remoteip contains a netmask + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.2.0/255.255.255.0 + action: allow + direction: in + protocol: tcp + +- name: Add same firewall rule when remoteip in CIDR notation + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + remoteip: 192.168.2.0/24 + action: allow + direction: in + protocol: tcp + register: add_same_firewall_rule_with_cidr_remoteip + +- name: Check that creating same firewall rule succeeds without a change when remoteip contains a netmask or CIDR + assert: + that: + - add_same_firewall_rule_with_cidr_remoteip.changed == false + +- name: Add firewall rule with multiple ports + win_firewall_rule: + name: http + enabled: yes + state: present + localport: '80,81' + action: allow + direction: in + protocol: tcp + register: add_firewall_rule_with_multiple_ports + +- name: Check that creating firewall rule with multiple ports succeeds with a change + assert: + that: + - add_firewall_rule_with_multiple_ports.changed == true + +- name: Add firewall rule with interface types in string format + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + interfacetypes: 'ras,lan,wireless' + register: add_firewall_rule_with_string_interface_types + +- name: Check that creating firewall rule with interface types in string format succeeds with a change + assert: + that: + - add_firewall_rule_with_string_interface_types.changed == true + +- name: Add firewall rule with interface types in list format + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + interfacetypes: [ras, lan] + register: add_firewall_rule_with_list_interface_types + +- name: Check that creating firewall rule with interface types in list format succeeds with a change + assert: + that: + - add_firewall_rule_with_list_interface_types.changed == true + +- name: Add firewall rule with interface type 'any' + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + interfacetypes: any + register: add_firewall_rule_with_interface_type_any + +- name: Check that creating firewall rule with interface type 'any' succeeds with a change + assert: + that: + - add_firewall_rule_with_interface_type_any.changed == true + +- name: Add firewall rule with edge traversal option 'deferapp' + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + edge: deferapp + register: add_firewall_rule_with_edge_traversal + +# Setup action creates ansible_distribution_version variable +- ansible.windows.setup: + +- name: Check that creating firewall rule with enge traversal option 'deferapp' succeeds with a change + assert: + that: + - add_firewall_rule_with_edge_traversal.changed == true + # Works on windows >= Windows 7/Windows Server 2008 R2 + when: ansible_distribution_version is version('6.1', '>=') + +- name: Add firewall rule with 'authenticate' secure flag + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + security: authenticate + register: add_firewall_rule_with_secure_flags + +- name: Check that creating firewall rule with secure flag 'authenticate' succeeds with a change + assert: + that: + - add_firewall_rule_with_secure_flags.changed == true + # Works on windows >= Windows 8/Windows Server 2012 + when: ansible_distribution_version is version('6.2', '>=') + +- name: Add firewall rule with profiles in string format + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + profiles: 'domain,public' + register: add_firewall_rule_with_string_profiles + +- name: Check that creating firewall rule with profiles in string format succeeds with a change + assert: + that: + - add_firewall_rule_with_string_profiles.changed == true + +- name: Set firewall rule profile back to 'all' + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + profiles: [Domain, Public, Private] + register: add_firewall_rule_with_string_profiles + +- name: Check that setting firewall rule profile back to 'all' succeeds with a change + assert: + that: + - add_firewall_rule_with_string_profiles.changed == true + +- name: Add firewall rule with profiles in list format + win_firewall_rule: + name: http + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + profiles: [Domain, Private] + register: add_firewall_rule_with_list_profiles + +- name: Check that creating firewall rule with profiles in list format succeeds with a change + assert: + that: + - add_firewall_rule_with_list_profiles.changed == true + +# Test for variable expansion in the path +- name: Add rule with path that needs to be expanded + win_firewall_rule: + name: VarExpansionTest + enabled: yes + state: present + action: allow + direction: in + protocol: tcp + program: '%SystemRoot%\system32\svchost.exe' + +- name: Add same rule with path that needs to be expanded + win_firewall_rule: + name: VarExpansionTest + enabled: yes + state: present + action: allow + direction: in + protocol: tcp + program: '%SystemRoot%\system32\svchost.exe' + register: add_firewall_rule_with_var_expand_path + +- name: Check that creating same firewall rule with expanded vars identified + assert: + that: + - add_firewall_rule_with_var_expand_path.changed == false + +- name: Add firewall rule for application group + win_firewall_rule: + name: Rule for application group + enabled: yes + state: present + localport: 80 + action: allow + direction: in + protocol: tcp + group: application + register: add_firewall_rule_with_group + +- name: Check that creating firewall rule for application group succeeds with a change + assert: + that: + - add_firewall_rule_with_group.changed == true + +# Test icmptypecode +- name: Add rule with icmptypecode + win_firewall_rule: + name: icmptest + enabled: yes + state: present + action: allow + direction: in + protocol: icmpv4 + icmp_type_code: '8:*' + register: add_firewall_rule_with_icmptypecode + +- name: Check that creating same firewall rule with expanded vars identified + assert: + that: + - add_firewall_rule_with_icmptypecode.changed == true + +- name: Remove rule with icmptypecode + win_firewall_rule: + name: icmptest + enabled: yes + state: absent + action: allow + direction: in + protocol: icmpv4 + icmp_type_code: '8:*' + register: remove_firewall_rule_with_icmptypecode + +- name: Check that removing same firewall rule with expanded vars identified + assert: + that: + - remove_firewall_rule_with_icmptypecode.changed == true + +# test for application name / program changes to any (and if they null the property) +# ----------------------------------------------------------------------------- +- name: Add rule with an accociated program + win_firewall_rule: + name: ApplicationToAnyTest + enabled: true + state: present + action: allow + direction: in + protocol: tcp + program: '%SystemRoot%\system32\svchost.exe' + register: add_firewall_rule_with_program + +- name: Check that creating firewall rule succeeds with a change + ansible.builtin.assert: + that: + - add_firewall_rule_with_program.changed == true + +- name: Add same rule with program set to any + win_firewall_rule: + name: ApplicationToAnyTest + enabled: true + state: present + action: allow + direction: in + protocol: tcp + program: any + register: change_firewall_rule_with_program + +- name: Check that changing firewall rule succeeds with a change + ansible.builtin.assert: + that: + - change_firewall_rule_with_program.changed == true + +- name: Get the actual values from the changed firewall rule and check if ApplicationName is null + ansible.windows.win_shell: >- + ((New-Object -ComObject HNetCfg.FwPolicy2).Rules | Where-Object { $_.Name -eq "ApplicationToAnyTest" } | Foreach-Object { $_.ApplicationName }) -eq $null + register: firewall_rule_actual + failed_when: 'firewall_rule_actual.stdout_lines[0] != "True"' +# ----------------------------------------------------------------------------- diff --git a/ansible_collections/community/windows/tests/integration/targets/win_format/aliases b/ansible_collections/community/windows/tests/integration/targets/win_format/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_format/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_format/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_format/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_format/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/main.yml new file mode 100644 index 000000000..5ea27a6f0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- name: Check if Format-Volume is supported + ansible.windows.win_shell: if (Get-Command -Name Format-Volume -ErrorAction SilentlyContinue) { $true } else { $false } + register: module_present + +- include: pre_test.yml + when: module_present.stdout | trim | bool diff --git a/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/pre_test.yml b/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/pre_test.yml new file mode 100644 index 000000000..a29a47bbe --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/pre_test.yml @@ -0,0 +1,21 @@ +--- +- set_fact: + AnsibleVhdx: '{{ remote_tmp_dir }}\AnsiblePart.vhdx' + +- name: Copy VHDX scripts + ansible.windows.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 + ansible.windows.win_command: diskpart.exe /s {{ remote_tmp_dir }}\partition_creation_script.txt + +- name: Run tests + block: + - include: tests.yml + always: + - name: Detach disk + ansible.windows.win_command: diskpart.exe /s {{ remote_tmp_dir }}\partition_deletion_script.txt diff --git a/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/tests.yml new file mode 100644 index 000000000..1383c6f9c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_format/tasks/tests.yml @@ -0,0 +1,182 @@ +--- +- ansible.windows.win_shell: $AnsiPart = Get-Partition -DriveLetter T; $AnsiVol = Get-Volume -DriveLetter T; "$($AnsiPart.Size),$($AnsiVol.Size)" + register: shell_result + +- name: Assert volume size is 0 for pristine volume + assert: + that: + - shell_result.stdout | trim == "2096037888,0" + +- name: Get partition access path + ansible.windows.win_shell: (Get-Partition -DriveLetter T).AccessPaths[1] + register: shell_partition_result + +- name: Try to format using mutually exclusive parameters + win_format: + drive_letter: T + path: "{{ shell_partition_result.stdout | trim }}" + register: format_mutex_result + ignore_errors: True + +- assert: + that: + - format_mutex_result is failed + - 'format_mutex_result.msg == "parameters are mutually exclusive: drive_letter, path, label"' + +- name: Fully format volume and assign label (check) + win_format: + drive_letter: T + new_label: Formatted + full: True + allocation_unit_size: 8192 + register: format_result_check + check_mode: True + +- ansible.windows.win_shell: $AnsiPart = Get-Partition -DriveLetter T; $AnsiVol = Get-Volume -DriveLetter T; "$($AnsiPart.Size),$($AnsiVol.Size),$($AnsiVol.FileSystemLabel),$((Get-CimInstance -ClassName Win32_Volume -Filter "DriveLetter = 'T:'" -Property BlockSize).BlockSize)" + register: formatted_value_result_check + +- name: Fully format volume and assign label + win_format: + drive_letter: T + new_label: Formatted + full: True + allocation_unit_size: 8192 + register: format_result + +- ansible.windows.win_shell: $AnsiPart = Get-Partition -DriveLetter T; $AnsiVol = Get-Volume -DriveLetter T; "$($AnsiPart.Size),$($AnsiVol.Size),$($AnsiVol.FileSystemLabel),$((Get-CimInstance -ClassName Win32_Volume -Filter "DriveLetter = 'T:'" -Property BlockSize).BlockSize)" + register: formatted_value_result + +- assert: + that: + - format_result_check is changed + - format_result is changed + - formatted_value_result_check.stdout | trim == "2096037888,0,," + - formatted_value_result.stdout | trim == "2096037888,2096029696,Formatted,8192" + +- name: Format NTFS volume with integrity streams enabled + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: ntfs + integrity_streams: True + ignore_errors: True + register: ntfs_integrity_streams + +- assert: + that: + - ntfs_integrity_streams is failed + - 'ntfs_integrity_streams.msg == "Integrity streams can be enabled only on ReFS volumes. You specified: ntfs"' + +- name: Format volume (require force_format for specifying different file system) + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: fat32 + ignore_errors: True + register: require_force_format + +- assert: + that: + - require_force_format is failed + - 'require_force_format.msg == "Force format must be specified since target file system: fat32 is different from the current file system of the volume: ntfs"' + +- name: Format volume (forced) (check) + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: refs + force: True + check_mode: True + ignore_errors: True + register: not_pristine_forced_check + +- name: Format volume (forced) + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: refs + force: True + register: not_pristine_forced + +- name: Format volume (forced) (idempotence will not work) + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: refs + force: True + register: not_pristine_forced_idem_fails + +- name: Format volume (idempotence) + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: refs + register: not_pristine_forced_idem + +- assert: + that: + - not_pristine_forced_check is changed + - not_pristine_forced is changed + - not_pristine_forced_idem_fails is changed + - not_pristine_forced_idem is not changed + +- name: Add a file + ansible.windows.win_file: + path: T:\path\to\directory + state: directory + +- name: Format volume with file inside without force and same fs + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + register: format_volume_without_force_same_fs + +- name: Format volume (forced) - to test case for files existing and a different fs + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: ntfs + force: True + +- name: Add a file + ansible.windows.win_file: + path: T:\path\to\directory + state: directory + register: add_file_to_volume + +- name: Format volume with file inside without force + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + file_system: refs + register: format_volume_without_force + ignore_errors: True + +- name: Format volume with file inside with force + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + force: True + register: format_volume_with_force + +- assert: + that: + - add_file_to_volume is changed + - format_volume_without_force is failed + - format_volume_without_force_same_fs is not changed + - 'format_volume_without_force.msg == "Force format must be specified to format non-pristine volumes"' + - format_volume_with_force is changed + +- name: Reformat using different alu without force format + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + allocation_unit_size: 8192 + file_system: ntfs + register: reformat_using_alu_without_force + ignore_errors: True + +- assert: + that: + - reformat_using_alu_without_force is failed + +- name: Reformat using different alu using force format + win_format: + path: "{{ shell_partition_result.stdout | trim }}" + allocation_unit_size: 8192 + file_system: ntfs + force: True + register: reformat_using_alu_with_force + +- assert: + that: + - reformat_using_alu_with_force is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_format/templates/partition_creation_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_format/templates/partition_creation_script.j2 new file mode 100644 index 000000000..8e47fda95 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_format/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/ansible_collections/community/windows/tests/integration/targets/win_format/templates/partition_deletion_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_format/templates/partition_deletion_script.j2 new file mode 100644 index 000000000..c2be9cd14 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_format/templates/partition_deletion_script.j2 @@ -0,0 +1,3 @@ +select vdisk file="{{ AnsibleVhdx }}" + +detach vdisk diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hosts/aliases b/ansible_collections/community/windows/tests/integration/targets/win_hosts/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hosts/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hosts/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_hosts/defaults/main.yml new file mode 100644 index 000000000..c6270216d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/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/ansible_collections/community/windows/tests/integration/targets/win_hosts/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_hosts/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hosts/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hosts/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_hosts/tasks/main.yml new file mode 100644 index 000000000..02a8b873e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hosts/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- name: take a copy of the original hosts file + ansible.windows.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 + ansible.windows.win_copy: + src: '{{ remote_tmp_dir }}\hosts' + dest: C:\Windows\System32\drivers\etc\hosts + remote_src: yes diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hosts/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_hosts/tasks/tests.yml new file mode 100644 index 000000000..5ced7ba26 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/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 + ansible.windows.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 + ansible.windows.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 + ansible.windows.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 + ansible.windows.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 + ansible.windows.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 + ansible.windows.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 + ansible.windows.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/ansible_collections/community/windows/tests/integration/targets/win_hotfix/aliases b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/aliases new file mode 100644 index 000000000..11addc63b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/aliases @@ -0,0 +1,2 @@ +shippable/windows/group4 +unstable diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hotfix/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/defaults/main.yml new file mode 100644 index 000000000..22edea7c1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/defaults/main.yml @@ -0,0 +1,13 @@ +--- +# these hotfixes, are for Hyper-V, there may be a chance the system already has them +# but in most cases for our CI purposes they wouldn't be present +test_win_hotfix_good_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_hotfix/windows8.1-kb3027108-v2-x64_66366c7be2d64d83b63cac42bc40c0a3c01bc70d.msu +test_win_hotfix_reboot_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_hotfix/windows8.1-kb2913659-v2-x64_963a4d890c9ff9cc83a97cf54305de6451038ba4.msu +test_win_hotfix_bad_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_hotfix/windows8-rt-kb3172729-x64_69cab4c7785b1faa3fc450f32bed4873d53bb96f.msu +test_win_hotfix_path: C:\ansible\win_hotfix + +test_win_hotfix_kb: KB3027108 +test_win_hotfix_identifier: Package_for_KB3027108~31bf3856ad364e35~amd64~~6.3.2.0 + +test_win_hotfix_reboot_kb: KB2913659 +test_win_hotfix_reboot_identifier: Package_for_KB2913659~31bf3856ad364e35~amd64~~6.3.2.0 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/main.yml new file mode 100644 index 000000000..47b2d3056 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: filter servers that can support DISM + ansible.windows.win_command: powershell.exe "Import-Module -Name DISM" + register: eligable_servers + ignore_errors: True + +- name: fail to run module on servers that don't support DISM + win_hotfix: + path: fake + state: present + register: fail_no_dism + failed_when: fail_no_dism.msg != 'The DISM PS module needs to be installed, this can be done through the windows-adk chocolately package' + when: eligable_servers.rc != 0 + +- name: run tests on hosts that support DISM + include_tasks: tests.yml + when: eligable_servers.rc == 0 + +- name: set output to true if running Server 2012 R2 + ansible.windows.win_command: powershell.exe "$version = [Environment]::OSVersion.Version; if ($version.Major -eq 6 -and $version.Minor -eq 3) { 'true' } else { 'false' }" + register: test_hotfix + +- block: + - name: ensure hotfixes are uninstalled before tests + win_hotfix: + hotfix_identifier: '{{item}}' + state: absent + register: pre_uninstall + with_items: + - '{{test_win_hotfix_identifier}}' + - '{{test_win_hotfix_reboot_identifier}}' + + - name: reboot after pre test uninstall if required + ansible.windows.win_reboot: + when: pre_uninstall.results[0].reboot_required == True or pre_uninstall.results[1].reboot_required == True + + - name: run actual hotfix tests on Server 2012 R2 only + include_tasks: tests_2012R2.yml + + always: + - name: ensure hotfixes are uninstalled after tests + win_hotfix: + hotfix_identifier: '{{item}}' + state: absent + register: post_uninstall + with_items: + - '{{test_win_hotfix_identifier}}' + - '{{test_win_hotfix_reboot_identifier}}' + + - name: reboot after post test uninstall if required + ansible.windows.win_reboot: + when: post_uninstall.results[0].reboot_required == True or post_uninstall.results[1].reboot_required == True + + when: test_hotfix.stdout_lines[0] == "true" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/tests.yml new file mode 100644 index 000000000..8e7a7df37 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/tests.yml @@ -0,0 +1,35 @@ +# only basic tests, doesn't actually install/uninstall and hotfixes +--- +- name: fail when source isn't set + win_hotfix: + state: present + register: fail_no_source + failed_when: fail_no_source.msg != 'source must be set when state=present' + +- name: fail when identifier or kb isn't set on absent + win_hotfix: + state: absent + register: fail_no_key + failed_when: fail_no_key.msg != 'either hotfix_identifier or hotfix_kb needs to be set when state=absent' + +- name: remove an identifier that isn't installed + win_hotfix: + hotfix_identifier: fake~identifier + state: absent + register: remove_missing_hotfix_identifier + +- name: assert remove an identifier that isn't installed + assert: + that: + - remove_missing_hotfix_identifier is not changed + +- name: remove a kb that isn't installed + win_hotfix: + hotfix_kb: KB123456 + state: absent + register: remove_missing_hotfix_kb + +- name: assert remove a kb that isn't installed + assert: + that: + - remove_missing_hotfix_kb is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/tests_2012R2.yml b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/tests_2012R2.yml new file mode 100644 index 000000000..14ff38ec5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_hotfix/tasks/tests_2012R2.yml @@ -0,0 +1,265 @@ +--- +- name: create test staging folder + ansible.windows.win_file: + path: '{{test_win_hotfix_path}}' + state: directory + +- name: download hotfix + ansible.windows.win_get_url: + url: '{{test_win_hotfix_good_url}}' + dest: '{{test_win_hotfix_path}}\good.msu' + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + +- name: download reboot hotfix + ansible.windows.win_get_url: + url: '{{test_win_hotfix_reboot_url}}' + dest: '{{test_win_hotfix_path}}\reboot.msu' + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + +- name: download bad hotfix + ansible.windows.win_get_url: + url: '{{test_win_hotfix_bad_url}}' + dest: '{{test_win_hotfix_path}}\bad.msu' + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + +- name: fail install install hotfix where kb doesn't match + win_hotfix: + hotfix_kb: KB0000000 + source: '{{test_win_hotfix_path}}\good.msu' + state: present + register: fail_install_invalid_kb + failed_when: fail_install_invalid_kb.msg != 'the hotfix KB KB0000000 does not match with the source msu KB ' + test_win_hotfix_kb + ', please omit or specify the correct KB to continue' + +- name: fail install install hotfix where identifier doesn't match + win_hotfix: + hotfix_identifier: invalid + source: '{{test_win_hotfix_path}}\good.msu' + state: present + register: fail_install_invalid_identifier + failed_when: fail_install_invalid_identifier.msg != 'the hotfix identifier invalid does not match with the source msu identifier ' + test_win_hotfix_identifier + ', please omit or specify the correct identifier to continue' + +- name: fail install not applicable hotfix + win_hotfix: + source: '{{test_win_hotfix_path}}\bad.msu' + state: present + register: fail_install_not_applicable + failed_when: fail_install_not_applicable.msg != 'hotfix package is not applicable for this server' + +- name: install hotfix check + win_hotfix: + source: '{{test_win_hotfix_path}}\good.msu' + state: present + register: install_hotfix_check + check_mode: yes + +- name: get result of install hotfix check + ansible.windows.win_command: powershell.exe Get-Hotfix -Id {{test_win_hotfix_kb}} + register: install_hotfix_actual_check + ignore_errors: True + +- name: assert install hotfix check + assert: + that: + - install_hotfix_check is changed + - install_hotfix_check.kb == test_win_hotfix_kb + - install_hotfix_check.identifier == test_win_hotfix_identifier + - install_hotfix_actual_check.rc != 0 + +- name: install hotfix + win_hotfix: + source: '{{test_win_hotfix_path}}\good.msu' + state: present + register: install_hotfix + +- name: get result of install hotfix + ansible.windows.win_command: powershell.exe Get-Hotfix -Id {{test_win_hotfix_kb}} + register: install_hotfix_actual + +- name: assert install hotfix + assert: + that: + - install_hotfix is changed + - install_hotfix.kb == test_win_hotfix_kb + - install_hotfix.identifier == test_win_hotfix_identifier + - install_hotfix.reboot_required == False + - install_hotfix_actual.rc == 0 + +- name: install hotfix again + win_hotfix: + source: '{{test_win_hotfix_path}}\good.msu' + state: present + register: install_hotfix_again + +- name: assert install hotfix again + assert: + that: + - install_hotfix_again is not changed + - install_hotfix_again.kb == test_win_hotfix_kb + - install_hotfix_again.identifier == test_win_hotfix_identifier + - install_hotfix_again.reboot_required == False + +- name: uninstall hotfix check + win_hotfix: + hotfix_identifier: '{{test_win_hotfix_identifier}}' + state: absent + register: uninstall_hotfix_check + check_mode: yes + +- name: get result of uninstall hotfix check + ansible.windows.win_command: powershell.exe Get-Hotfix -Id {{test_win_hotfix_kb}} + register: uninstall_hotfix_actual_check + +- name: assert uninstall hotfix check + assert: + that: + - uninstall_hotfix_check is changed + - uninstall_hotfix_check.kb == test_win_hotfix_kb + - uninstall_hotfix_check.identifier == test_win_hotfix_identifier + - uninstall_hotfix_actual_check.rc == 0 + +- name: uninstall hotfix + win_hotfix: + hotfix_identifier: '{{test_win_hotfix_identifier}}' + state: absent + register: uninstall_hotfix + +- name: get result of uninstall hotfix + ansible.windows.win_command: powershell.exe Get-Hotfix -Id {{test_win_hotfix_kb}} + register: uninstall_hotfix_actual + ignore_errors: True + +- name: assert uninstall hotfix + assert: + that: + - uninstall_hotfix is changed + - uninstall_hotfix.kb == test_win_hotfix_kb + - uninstall_hotfix.identifier == test_win_hotfix_identifier + - uninstall_hotfix.reboot_required == False + - uninstall_hotfix_actual.rc != 0 + +- name: uninstall hotfix again + win_hotfix: + hotfix_identifier: '{{test_win_hotfix_identifier}}' + state: absent + register: uninstall_hotfix_again + +- name: assert uninstall hotfix again + assert: + that: + - uninstall_hotfix_again is not changed + - uninstall_hotfix_again.reboot_required == False + +- name: install reboot hotfix + win_hotfix: + hotfix_kb: '{{test_win_hotfix_reboot_kb}}' + source: '{{test_win_hotfix_path}}\reboot.msu' + state: present + register: install_reboot_hotfix + +- name: get result of install reboot hotfix + ansible.windows.win_command: powershell.exe Get-Hotfix -Id {{test_win_hotfix_reboot_kb}} + register: install_hotfix_reboot_actual + +- name: assert install reboot hotfix + assert: + that: + - install_reboot_hotfix is changed + - install_reboot_hotfix.kb == test_win_hotfix_reboot_kb + - install_reboot_hotfix.identifier == test_win_hotfix_reboot_identifier + - install_reboot_hotfix.reboot_required == True + - install_hotfix_reboot_actual.rc == 0 + +- name: run install reboot again before rebooting + win_hotfix: + source: '{{test_win_hotfix_path}}\reboot.msu' + state: present + register: install_before_rebooting + +- name: assert install reboot again before rebooting + assert: + that: + - install_before_rebooting is not changed + - install_before_rebooting.reboot_required == True + +- ansible.windows.win_reboot: + +- name: install reboot hotfix again + win_hotfix: + hotfix_identifier: '{{test_win_hotfix_reboot_identifier}}' + source: '{{test_win_hotfix_path}}\reboot.msu' + state: present + register: install_reboot_hotfix_again + +- name: assert install reboot hotfix again + assert: + that: + - install_reboot_hotfix_again is not changed + - install_reboot_hotfix_again.reboot_required == False + +- name: uninstall hotfix with kb check + win_hotfix: + hotfix_kb: '{{test_win_hotfix_reboot_kb}}' + state: absent + register: uninstall_hotfix_kb_check + check_mode: yes + +- name: get result of uninstall hotfix with kb check + ansible.windows.win_command: powershell.exe Get-Hotfix -Id {{test_win_hotfix_reboot_kb}} + register: uninstall_hotfix_kb_actual_check + +- name: assert uninstall hotfix with kb check + assert: + that: + - uninstall_hotfix_kb_check is changed + - uninstall_hotfix_kb_check.kb == test_win_hotfix_reboot_kb + - uninstall_hotfix_kb_check.identifier == test_win_hotfix_reboot_identifier + - uninstall_hotfix_kb_check.reboot_required == False + - uninstall_hotfix_kb_actual_check.rc == 0 + +- name: uninstall hotfix with kb + win_hotfix: + hotfix_kb: '{{test_win_hotfix_reboot_kb}}' + state: absent + register: uninstall_hotfix_kb + +- name: get result of uninstall hotfix with kb + ansible.windows.win_command: powershell.exe Get-Hotfix -Id {{test_win_hotfix_kb}} + register: uninstall_hotfix_kb_actual + ignore_errors: True + +- name: assert uninstall hotfix with kb + assert: + that: + - uninstall_hotfix_kb is changed + - uninstall_hotfix_kb.kb == test_win_hotfix_reboot_kb + - uninstall_hotfix_kb.identifier == test_win_hotfix_reboot_identifier + - uninstall_hotfix_kb.reboot_required == True + - uninstall_hotfix_kb_actual.rc != 0 + +- ansible.windows.win_reboot: + +- name: uninstall hotfix with kb again + win_hotfix: + hotfix_kb: '{{test_win_hotfix_reboot_kb}}' + state: absent + register: uninstall_hotfix_kb_again + +- name: assert uninstall hotfix with kb again + assert: + that: + - uninstall_hotfix_kb_again is not changed + - uninstall_hotfix_kb_again.reboot_required == False + +- name: remove test staging folder + ansible.windows.win_file: + path: '{{test_win_hotfix_path}}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/aliases b/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/tasks/main.yml new file mode 100644 index 000000000..5da9aa7fe --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/tasks/main.yml @@ -0,0 +1,14 @@ +--- +- name: make sure we start the tests with no proxy set + win_http_proxy: + +- block: + - name: run tests + include_tasks: tests.yml + + always: + - name: remove any explicit proxy settings + win_http_proxy: + + - name: reset WinINet proxy settings + win_inet_proxy: diff --git a/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/tasks/tests.yml new file mode 100644 index 000000000..04a763d08 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_http_proxy/tasks/tests.yml @@ -0,0 +1,265 @@ +--- +- name: ensure we fail when proxy is not set with bypass + win_http_proxy: + bypass: abc + register: fail_bypass + failed_when: 'fail_bypass.msg != "missing parameter(s) required by ''bypass'': proxy"' + +- name: ensure we fail when proxy and source is set + win_http_proxy: + proxy: proxy + source: ie + register: fail_source + failed_when: 'fail_source.msg != "parameters are mutually exclusive: proxy, source"' + +- name: ensure we fail if an invalid protocol is specified + win_http_proxy: + proxy: + fail1: fail + fail2: fail + register: fail_protocol + failed_when: 'fail_protocol.msg != "Invalid keys found in proxy: fail1, fail2. Valid keys are http, https, ftp, socks."' + +# WinHTTP does not validate on set, this ensures the module checks and revert any failed attempts at setting the proxy +# FIXME: Only certain hosts seem to have a strict winhttp definition, we can't run this in CI for now +#- name: ensure we fail if invalid value is set +# win_http_proxy: +# proxy: fake=proxy +# register: fail_invalid +# failed_when: fail_invalid.msg != "Unknown error when trying to set proxy 'fake=proxy' or bypass ''" +# +#- name: check proxy is still set to Direct access +# ansible.windows.win_command: netsh winhttp show proxy +# register: fail_invalid_actual +# failed_when: fail_invalid_actual.stdout_lines[3]|trim != "Direct access (no proxy server)." + +- name: set a proxy using a string (check) + win_http_proxy: + proxy: proxyhost + register: proxy_str_check + check_mode: True + +- name: get result of set a proxy using a string (check) + ansible.windows.win_command: netsh winhttp show proxy + register: proxy_str_actual_check + +- name: assert set a proxy using a string (check) + assert: + that: + - proxy_str_check is changed + - proxy_str_actual_check.stdout_lines[3]|trim == "Direct access (no proxy server)." + +- name: set a proxy using a string + win_http_proxy: + proxy: proxyhost + register: proxy_str + +- name: get result of set a proxy using a string + ansible.windows.win_command: netsh winhttp show proxy + register: proxy_str_actual + +- name: assert set a proxy using a string + assert: + that: + - proxy_str is changed + - 'proxy_str_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost"' + - 'proxy_str_actual.stdout_lines[4]|trim == "Bypass List : (none)"' + +- name: set a proxy using a string (idempotent) + win_http_proxy: + proxy: proxyhost + register: proxy_str_again + +- name: assert set a proxy using a string (idempotent) + assert: + that: + - not proxy_str_again is changed + +- name: change a proxy and set bypass (check) + win_http_proxy: + proxy: proxyhost:8080 + bypass: + - abc + - def + - <local> + register: change_proxy_check + check_mode: True + +- name: get result of change a proxy and set bypass (check) + ansible.windows.win_command: netsh winhttp show proxy + register: change_proxy_actual_check + +- name: assert change a proxy and set bypass (check) + assert: + that: + - change_proxy_check is changed + - 'change_proxy_actual_check.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost"' + - 'change_proxy_actual_check.stdout_lines[4]|trim == "Bypass List : (none)"' + +- name: change a proxy and set bypass + win_http_proxy: + proxy: proxyhost:8080 + bypass: + - abc + - def + - <local> + register: change_proxy + +- name: get result of change a proxy and set bypass + ansible.windows.win_command: netsh winhttp show proxy + register: change_proxy_actual + +- name: assert change a proxy and set bypass + assert: + that: + - change_proxy is changed + - 'change_proxy_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"' + - 'change_proxy_actual.stdout_lines[4]|trim == "Bypass List : abc;def;<local>"' + +- name: change a proxy and set bypass (idempotent) + win_http_proxy: + proxy: proxyhost:8080 + bypass: abc,def,<local> + register: change_proxy_again + +- name: assert change a proxy and set bypass (idempotent) + assert: + that: + - not change_proxy_again is changed + +- name: change bypass list + win_http_proxy: + proxy: proxyhost:8080 + bypass: + - abc + - <-loopback> + register: change_bypass + +- name: get result of change bypass list + ansible.windows.win_command: netsh winhttp show proxy + register: change_bypass_actual + +- name: assert change bypass list + assert: + that: + - change_bypass is changed + - 'change_bypass_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"' + - 'change_bypass_actual.stdout_lines[4]|trim == "Bypass List : abc;<-loopback>"' + +- name: remove proxy without options (check) + win_http_proxy: + register: remove_proxy_check + check_mode: yes + +- name: get result of remove proxy without options (check) + ansible.windows.win_command: netsh winhttp show proxy + register: remove_proxy_actual_check + +- name: assert remove proxy without options (check) + assert: + that: + - remove_proxy_check is changed + - 'remove_proxy_actual_check.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"' + - 'remove_proxy_actual_check.stdout_lines[4]|trim == "Bypass List : abc;<-loopback>"' + +- name: remove proxy without options + win_http_proxy: + register: remove_proxy + +- name: get result of remove proxy without options + ansible.windows.win_command: netsh winhttp show proxy + register: remove_proxy_actual + +- name: assert remove proxy without options + assert: + that: + - remove_proxy is changed + - remove_proxy_actual.stdout_lines[3]|trim == "Direct access (no proxy server)." + +- name: remove proxy without options (idempotent) + win_http_proxy: + register: remove_proxy_again + +- name: assert remove proxy without options (idempotent) + assert: + that: + - not remove_proxy_again is changed + +- name: set proxy with dictionary + win_http_proxy: + proxy: + http: proxy:8080 + https: proxy:8443 + ftp: proxy:821 + socks: proxy:888 + register: set_dict + +- name: get result of set proxy with dictionary + ansible.windows.win_command: netsh winhttp show proxy + register: set_dict_actual + +- name: assert set proxy with dictionary + assert: + that: + - set_dict is changed + - 'set_dict_actual.stdout_lines[3]|trim == "Proxy Server(s) : http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888"' + - 'set_dict_actual.stdout_lines[4]|trim == "Bypass List : (none)"' + +- name: set proxy protocol with str + win_http_proxy: + proxy: http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888 + register: set_str_protocol + +- name: assert set proxy protocol with str + assert: + that: + - not set_str_protocol is changed + +- name: remove proxy with empty string + win_http_proxy: + proxy: '' + register: remove_empty_str + +- name: get result of remove proxy with empty string + ansible.windows.win_command: netsh winhttp show proxy + register: remove_empty_str_actual + +- name: assert remove proxy with empty string + assert: + that: + - remove_empty_str is changed + - remove_empty_str_actual.stdout_lines[3]|trim == "Direct access (no proxy server)." + +- name: set explicit proxy for WinINet + win_inet_proxy: + proxy: proxyhost:8080 + bypass: + - abc + - def + - <local> + +- name: import proxy from IE + win_http_proxy: + source: ie + register: import_ie + +- name: get result of import proxy from IE + ansible.windows.win_command: netsh winhttp show proxy + register: import_ie_actual + +- name: assert import proxy from IE + assert: + that: + - import_ie is changed + - 'import_ie_actual.stdout_lines[3]|trim == "Proxy Server(s) : proxyhost:8080"' + - 'import_ie_actual.stdout_lines[4]|trim == "Bypass List : abc;def;<local>"' + +- name: import proxy from IE (idempotent) + win_http_proxy: + source: ie + register: import_ie_again + +- name: assert import proxy from IE (idempotent) + assert: + that: + - not import_ie_again is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/aliases b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/aliases new file mode 100644 index 000000000..de2313a6a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/aliases @@ -0,0 +1 @@ +shippable/windows/group4
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/defaults/main.yml new file mode 100644 index 000000000..b9dfc1971 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/defaults/main.yml @@ -0,0 +1,10 @@ +--- + +test_vdir_name: testvdir +test_physical_path: "{{ remote_tmp_dir }}" + +test_site_name: 'Test Site' +test_app_name: 'testapp' + +test_user: testuser +test_password: testpass
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/meta/main.yml new file mode 100644 index 000000000..e3dd5fb10 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/tasks/main.yml new file mode 100644 index 000000000..3374cbb68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/tasks/main.yml @@ -0,0 +1,90 @@ +--- +- name: check if we can run the tests + ansible.windows.win_shell: | + $osVersion = [Version](Get-Item -LiteralPath "$env:SystemRoot\System32\kernel32.dll").VersionInfo.ProductVersion + $osVersion -ge [Version]"6.2" + register: run_test + changed_when: False + +- name: Run on Server 2012 and higher + when: run_test.stdout | trim | bool + block: + - name: ensure IIS features are installed + ansible.windows.win_feature: + name: Web-Server + state: present + include_management_tools: True + register: feature_install + + - name: reboot after feature install + ansible.windows.win_reboot: + when: feature_install.reboot_required + + # may be possible that copy corrupts the file + - name: Get iis configuration checksum + ansible.windows.win_stat: + path: C:\Windows\System32\inetsrv\config\applicationHost.config + checksum_algorithm: sha1 + register: stat_result + + - name: take a copy of the original iis configuration + ansible.windows.win_copy: + src: C:\Windows\System32\inetsrv\config\applicationHost.config + dest: '{{ remote_tmp_dir }}\applicationHost.config' + remote_src: yes + register: copy_result + + - assert: + that: + - "stat_result.stat.checksum == copy_result.checksum" + + # Tests + - name: run tests on hosts that support it + include_tasks: tests.yml + + always: + # Cleanup + - name: remove test virtual directory + win_iis_virtualdirectory: + state: absent + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + + - name: remove test application + win_iis_webapplication: + name: "{{ test_app_name }}" + site: "{{ test_site_name }}" + state: absent + + - name: remove test site + win_iis_website: + name: "{{ test_site_name }}" + state: absent + + - name: delete test application temporary directory + win_file: + path: "{{ test_app_tmp_dir.path }}" + state: absent + + - name: restore iis configuration + ansible.windows.win_copy: + src: '{{ remote_tmp_dir }}\applicationHost.config' + dest: C:\Windows\System32\inetsrv\config\applicationHost.config + remote_src: yes + register: copy_result + + - assert: + that: + - "stat_result.stat.checksum == copy_result.checksum" + + - name: remove IIS feature if it was installed + ansible.windows.win_feature: + name: Web-Server + state: absent + include_management_tools: True + when: feature_install is changed + register: feature_uninstall + + - name: reboot after removing IIS features + ansible.windows.win_reboot: + when: feature_uninstall.reboot_required | default(False) diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/tasks/tests.yml new file mode 100644 index 000000000..e98271b2e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_virtualdirectory/tasks/tests.yml @@ -0,0 +1,111 @@ +--- +- name: test site exists, but stopped in case of duplicate web binding + win_iis_website: + name: "{{ test_site_name }}" + state: stopped + physical_path: 'C:\inetpub\wwwroot' + +- name: test virtual directory is absent (baseline) + win_iis_virtualdirectory: + state: absent + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + +- name: create test virtual directory + win_iis_virtualdirectory: + state: present + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + physical_path: "{{ test_physical_path }}" + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.directory.PhysicalPath == test_physical_path' + +- name: create test virtual directory (idempotent) + win_iis_virtualdirectory: + state: present + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + physical_path: "{{ test_physical_path }}" + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.directory.PhysicalPath == test_physical_path' + +- name: set test virtual directory credentials + win_iis_virtualdirectory: + state: present + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + connect_as: specific_user + username: "{{ test_user }}" + password: "{{ test_password }}" + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.directory.PhysicalPath == test_physical_path' + +- name: set test virtual directory credentials (idempotent) + win_iis_virtualdirectory: + state: present + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + connect_as: specific_user + username: "{{ test_user }}" + password: "{{ test_password }}" + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.directory.PhysicalPath == test_physical_path' + +- name: create test application temporary directory + ansible.windows.win_tempfile: + suffix: ".{{ test_app_name }}" + state: directory + register: test_app_tmp_dir + +- name: create new test application + win_iis_webapplication: + name: "{{ test_app_name }}" + site: "{{ test_site_name }}" + physical_path: "{{ test_app_tmp_dir.path }}" + state: present + +- name: create virtual directory and use pass through authentication + win_iis_virtualdirectory: + state: present + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + physical_path: "{{ test_physical_path }}" + connect_as: pass_through + application: "{{ test_app_name }}" + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.directory.PhysicalPath == test_physical_path' + +- name: create virtual directory and use pass through authentication (idempotent) + win_iis_virtualdirectory: + state: present + site: "{{ test_site_name }}" + name: "{{ test_vdir_name }}" + physical_path: "{{ test_physical_path }}" + connect_as: pass_through + application: "{{ test_app_name }}" + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.directory.PhysicalPath == test_physical_path'
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/aliases b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/aliases new file mode 100644 index 000000000..54a5923a5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/aliases @@ -0,0 +1,2 @@ +shippable/windows/group4 +unstable # Random IIS configuration errors
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/defaults/main.yml new file mode 100644 index 000000000..e5a582dee --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/defaults/main.yml @@ -0,0 +1,11 @@ +--- + +test_app_name: TestApp + +test_site_name: 'Test Site' + +test_user: testuser +test_password: testpass + +test_physical_path: "{{ remote_tmp_dir }}" +test_apppool: 'testapppool' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/meta/main.yml new file mode 100644 index 000000000..e3dd5fb10 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/tasks/main.yml new file mode 100644 index 000000000..64b022b6a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/tasks/main.yml @@ -0,0 +1,84 @@ +--- +- name: check if we can run the tests + ansible.windows.win_shell: | + $osVersion = [Version](Get-Item -LiteralPath "$env:SystemRoot\System32\kernel32.dll").VersionInfo.ProductVersion + $osVersion -ge [Version]"6.2" + register: run_test + changed_when: False + +- name: Run on Server 2012 and higher + when: run_test.stdout | trim | bool + block: + - name: ensure IIS features are installed + ansible.windows.win_feature: + name: Web-Server + state: present + include_management_tools: True + register: feature_install + + - name: reboot after feature install + ansible.windows.win_reboot: + when: feature_install.reboot_required + + # may be possible that copy corrupts the file + - name: Get iis configuration checksum + ansible.windows.win_stat: + path: C:\Windows\System32\inetsrv\config\applicationHost.config + checksum_algorithm: sha1 + register: stat_result + + - name: take a copy of the original iis configuration + ansible.windows.win_copy: + src: C:\Windows\System32\inetsrv\config\applicationHost.config + dest: '{{ remote_tmp_dir }}\applicationHost.config' + remote_src: yes + register: copy_result + + - assert: + that: + - "stat_result.stat.checksum == copy_result.checksum" + + # Tests + - name: run tests on hosts that support it + include_tasks: tests.yml + + always: + # Cleanup + - name: remove test application + win_iis_webapplication: + state: absent + site: "{{ test_site_name }}" + name: "{{ test_app_name }}" + + - name: remove test application pool + win_iis_webapppool: + name: "{{ test_apppool }}" + state: absent + + - name: remove test site + win_iis_website: + name: "{{ test_site_name }}" + state: absent + + - name: restore iis configuration + ansible.windows.win_copy: + src: '{{ remote_tmp_dir }}\applicationHost.config' + dest: C:\Windows\System32\inetsrv\config\applicationHost.config + remote_src: yes + register: copy_result + + - assert: + that: + - "stat_result.stat.checksum == copy_result.checksum" + + - name: remove IIS feature if it was installed + ansible.windows.win_feature: + name: Web-Server + state: absent + include_management_tools: True + when: feature_install is changed + register: feature_uninstall + + - name: reboot after removing IIS features + ansible.windows.win_reboot: + when: feature_uninstall.reboot_required | default(False) diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/tasks/tests.yml new file mode 100644 index 000000000..135cccfec --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapplication/tasks/tests.yml @@ -0,0 +1,91 @@ +--- +- name: test site exists, but stopped in case of duplicate web binding + win_iis_website: + name: "{{ test_site_name }}" + state: stopped + physical_path: 'C:\inetpub\wwwroot' + +- name: test app is absent (baseline) + win_iis_webapplication: + state: absent + site: "{{ test_site_name }}" + name: "{{ test_app_name }}" + +- name: create test app + win_iis_webapplication: + state: present + site: "{{ test_site_name }}" + name: "{{ test_app_name }}" + physical_path: "{{ test_physical_path }}" + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.physical_path == test_physical_path' + +- name: create test app (idempotent) + win_iis_webapplication: + state: present + site: "{{ test_site_name }}" + name: "{{ test_app_name }}" + physical_path: "{{ test_physical_path }}" + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.physical_path == test_physical_path' + +- name: set test app credentials + win_iis_webapplication: + state: present + site: "{{ test_site_name }}" + name: "{{ test_app_name }}" + connect_as: specific_user + username: "{{ test_user }}" + password: "{{ test_password }}" + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.physical_path == test_physical_path' + - "result.connect_as == 'specific_user'" + +- name: set test app credentials (idempotent) + win_iis_webapplication: + state: present + site: "{{ test_site_name }}" + name: "{{ test_app_name }}" + connect_as: specific_user + username: "{{ test_user }}" + password: "{{ test_password }}" + register: result + +- assert: + that: + - 'result.changed == false' + - 'result.physical_path == test_physical_path' + - "result.connect_as == 'specific_user'" + +- name: create new test application pool + win_iis_webapppool: + name: "{{ test_apppool }}" + state: present + +- name: change app pool and use pass through authentication + win_iis_webapplication: + state: present + site: "{{ test_site_name }}" + name: "{{ test_app_name }}" + connect_as: pass_through + application_pool: "{{ test_apppool }}" + register: result + +- assert: + that: + - 'result.changed == true' + - 'result.physical_path == test_physical_path' + - "result.connect_as == 'pass_through'" + - "result.application_pool == test_apppool" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/aliases b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/defaults/main.yml new file mode 100644 index 000000000..bd0f15c99 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/defaults/main.yml @@ -0,0 +1 @@ +test_iis_webapppool_name: TestPool
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/tasks/main.yml new file mode 100644 index 000000000..f00fbc70a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/tasks/main.yml @@ -0,0 +1,43 @@ +--- +# Cannot use win_feature to install IIS on Server 2008. +# Run a brief check and skip hosts that don't support +# that operation +- name: check if win_feature will work on test host + ansible.windows.win_command: powershell.exe "Get-WindowsFeature" + register: module_available + failed_when: False + +# Run actual tests +- block: + - name: ensure IIS features are installed + ansible.windows.win_feature: + name: Web-Server + state: present + include_management_tools: True + register: feature_install + + - name: reboot after feature install + ansible.windows.win_reboot: + when: feature_install.reboot_required + + - name: set version of IIS for tests + win_file_version: + path: C:\Windows\System32\inetsrv\w3wp.exe + register: iis_version + + - name: ensure test pool is deleted as a baseline + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: absent + + # Tests + - name: run tests on hosts that support it + include_tasks: tests.yml + + always: + # Cleanup + - name: ensure test pool is deleted + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: absent + when: module_available.rc == 0 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/tasks/tests.yml new file mode 100644 index 000000000..ff28a6990 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webapppool/tasks/tests.yml @@ -0,0 +1,424 @@ +--- +- name: create default pool check + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: started + register: create_default_check + check_mode: yes + +- name: get actual of create default pool check + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}" + register: create_default_actual_check + failed_when: False + +- name: assert create default pool check + assert: + that: + - create_default_check is changed + - create_default_actual_check.rc == 1 + +- name: create default pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + register: create_default + +- name: get actual of create default pool + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}" + register: create_default_actual + failed_when: False + +- name: assert create default pool + assert: + that: + - create_default is changed + - create_default.info.attributes.name == test_iis_webapppool_name + - create_default.info.attributes.startMode == 'OnDemand' + - create_default.info.attributes.state == 'Started' + - create_default_actual.rc == 0 + +- name: change attributes of pool check + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + managedPipelineMode: 1 # Using an enum value + cpu.limit: 95 # Nested values + processModel.identityType: LocalSystem # Using an enum name + processModel.loadUserProfile: True + register: change_pool_attributes_check + check_mode: yes + +- name: assert change attributes of pool check + assert: + that: + - change_pool_attributes_check is changed + - change_pool_attributes_check.info == create_default.info + +- name: change attributes of pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + managedPipelineMode: 1 # Using an enum value + cpu.limit: 95 # Nested values + processModel.identityType: LocalSystem # Using an enum name + processModel.loadUserProfile: True + test: True + register: change_pool_attributes + +- name: assert change attributes of pool + assert: + that: + - change_pool_attributes is changed + - change_pool_attributes.info.attributes.managedPipelineMode == 'Classic' + - change_pool_attributes.info.cpu.limit == 95 + - change_pool_attributes.info.processModel.identityType == 'LocalSystem' + - change_pool_attributes.info.processModel.loadUserProfile == True + +- name: change attributes of pool again + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + managedPipelineMode: 1 # Using an enum value + cpu.limit: 95 # Nested values + processModel.identityType: LocalSystem # Using an enum name + processModel.loadUserProfile: True + register: change_pool_attributes_again + +- name: assert change attributes of pool again + assert: + that: + - change_pool_attributes_again is not changed + - change_pool_attributes_again.info == change_pool_attributes.info + +- name: change more complex variables check + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + queueLength: 500 + recycling.periodicRestart.requests: 10 # Deeply nested attribute + recycling.periodicRestart.time: "00:00:05:00.000000" # Timespan with string + processModel.pingResponseTime: "00:03:00" # Timespan without days or milliseconds + register: change_complex_attributes_check + check_mode: yes + +- name: assert change more complex variables check + assert: + that: + - change_complex_attributes_check is changed + - change_complex_attributes_check.info == change_pool_attributes_again.info + +- name: change more complex variables + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + queueLength: 500 + recycling.periodicRestart.requests: 10 # Deeply nested attribute + recycling.periodicRestart.time: "00:00:05:00.000000" # Timespan with string + processModel.pingResponseTime: "00:03:00" # Timespan without days or milliseconds + register: change_complex_attributes + +- name: assert change more complex variables + assert: + that: + - change_complex_attributes is changed + - change_complex_attributes.info.attributes.queueLength == 500 + - change_complex_attributes.info.recycling.periodicRestart.requests == 10 + - change_complex_attributes.info.recycling.periodicRestart.time.TotalSeconds == 300 + - change_complex_attributes.info.processModel.pingResponseTime.TotalSeconds == 180 + +- name: change more complex variables again + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + queueLength: 500 + recycling.periodicRestart.requests: 10 # Deeply nested attribute + recycling.periodicRestart.time: "00:00:05:00.000000" # Timespan with string + processModel.pingResponseTime: "00:03:00" # Timespan without days or milliseconds + register: change_complex_attributes_again + +- name: assert change more complex variables again + assert: + that: + - change_complex_attributes_again is not changed + - change_complex_attributes_again.info == change_complex_attributes.info + +- name: stop web pool check + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: stopped + register: stop_pool_check + check_mode: yes + +- name: get actual status of pool check + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: stop_pool_actual_check + +- name: assert stop web pool check + assert: + that: + - stop_pool_check is changed + - stop_pool_actual_check.stdout == 'Started\r\n' + +- name: stop web pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: stopped + register: stop_pool + +- name: get actual status of pool + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: stop_pool_actual + +- name: assert stop web pool + assert: + that: + - stop_pool is changed + - stop_pool_actual.stdout == 'Stopped\r\n' + +- name: stop web pool again + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: stopped + register: stop_pool_again + +- name: get actual status of pool again + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: stop_pool_actual_again + +- name: assert stop web pool again + assert: + that: + - stop_pool_again is not changed + - stop_pool_actual_again.stdout == 'Stopped\r\n' + +- name: start web pool check + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: started + register: start_pool_check + check_mode: yes + +- name: get actual status of pool check + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: start_pool_actual_check + +- name: assert start web pool check + assert: + that: + - start_pool_check is changed + - start_pool_actual_check.stdout == 'Stopped\r\n' + +- name: start web pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: started + register: start_pool + +- name: get actual status of pool + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: start_pool_actual + +- name: assert start web pool + assert: + that: + - start_pool is changed + - start_pool_actual.stdout == 'Started\r\n' + +- name: start web pool again + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: started + register: start_pool_again + +- name: get actual status of pool again + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: start_pool_actual_again + +- name: assert start web pool again + assert: + that: + - start_pool_again is not changed + - start_pool_actual_again.stdout == 'Started\r\n' + +- name: restart web pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: restarted + register: restart_pool + +- name: get actual status of pool + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: restart_pool_actual + +- name: assert restart web pool + assert: + that: + - restart_pool is changed + - restart_pool_actual.stdout == 'Started\r\n' + +- name: stop pool before restart on stop test + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: stopped + +- name: restart from stopped web pool check + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: restarted + register: restart_from_stop_pool_check + check_mode: yes + +- name: get actual status of pool check + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: restart_from_stop_pool_actual_check + +- name: assert restart from stopped web pool check + assert: + that: + - restart_from_stop_pool_check is changed + - restart_from_stop_pool_actual_check.stdout == 'Stopped\r\n' + +- name: restart from stopped web pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: restarted + register: restart_from_stop_pool + +- name: get actual status of pool + ansible.windows.win_command: powershell.exe "Import-Module WebAdministration; (Get-Item -Path IIS:\AppPools\{{test_iis_webapppool_name}}).state" + register: restart_from_stop_pool_actual + +- name: assert restart from stopped web pool + assert: + that: + - restart_from_stop_pool is changed + - restart_from_stop_pool_actual.stdout == 'Started\r\n' + +- name: set web pool attribute that is a collection (check mode) + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + recycling.periodicRestart.schedule: "00:10:00,10:10:00" + register: collection_change_check + check_mode: yes + +- name: get result of set web pool attribute that is a collection (check mode) + ansible.windows.win_shell: | + Import-Module WebAdministration + (Get-ItemProperty -Path "IIS:\AppPools\{{test_iis_webapppool_name}}" -Name recycling.periodicRestart.schedule).Collection | ForEach-Object { $_.value.ToString() } + register: collection_change_result_check + +- name: assert results of set web pool attribute that is a collection (check mode) + assert: + that: + - collection_change_check is changed + - collection_change_result_check.stdout == "" + +- name: set web pool attribute that is a collection + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + recycling.periodicRestart.schedule: "00:10:00,10:10:00" + register: collection_change + +- name: get result of set web pool attribute that is a collection + ansible.windows.win_shell: | + Import-Module WebAdministration + (Get-ItemProperty -Path "IIS:\AppPools\{{test_iis_webapppool_name}}" -Name recycling.periodicRestart.schedule).Collection | ForEach-Object { $_.value.ToString() } + register: collection_change_result + +- name: assert results of set web pool attribute that is a collection + assert: + that: + - collection_change is changed + - collection_change_result.stdout_lines == [ "00:10:00", "10:10:00" ] + +- name: set web pool attribute that is a collection (idempotent) + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + recycling.periodicRestart.schedule: [ "00:10:00", "10:10:00" ] + register: collection_change_again + +- name: assert results of set web pool attribute that is a collection (idempotent) + assert: + that: + - collection_change_again is not changed + +# The following tests are only for IIS versions 8.0 or newer +- block: + - name: delete test pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: absent + + - name: create test pool + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + register: iis_attributes_blank + + - name: change attributes for newer IIS version check + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + startMode: AlwaysRunning + processModel.identityType: SpecificUser + processModel.userName: '{{ansible_user}}' + processModel.password: '{{ansible_password}}' + register: iis_attributes_new_check + check_mode: yes + + - name: assert change attributes for newer IIS version check + assert: + that: + - iis_attributes_new_check is changed + - iis_attributes_new_check.info == iis_attributes_blank.info + + - name: change attributes for newer IIS version + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + startMode: AlwaysRunning + processModel.identityType: SpecificUser + processModel.userName: '{{ansible_user}}' + processModel.password: '{{ansible_password}}' + register: iis_attributes_new + + - name: assert change attributes for newer IIS version + assert: + that: + - iis_attributes_new is changed + - iis_attributes_new.info.attributes.startMode == 'AlwaysRunning' + - iis_attributes_new.info.processModel.identityType == 'SpecificUser' + - iis_attributes_new.info.processModel.userName == ansible_user + + - name: change attributes for newer IIS version again + win_iis_webapppool: + name: '{{test_iis_webapppool_name}}' + state: present + attributes: + startMode: AlwaysRunning + processModel.identityType: 3 + processModel.userName: '{{ansible_user}}' + processModel.password: '{{ansible_password}}' + register: iis_attributes_new_again + + - name: assert change attributes for newer IIS version again + assert: + that: + - iis_attributes_new_again is not changed + - iis_attributes_new_again.info == iis_attributes_new.info + + when: iis_version.win_file_version.file_major_part|int > 7 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/aliases b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/defaults/main.yml new file mode 100644 index 000000000..13f0bc333 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/defaults/main.yml @@ -0,0 +1,30 @@ +test_iis_site_name: default web site + +http_vars: + protocol: http + port: 80 + ip: '*' + +http_header_vars: + protocol: http + port: 80 + ip: '*' + header: test.com + +https_vars: + protocol: https + port: 443 + ip: '*' + +https_header_vars: + protocol: https + port: 443 + ip: '*' + header: test.com + ssl_flags: 1 + +https_wc_vars: + protocol: https + port: 443 + ip: '127.0.0.1' + header: wc.test.com diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/library/test_get_webbindings.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/library/test_get_webbindings.ps1 new file mode 100644 index 000000000..f1d49f46a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/library/test_get_webbindings.ps1 @@ -0,0 +1,106 @@ +#!powershell + +# Copyright: (c) 2017, Noah Sparks <nsparks@outlook.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#Requires -Module Ansible.ModuleUtils.Legacy + +$params = Parse-Args -arguments $args -supports_check_mode $true + +$name = Get-AnsibleParam $params -name "name" -type str -failifempty $true -aliases 'website' +$host_header = Get-AnsibleParam $params -name "host_header" -type str +$protocol = Get-AnsibleParam $params -name "protocol" -type str -default 'http' +$port = Get-AnsibleParam $params -name "port" -type int -default '80' +$ip = Get-AnsibleParam $params -name "ip" -default '*' + +$result = @{ + changed = $false +} +function New-BindingInfo { + $ht = @{ + 'bindingInformation' = $args[0].bindingInformation + 'ip' = $args[0].bindingInformation.split(':')[0] + 'port' = [int]$args[0].bindingInformation.split(':')[1] + 'hostheader' = $args[0].bindingInformation.split(':')[2] + 'isDsMapperEnabled' = $args[0].isDsMapperEnabled + 'protocol' = $args[0].protocol + 'certificateStoreName' = $args[0].certificateStoreName + 'certificateHash' = $args[0].certificateHash + } + + #handle sslflag support + If ([version][System.Environment]::OSVersion.Version -lt [version]'6.2') { + $ht.sslFlags = 'not supported' + } + Else { + $ht.sslFlags = [int]$args[0].sslFlags + } + + Return $ht +} + +# Used instead of get-webbinding to ensure we always return a single binding +# pass it $binding_parameters hashtable +function Get-SingleWebBinding { + + Try { + $site_bindings = get-webbinding -name $args[0].name + } + Catch { + # 2k8r2 throws this error when you run get-webbinding with no bindings in iis + $msg = 'Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value' + If (-not $_.Exception.Message.CompareTo($msg)) { + Throw $_.Exception.Message + } + Else { return } + } + + Foreach ($binding in $site_bindings) { + $splits = $binding.bindingInformation -split ':' + + if ( + $args[0].protocol -eq $binding.protocol -and + $args[0].ipaddress -eq $splits[0] -and + $args[0].port -eq $splits[1] -and + $args[0].hostheader -eq $splits[2] + ) { + Return $binding + } + } +} + +# create binding search splat +$binding_parameters = @{ + Name = $name + Protocol = $protocol + Port = $port + IPAddress = $ip +} + +# insert host header to search if specified, otherwise it will return * (all bindings matching protocol/ip) +If ($host_header) { + $binding_parameters.HostHeader = $host_header +} +Else { + $binding_parameters.HostHeader = [string]::Empty +} + +# Get bindings matching parameters +Try { + $current_bindings = Get-SingleWebBinding $binding_parameters +} +Catch { + Fail-Json -obj $result -message "Failed to retrieve bindings with Get-SingleWebBinding - $($_.Exception.Message)" +} + +If ($current_bindings) { + Try { + $binding_info = New-BindingInfo $current_bindings + } + Catch { + Fail-Json -obj $result -message "Failed to create binding info - $($_.Exception.Message)" + } + + $result.binding = $binding_info +} +exit-json -obj $result
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/failures.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/failures.yml new file mode 100644 index 000000000..92736fe1b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/failures.yml @@ -0,0 +1,70 @@ +- name: failure check define * for host header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: '*' + protocol: http + ip: '*' + register: failure + failed_when: failure.msg != "To make or remove a catch-all binding, please omit the host_header parameter entirely rather than specify host_header *" + +- debug: + var: failure + verbosity: 1 + +- block: + - name: get all websites from server + raw: powershell.exe "(get-website).name" + register: existing_sites + + - name: ensure all sites are removed for clean testing + win_iis_website: + name: "{{ item }}" + state: absent + with_items: + - "{{ existing_sites.stdout_lines }}" + + - name: add testremove site + win_iis_website: + name: testremove + state: started + physical_path: c:\inetpub\wwwroot + + - name: add bindings to testremove + win_iis_webbinding: + name: testremove + ip: "{{ item.ip }}" + port: "{{ item.port }}" + with_items: + - {ip: 127.0.0.1, port: 80} + - {ip: '*', port: 80} + + - name: remove ip * binding from testremove + win_iis_webbinding: + name: testremove + state: absent + port: 80 + ip: '*' + + - name: get the remaining binding from testremove + test_get_webbindings: + name: testremove + port: 80 + ip: 127.0.0.1 + register: test_result + + - debug: + var: test_result + verbosity: 1 + + - name: assert that remove *:80 doesn't also remove 127.0.0.1:80 + assert: + that: + - test_result.binding.ip == '127.0.0.1' + - test_result.binding.port == 80 + + always: + - name: remove websites + win_iis_website: + name: testremove + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/http.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/http.yml new file mode 100644 index 000000000..34c4cc2c1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/http.yml @@ -0,0 +1,317 @@ +#cm add +#changed true, check nothing present +- name: CM add http binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: http_no_header + check_mode: yes + +- name: CM get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: get_http_no_header + changed_when: false + +- name: CM add http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: http_header + check_mode: yes + +- name: CM get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: get_http_header + changed_when: false + +- name: CM assert changed, but not added + assert: + that: + - http_no_header is changed + - http_no_header.binding_info is none + - get_http_no_header.binding is not defined + - http_header is changed + - http_header.binding_info is none + - get_http_header.binding is not defined + +#add +#changed true, new bindings present +- name: add http binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: http_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: get_http_no_header + changed_when: false + +- name: add http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: http_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: get_http_header + changed_when: false + +- name: assert changed and added + assert: + that: + - http_no_header is changed + - http_no_header.binding_info is defined + - http_no_header.operation_type == 'added' + - http_no_header.binding_info.ip == "{{ http_vars.ip }}" + - http_no_header.binding_info.port == {{ http_vars.port }} + - http_no_header.binding_info.protocol == "{{ http_vars.protocol }}" + - http_header is changed + - http_header.binding_info is defined + - http_header.operation_type == 'added' + - http_header.binding_info.ip == "{{ http_header_vars.ip }}" + - http_header.binding_info.port == {{ http_header_vars.port }} + - http_header.binding_info.protocol == "{{ http_header_vars.protocol }}" + - http_header.binding_info.hostheader == "{{ http_header_vars.header }}" + +#add idem +#changed false +- name: idem add http binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: http_no_header + +- name: idem add http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: http_header + +- name: idem assert not changed + assert: + that: + - http_no_header is not changed + - http_header is not changed + +#modify +#can't test modify for http, it will add a new binding instead since +#there's no way to match existing bindings against the new parameters + +#cm remove +#changed true, bindings still present +- name: cm remove http binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: http_no_header + check_mode: yes + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: get_http_no_header + changed_when: false + +- name: cm remove http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: http_header + check_mode: yes + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: get_http_header + changed_when: false + +- name: cm remove assert changed, but still present + assert: + that: + - http_no_header is changed + - http_no_header.binding_info is defined + - http_no_header.operation_type == 'removed' + - http_no_header.binding_info.ip == "{{ http_vars.ip }}" + - http_no_header.binding_info.port == {{ http_vars.port }} + - http_no_header.binding_info.protocol == "{{ http_vars.protocol }}" + - get_http_no_header.binding is defined + - get_http_no_header.binding.ip == "{{ http_vars.ip }}" + - get_http_no_header.binding.port == {{ http_vars.port }} + - get_http_no_header.binding.protocol == "{{ http_vars.protocol }}" + - http_header is changed + - http_header.binding_info is defined + - http_header.operation_type == 'removed' + - http_header.binding_info.ip == "{{ http_header_vars.ip }}" + - http_header.binding_info.port == {{ http_header_vars.port }} + - http_header.binding_info.protocol == "{{ http_header_vars.protocol }}" + - http_header.binding_info.hostheader == "{{ http_header_vars.header }}" + - get_http_header.binding is defined + - get_http_header.binding.ip == "{{ http_header_vars.ip }}" + - get_http_header.binding.port == {{ http_header_vars.port }} + - get_http_header.binding.protocol == "{{ http_header_vars.protocol }}" + - get_http_header.binding.hostheader == "{{ http_header_vars.header }}" + + +#remove +#changed true, bindings gone +- name: remove http binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: http_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: get_http_no_header + changed_when: false + +- name: remove http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: http_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: get_http_header + changed_when: false + +- name: remove assert changed and gone + assert: + that: + - http_no_header is changed + - http_no_header.operation_type == 'removed' + - http_no_header.binding_info is defined + - http_no_header.binding_info.ip == "{{ http_vars.ip }}" + - http_no_header.binding_info.port == {{ http_vars.port }} + - http_no_header.binding_info.protocol == "{{ http_vars.protocol }}" + - get_http_no_header.binding is not defined + - http_header is changed + - http_header.binding_info is defined + - http_header.operation_type == 'removed' + - http_header.binding_info.ip == "{{ http_header_vars.ip }}" + - http_header.binding_info.port == {{ http_header_vars.port }} + - http_header.binding_info.protocol == "{{ http_header_vars.protocol }}" + - http_header.binding_info.hostheader == "{{ http_header_vars.header }}" + - get_http_header.binding is not defined + +#remove idem +#change false, bindings gone +- name: idem remove http binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: http_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ http_vars.protocol }}" + ip: "{{ http_vars.ip }}" + port: "{{ http_vars.port }}" + register: get_http_no_header + changed_when: false + +- name: idem remove http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: http_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ http_header_vars.header }}" + protocol: "{{ http_header_vars.protocol }}" + ip: "{{ http_header_vars.ip }}" + port: "{{ http_header_vars.port }}" + register: get_http_header + changed_when: false + +- name: idem remove assert changed and gone + assert: + that: + - http_no_header is not changed + - http_no_header.binding_info is not defined + - get_http_no_header.binding is not defined + - http_header is not changed + - http_header.binding_info is not defined + - get_http_header.binding is not defined diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/https-ge6.2.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/https-ge6.2.yml new file mode 100644 index 000000000..f883c673f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/https-ge6.2.yml @@ -0,0 +1,459 @@ +############## +### CM Add ### +############## +#changed true, check nothing present +- name: CM add https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_no_header + check_mode: yes + +- name: CM get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: CM add https binding with header and SNI + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + ssl_flags: 1 + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_header + check_mode: yes + +- name: CM get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: get_https_header + changed_when: false + +- name: CM assert changed, but not added + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'added' + - https_no_header.binding_info is none + - get_https_no_header.binding is not defined + - https_header is changed + - https_header.operation_type == 'added' + - https_header.binding_info is none + - get_https_header.binding is not defined + +########### +### Add ### +########### +#changed true, new bindings present +- name: add https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: add https binding with header SNI + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + ssl_flags: 1 + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: get_https_header + changed_when: false + +- name: assert changed and added + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'added' + - https_no_header.binding_info is defined + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.hostheader == '' + - https_no_header.binding_info.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + - https_header is changed + - https_header.operation_type == 'added' + - https_header.binding_info is defined + - https_header.binding_info.hostheader == "{{ https_header_vars.header }}" + - https_header.binding_info.protocol == "{{ https_header_vars.protocol }}" + - https_header.binding_info.ip == "{{ https_header_vars.ip }}" + - https_header.binding_info.port == {{ https_header_vars.port }} + - https_header.binding_info.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + - https_header.binding_info.sslFlags == 1 + +################ +### Idem Add ### +################ +#changed false +- name: idem add https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: https + ip: '*' + port: 443 + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_no_header + +- name: idem add https binding with header and SNI + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: test.com + protocol: https + ip: '*' + port: 443 + ssl_flags: 1 + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_header + +- name: idem assert not changed + assert: + that: + - https_no_header is not changed + - https_header is not changed + +################# +### CM Modify ### +################# +# changed true, verify no changes occurred + +#modify sni +- name: CM modify https binding with header, change cert + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + ssl_flags: 1 + certificate_hash: "{{ thumbprint2.stdout_lines[0] }}" + register: https_header + check_mode: yes + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: get_https_header + changed_when: false + +- name: CM assert changed but old cert + assert: + that: + - https_header is changed + - https_header.operation_type == 'updated' + - https_header.binding_info is defined + - https_header.binding_info.ip == "{{ https_header_vars.ip }}" + - https_header.binding_info.port == {{ https_header_vars.port }} + - https_header.binding_info.protocol == "{{ https_header_vars.protocol }}" + - https_header.binding_info.hostheader == "{{ https_header_vars.header }}" + - https_header.binding_info.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + - https_header.binding_info.sslFlags == 1 + - get_https_header.binding is defined + - get_https_header.binding.ip == "{{ https_header_vars.ip }}" + - get_https_header.binding.port == {{ https_header_vars.port }} + - get_https_header.binding.protocol == "{{ https_header_vars.protocol }}" + - get_https_header.binding.hostheader == "{{ https_header_vars.header }}" + - get_https_header.binding.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + - get_https_header.binding.sslFlags == 1 + +############## +### Modify ### +############## +# modify ssl flags +- name: modify https binding with header, change cert + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + ssl_flags: 1 + certificate_hash: "{{ thumbprint2.stdout_lines[0] }}" + register: https_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: get_https_header + changed_when: false + +- name: modify assert changed and new cert + assert: + that: + - https_header is changed + - https_header.operation_type == 'updated' + - https_header.binding_info is defined + - https_header.binding_info.ip == "{{ https_header_vars.ip }}" + - https_header.binding_info.port == {{ https_header_vars.port }} + - https_header.binding_info.protocol == "{{ https_header_vars.protocol }}" + - https_header.binding_info.hostheader == "{{ https_header_vars.header }}" + - https_header.binding_info.certificateHash == "{{ thumbprint2.stdout_lines[0] }}" + - https_header.binding_info.sslFlags == 1 + - get_https_header.binding is defined + - get_https_header.binding.ip == "{{ https_header_vars.ip }}" + - get_https_header.binding.port == {{ https_header_vars.port }} + - get_https_header.binding.protocol == "{{ https_header_vars.protocol }}" + - get_https_header.binding.hostheader == "{{ https_header_vars.header }}" + - get_https_header.binding.certificateHash == "{{ thumbprint2.stdout_lines[0] }}" + - get_https_header.binding.sslFlags == 1 + +################### +### Idem Modify ### +################### +#changed false + +#idem modify ssl flags +- name: idem modify https binding with header, enable SNI and change cert + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + ssl_flags: 1 + certificate_hash: "{{ thumbprint2.stdout_lines[0] }}" + register: https_header + +- name: idem assert not changed + assert: + that: + - https_header is not changed + +################# +### CM Remove ### +################# +#changed true, bindings still present +- name: cm remove https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: https_no_header + check_mode: yes + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: cm remove https binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: https_header + check_mode: yes + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: get_https_header + changed_when: false + +- name: cm remove assert changed, but still present + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'removed' + - https_no_header.binding_info is defined + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - get_https_no_header.binding is defined + - get_https_no_header.binding.ip == "{{ https_vars.ip }}" + - get_https_no_header.binding.port == {{ https_vars.port }} + - get_https_no_header.binding.protocol == "{{ https_vars.protocol }}" + - get_https_no_header.binding.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + - https_header is changed + - https_header.binding_info is defined + - https_header.operation_type == 'removed' + - https_header.binding_info.ip == "{{ https_header_vars.ip }}" + - https_header.binding_info.port == {{ https_header_vars.port }} + - https_header.binding_info.protocol == "{{ https_header_vars.protocol }}" + - https_header.binding_info.hostheader == "{{ https_header_vars.header }}" + - get_https_header.binding is defined + - get_https_header.binding.ip == "{{ https_header_vars.ip }}" + - get_https_header.binding.port == {{ https_header_vars.port }} + - get_https_header.binding.protocol == "{{ https_header_vars.protocol }}" + - get_https_header.binding.hostheader == "{{ https_header_vars.header }}" + - get_https_header.binding.certificateHash == "{{ thumbprint2.stdout_lines[0] }}" + +############## +### remove ### +############## +#changed true, bindings gone +- name: remove https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: https_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: remove https binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: https_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: get_https_header + changed_when: false + +- name: remove assert changed and gone + assert: + that: + - https_no_header is changed + - https_no_header.binding_info is defined + - https_no_header.operation_type == 'removed' + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - get_https_no_header.binding is not defined + - https_header is changed + - https_header.binding_info is defined + - https_header.operation_type == 'removed' + - https_header.binding_info.ip == "{{ https_header_vars.ip }}" + - https_header.binding_info.port == {{ https_header_vars.port }} + - https_header.binding_info.protocol == "{{ https_header_vars.protocol }}" + - https_header.binding_info.hostheader == "{{ https_header_vars.header }}" + - get_https_header.binding is not defined + +################### +### remove idem ### +################### +#change false, bindings gone +- name: idem remove https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: https_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: idem remove https binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: https_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ https_header_vars.header }}" + protocol: "{{ https_header_vars.protocol }}" + ip: "{{ https_header_vars.ip }}" + port: "{{ https_header_vars.port }}" + register: get_https_header + changed_when: false + +- name: idem remove assert changed and gone + assert: + that: + - https_no_header is not changed + - https_no_header.binding_info is not defined + - get_https_no_header.binding is not defined + - https_header is not changed + - https_header.binding_info is not defined + - get_https_header.binding is not defined diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/https-lt6.2.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/https-lt6.2.yml new file mode 100644 index 000000000..1950641e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/https-lt6.2.yml @@ -0,0 +1,423 @@ +############## +### CM Add ### +############## +#changed true, check nothing present +- name: CM add https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_no_header + check_mode: yes + +- name: CM get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: CM assert changed, but not added + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'added' + - https_no_header.binding_info is none + - get_https_no_header.binding is not defined + +########### +### Add ### +########### +#changed true, new bindings present +- name: add https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_no_header + +- name: assert changed and added + assert: + that: + - https_no_header is changed + - https_no_header.binding_info is defined + - https_no_header.operation_type == 'added' + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - https_no_header.binding_info.hostheader == '' + - https_no_header.binding_info.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + +################ +### Idem Add ### +################ +#changed false +- name: idem add https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: https_no_header + +- name: idem assert not changed + assert: + that: + - https_no_header is not changed + +################# +### CM Modify ### +################# +# changed true, verify no changes occurred + +#modify sni +- name: CM modify https binding change cert + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint2.stdout_lines[0] }}" + register: https_no_header + check_mode: yes + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: CM assert changed but old cert + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'updated' + - https_no_header.binding_info is defined + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - https_no_header.binding_info.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + - get_https_no_header.binding is defined + - get_https_no_header.binding.ip == "{{ https_vars.ip }}" + - get_https_no_header.binding.port == {{ https_vars.port }} + - get_https_no_header.binding.protocol == "{{ https_vars.protocol }}" + - get_https_no_header.binding.certificateHash == "{{ thumbprint1.stdout_lines[0] }}" + +############## +### Modify ### +############## +# modify ssl flags +- name: modify https binding, change cert + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint2.stdout_lines[0] }}" + register: https_no_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: modify assert changed and new cert + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'updated' + - https_no_header.binding_info is defined + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - https_no_header.binding_info.certificateHash == "{{ thumbprint2.stdout_lines[0] }}" + - get_https_no_header.binding is defined + - get_https_no_header.binding.ip == "{{ https_vars.ip }}" + - get_https_no_header.binding.port == {{ https_vars.port }} + - get_https_no_header.binding.protocol == "{{ https_vars.protocol }}" + - get_https_no_header.binding.hostheader == '' + - get_https_no_header.binding.certificateHash == "{{ thumbprint2.stdout_lines[0] }}" + +################### +### Idem Modify ### +################### +#changed false + +#idem modify ssl flags +- name: idem modify https binding and change cert + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + certificate_hash: "{{ thumbprint2.stdout_lines[0] }}" + register: https_header + +- name: idem assert not changed + assert: + that: + - https_header is not changed + +################# +### CM Remove ### +################# +#changed true, bindings still present +- name: cm remove https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: https_no_header + check_mode: yes + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: cm remove assert changed, but still present + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'removed' + - https_no_header.binding_info is defined + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - https_no_header.binding_info.certificateHash == "{{ thumbprint2.stdout_lines[0] }}" + - get_https_no_header.binding is defined + - get_https_no_header.binding.ip == "{{ https_vars.ip }}" + - get_https_no_header.binding.port == {{ https_vars.port }} + - get_https_no_header.binding.protocol == "{{ https_vars.protocol }}" + - get_https_no_header.binding.certificateHash == "{{ thumbprint2.stdout_lines[0] }}" + +############## +### remove ### +############## +#changed true, bindings gone +- name: remove https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: https_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: remove assert changed and gone + assert: + that: + - https_no_header is changed + - https_no_header.operation_type == 'removed' + - https_no_header.binding_info is defined + - https_no_header.binding_info.ip == "{{ https_vars.ip }}" + - https_no_header.binding_info.port == {{ https_vars.port }} + - https_no_header.binding_info.protocol == "{{ https_vars.protocol }}" + - get_https_no_header.binding is not defined + +################### +### remove idem ### +################### +#change false, bindings gone +- name: idem remove https binding no header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: https_no_header + +- name: get binding info no header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + protocol: "{{ https_vars.protocol }}" + ip: "{{ https_vars.ip }}" + port: "{{ https_vars.port }}" + register: get_https_no_header + changed_when: false + +- name: idem remove assert changed and gone + assert: + that: + - https_no_header is not changed + - https_no_header.binding_info is not defined + - get_https_no_header.binding is not defined + + +################## +### WC Testing ### +################## + +# Unfortunately this does not work due to some strange errors +# that are caused when using a self signed wildcard cert. +# I'm leaving this here in case someone finds a solution in the +# future. + +# - name: add https binding wildcard with header +# win_iis_webbinding: +# name: "{{ test_iis_site_name }}" +# state: present +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# certificate_hash: "{{ thumbprint_wc.stdout_lines[0] }}" +# register: https_header + +# - name: assert changed and added +# assert: +# that: +# - https_header is changed +# - https_header.added is defined +# - https_header.added.ip == "{{ https_wc_vars.ip }}" +# - https_header.added.port == {{ https_wc_vars.port }} +# - https_header.added.protocol == "{{ https_wc_vars.protocol }}" +# - https_header.added.hostheader == "{{ https_wc_vars.header }}" +# - https_header.added.certificateHash == "{{ thumbprint_wc.stdout_lines[0] }}" + + +# - name: idem add https binding wildcard with header +# win_iis_webbinding: +# name: "{{ test_iis_site_name }}" +# state: present +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# certificate_hash: "{{ thumbprint_wc.stdout_lines[0] }}" +# register: https_header + + +# - name: cm remove wildcard https binding +# win_iis_webbinding: +# name: "{{ test_iis_site_name }}" +# state: absent +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# register: https_header +# check_mode: yes + +# - name: get binding info header +# test_get_webbindings: +# name: "{{ test_iis_site_name }}" +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# register: get_https_header +# changed_when: false + +# - name: cm remove assert changed, but still present +# assert: +# that: +# - https_header is changed +# - https_header.removed is defined +# - https_header.removed.ip == "{{ https_wc_vars.ip }}" +# - https_header.removed.port == {{ https_wc_vars.port }} +# - https_header.removed.protocol == "{{ https_wc_vars.protocol }}" +# - https_header.removed.hostheader == "{{ https_wc_vars.header }}" +# - https_header.removed.certificateHash == "{{ thumbprint_wc.stdout_lines[0] }}" +# - get_https_header.binding is defined +# - get_https_header.removed.ip == "{{ https_wc_vars.ip }}" +# - get_https_header.removed.port == {{ https_wc_vars.port }} +# - get_https_header.removed.protocol == "{{ https_wc_vars.protocol }}" +# - get_https_header.removed.hostheader == "{{ https_wc_vars.header }}" +# - get_https_header.removed.certificateHash == "{{ thumbprint_wc.stdout_lines[0] }}" + +# - name: remove wildcard https binding +# win_iis_webbinding: +# name: "{{ test_iis_site_name }}" +# state: absent +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# register: https_header + +# - name: get binding info header +# test_get_webbindings: +# name: "{{ test_iis_site_name }}" +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# register: get_https_header +# changed_when: false + + +# - name: remove assert changed and gone +# assert: +# that: +# - https_header is changed +# - https_header.removed is defined +# - https_header.removed.ip == "{{ https_wc_vars.ip }}" +# - https_header.removed.port == {{ https_wc_vars.port }} +# - https_header.removed.protocol == "{{ https_wc_vars.protocol }}" +# - https_header.removed.hostheader == "{{ https_wc_vars.header }}" +# - https_header.removed.certificateHash == "{{ thumbprint_wc.stdout_lines[0] }}" +# - get_https_header.binding is not defined + +# - name: idem remove wildcard https binding +# win_iis_webbinding: +# name: "{{ test_iis_site_name }}" +# state: absent +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# register: https_header + +# - name: get binding info header +# test_get_webbindings: +# name: "{{ test_iis_site_name }}" +# host_header: "{{ https_wc_vars.header }}" +# protocol: "{{ https_wc_vars.protocol }}" +# ip: "{{ https_wc_vars.ip }}" +# port: "{{ https_wc_vars.port }}" +# register: get_https_header +# changed_when: false + +# - name: idem remove assert changed and gone +# assert: +# that: +# - https_header is not changed +# - https_header.removed is not defined +# - get_https_header.binding is not defined diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/main.yml new file mode 100644 index 000000000..ee12ff78b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/main.yml @@ -0,0 +1,62 @@ +--- +# Cannot use win_feature to install IIS on Server 2008. +# Run a brief check and skip hosts that don't support +# that operation +#seems "raw" is the only module that works on 2008 non-r2. win_command and win_shell both failed +- name: register os version (seems integration tests don't gather this fact) + raw: powershell.exe "gwmi Win32_OperatingSystem | select -expand version" + register: os_version + changed_when: False + +- block: + - include_tasks: setup.yml + - include_tasks: http.yml + - include_tasks: https-lt6.2.yml + when: os_version.stdout_lines[0] is version('6.2','lt') + - include_tasks: https-ge6.2.yml + when: os_version.stdout_lines[0] is version('6.2','ge') + - include_tasks: failures.yml + + always: + - name: get all websites from server + raw: powershell.exe "(get-website).name" + register: existing_sites + + - name: ensure all sites are removed for clean testing + win_iis_website: + name: "{{ item }}" + state: absent + with_items: + - "{{ existing_sites.stdout_lines }}" + + - name: cleanup certreq files + ansible.windows.win_file: + path: "{{ item }}" + state: absent + with_items: + - c:\windows\temp\certreq1.txt + - c:\windows\temp\certreq2.txt + - c:\windows\temp\certreqwc.txt + - c:\windows\temp\certreqresp1.txt + - c:\windows\temp\certreqresp2.txt + - c:\windows\temp\certreqrespwc.txt + + - name: remove certs + raw: 'remove-item cert:\localmachine\my\{{ item }} -force -ea silentlycontinue' + with_items: + - "{{ thumbprint1.stdout_lines[0] }}" + - "{{ thumbprint2.stdout_lines[0] }}" + - "{{ thumbprint_wc.stdout_lines[0] }}" + + - name: remove IIS features after test + ansible.windows.win_feature: + name: Web-Server + state: absent + includ_sub_features: True + include_management_tools: True + register: feature_uninstall + + - name: reboot after feature install + ansible.windows.win_reboot: + when: feature_uninstall.reboot_required + when: os_version.stdout_lines[0] is version('6.1','gt') diff --git a/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/setup.yml b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/setup.yml new file mode 100644 index 000000000..234cc400d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_iis_webbinding/tasks/setup.yml @@ -0,0 +1,93 @@ +- name: reboot before feature install to ensure server is in clean state + ansible.windows.win_reboot: + +- name: ensure IIS features are installed + ansible.windows.win_feature: + name: Web-Server + state: present + includ_sub_features: True + include_management_tools: True + register: feature_install + +- name: reboot after feature install + ansible.windows.win_reboot: + when: feature_install.reboot_required + +- name: get all websites from server + raw: powershell.exe "(get-website).name" + register: existing_sites + +- name: ensure all sites are removed for clean testing + win_iis_website: + name: "{{ item }}" + state: absent + with_items: + - "{{ existing_sites.stdout_lines }}" + +- name: add testing site {{ test_iis_site_name }} + win_iis_website: + name: "{{ test_iis_site_name }}" + physical_path: c:\inetpub\wwwroot + +- name: ensure all bindings are removed prior to starting testing + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + protocol: "{{ item.protocol }}" + port: "{{ item.port }}" + with_items: + - {protocol: http, port: 80} + - {protocol: https, port: 443} + +- name: copy certreq file + ansible.windows.win_copy: + content: |- + [NewRequest] + Subject = "CN={{ item.name }}" + KeyLength = 2048 + KeyAlgorithm = RSA + MachineKeySet = true + RequestType = Cert + dest: "{{ item.dest }}" + with_items: + - {name: test.com, dest: 'c:\windows\temp\certreq1.txt'} + - {name: test1.com, dest: 'c:\windows\temp\certreq2.txt'} + - {name: '*.test.com', dest: 'c:\windows\temp\certreqwc.txt'} + +- name: make sure response files are absent + ansible.windows.win_file: + path: "{{ item }}" + state: absent + with_items: + - 'c:\windows\temp\certreqresp1.txt' + - 'c:\windows\temp\certreqresp2.txt' + - 'c:\windows\temp\certreqrespwc.txt' + +- name: create self signed cert from certreq + ansible.windows.win_command: certreq -new -machine {{ item.req }} {{ item.resp }} + with_items: + - {req: 'c:\windows\temp\certreq1.txt', resp: 'c:\windows\temp\certreqresp1.txt'} + - {req: 'c:\windows\temp\certreq2.txt', resp: 'c:\windows\temp\certreqresp2.txt'} + - {req: 'c:\windows\temp\certreqwc.txt', resp: 'c:\windows\temp\certreqrespwc.txt'} + +- name: register certificate thumbprint1 + raw: '(gci Cert:\LocalMachine\my | ? {$_.subject -eq "CN=test.com"})[0].Thumbprint' + register: thumbprint1 + +- name: register certificate thumbprint2 + raw: '(gci Cert:\LocalMachine\my | ? {$_.subject -eq "CN=test1.com"})[0].Thumbprint' + register: thumbprint2 + +- name: register certificate thumbprint_wc + raw: '(gci Cert:\LocalMachine\my | ? {$_.subject -eq "CN=*.test.com"})[0].Thumbprint' + register: thumbprint_wc + +- debug: + var: thumbprint1.stdout + verbosity: 1 +- debug: + var: thumbprint2.stdout + verbosity: 1 +- debug: + var: thumbprint_wc.stdout + verbosity: 1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/aliases b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/library/win_inet_proxy_info.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/library/win_inet_proxy_info.ps1 new file mode 100644 index 000000000..76bf03ec9 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/library/win_inet_proxy_info.ps1 @@ -0,0 +1,275 @@ +#!powershell + +# Copyright: (c) 2019, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#Requires -Module Ansible.ModuleUtils.AddType + +$spec = @{ + options = @{ + connection = @{ type = "str" } + } + supports_check_mode = $true +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) + +$connection = $module.Params.connection + +$win_inet_invoke = @' +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; + +namespace Ansible.WinINetProxyInfo +{ + internal class NativeHelpers + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public class INTERNET_PER_CONN_OPTION_LISTW : IDisposable + { + public UInt32 dwSize; + public IntPtr pszConnection; + public UInt32 dwOptionCount; + public UInt32 dwOptionError; + public IntPtr pOptions; + + public INTERNET_PER_CONN_OPTION_LISTW() + { + dwSize = (UInt32)Marshal.SizeOf(this); + } + + public void Dispose() + { + if (pszConnection != IntPtr.Zero) + Marshal.FreeHGlobal(pszConnection); + if (pOptions != IntPtr.Zero) + Marshal.FreeHGlobal(pOptions); + GC.SuppressFinalize(this); + } + ~INTERNET_PER_CONN_OPTION_LISTW() { this.Dispose(); } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public class INTERNET_PER_CONN_OPTIONW : IDisposable + { + public INTERNET_PER_CONN_OPTION dwOption; + public ValueUnion Value; + + [StructLayout(LayoutKind.Explicit)] + public class ValueUnion + { + [FieldOffset(0)] + public UInt32 dwValue; + + [FieldOffset(0)] + public IntPtr pszValue; + + [FieldOffset(0)] + public System.Runtime.InteropServices.ComTypes.FILETIME ftValue; + } + + public void Dispose() + { + // We can't just check if Value.pszValue is not IntPtr.Zero as the union means it could be set even + // when the value is a UInt32 or FILETIME. We check against a known string option type and only free + // the value in those cases. + List<INTERNET_PER_CONN_OPTION> stringOptions = new List<INTERNET_PER_CONN_OPTION> + { + { INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL }, + { INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS }, + { INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER } + }; + if (Value != null && Value.pszValue != IntPtr.Zero && stringOptions.Contains(dwOption)) + Marshal.FreeHGlobal(Value.pszValue); + GC.SuppressFinalize(this); + } + ~INTERNET_PER_CONN_OPTIONW() { this.Dispose(); } + } + + public enum INTERNET_OPTION : uint + { + INTERNET_OPTION_PER_CONNECTION_OPTION = 75, + } + + public enum INTERNET_PER_CONN_OPTION : uint + { + INTERNET_PER_CONN_FLAGS = 1, + INTERNET_PER_CONN_PROXY_SERVER = 2, + INTERNET_PER_CONN_PROXY_BYPASS = 3, + INTERNET_PER_CONN_AUTOCONFIG_URL = 4, + INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5, + INTERNET_PER_CONN_FLAGS_UI = 10, // IE8+ - Included with Windows 7 and Server 2008 R2 + } + + [Flags] + public enum PER_CONN_FLAGS : uint + { + PROXY_TYPE_DIRECT = 0x00000001, + PROXY_TYPE_PROXY = 0x00000002, + PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, + PROXY_TYPE_AUTO_DETECT = 0x00000008, + } + } + + internal class NativeMethods + { + [DllImport("Wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool InternetQueryOptionW( + IntPtr hInternet, + NativeHelpers.INTERNET_OPTION dwOption, + SafeMemoryBuffer lpBuffer, + ref UInt32 lpdwBufferLength); + } + + internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid + { + public SafeMemoryBuffer() : base(true) { } + public SafeMemoryBuffer(int cb) : base(true) + { + base.SetHandle(Marshal.AllocHGlobal(cb)); + } + public SafeMemoryBuffer(IntPtr handle) : base(true) + { + base.SetHandle(handle); + } + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + protected override bool ReleaseHandle() + { + Marshal.FreeHGlobal(handle); + return true; + } + } + + public class WinINetProxy + { + private string Connection; + + public string AutoConfigUrl; + public bool AutoDetect; + public string Proxy; + public string ProxyBypass; + + public WinINetProxy(string connection) + { + Connection = connection; + Refresh(); + } + + public void Refresh() + { + using (var connFlags = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS_UI)) + using (var autoConfigUrl = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_AUTOCONFIG_URL)) + using (var server = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_SERVER)) + using (var bypass = CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_PROXY_BYPASS)) + { + NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options = new NativeHelpers.INTERNET_PER_CONN_OPTIONW[] + { + connFlags, autoConfigUrl, server, bypass + }; + + try + { + QueryOption(options, Connection); + } + catch (Win32Exception e) + { + if (e.NativeErrorCode == 87) // ERROR_INVALID_PARAMETER + { + // INTERNET_PER_CONN_FLAGS_UI only works for IE8+, try the fallback in case we are still working + // with an ancient version. + connFlags.dwOption = NativeHelpers.INTERNET_PER_CONN_OPTION.INTERNET_PER_CONN_FLAGS; + QueryOption(options, Connection); + } + else + throw; + } + + NativeHelpers.PER_CONN_FLAGS flags = (NativeHelpers.PER_CONN_FLAGS)connFlags.Value.dwValue; + + AutoConfigUrl = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_PROXY_URL) + ? Marshal.PtrToStringUni(autoConfigUrl.Value.pszValue) : null; + AutoDetect = flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_AUTO_DETECT); + if (flags.HasFlag(NativeHelpers.PER_CONN_FLAGS.PROXY_TYPE_PROXY)) + { + Proxy = Marshal.PtrToStringUni(server.Value.pszValue); + ProxyBypass = Marshal.PtrToStringUni(bypass.Value.pszValue); + } + else + { + Proxy = null; + ProxyBypass = null; + } + } + } + + internal static NativeHelpers.INTERNET_PER_CONN_OPTIONW CreateConnOption(NativeHelpers.INTERNET_PER_CONN_OPTION option) + { + return new NativeHelpers.INTERNET_PER_CONN_OPTIONW + { + dwOption = option, + Value = new NativeHelpers.INTERNET_PER_CONN_OPTIONW.ValueUnion(), + }; + } + + internal static void QueryOption(NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection = null) + { + using (NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList = new NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW()) + using (SafeMemoryBuffer optionListPtr = MarshalOptionList(optionList, options, connection)) + { + UInt32 bufferSize = optionList.dwSize; + if (!NativeMethods.InternetQueryOptionW( + IntPtr.Zero, + NativeHelpers.INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, + optionListPtr, + ref bufferSize)) + { + throw new Win32Exception(); + } + + for (int i = 0; i < options.Length; i++) + { + IntPtr opt = IntPtr.Add(optionList.pOptions, i * Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW))); + NativeHelpers.INTERNET_PER_CONN_OPTIONW option = (NativeHelpers.INTERNET_PER_CONN_OPTIONW)Marshal.PtrToStructure(opt, + typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW)); + options[i].Value = option.Value; + option.Value = null; // Stops the GC from freeing the same memory twice + } + } + } + + internal static SafeMemoryBuffer MarshalOptionList(NativeHelpers.INTERNET_PER_CONN_OPTION_LISTW optionList, + NativeHelpers.INTERNET_PER_CONN_OPTIONW[] options, string connection) + { + optionList.pszConnection = Marshal.StringToHGlobalUni(connection); + optionList.dwOptionCount = (UInt32)options.Length; + + int optionSize = Marshal.SizeOf(typeof(NativeHelpers.INTERNET_PER_CONN_OPTIONW)); + optionList.pOptions = Marshal.AllocHGlobal(optionSize * options.Length); + for (int i = 0; i < options.Length; i++) + { + IntPtr option = IntPtr.Add(optionList.pOptions, i * optionSize); + Marshal.StructureToPtr(options[i], option, false); + } + + SafeMemoryBuffer optionListPtr = new SafeMemoryBuffer((int)optionList.dwSize); + Marshal.StructureToPtr(optionList, optionListPtr.DangerousGetHandle(), false); + return optionListPtr; + } + } +} +'@ +Add-CSharpType -References $win_inet_invoke -AnsibleModule $module + +$proxy = New-Object -TypeName Ansible.WinINetProxyInfo.WinINetProxy -ArgumentList @(, $connection) +$module.Result.auto_config_url = $proxy.AutoConfigUrl +$module.Result.auto_detect = $proxy.AutoDetect +$module.Result.proxy = $proxy.Proxy +$module.Result.bypass = $proxy.ProxyBypass + +$module.ExitJson() diff --git a/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/library/win_phonebook_entry.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/library/win_phonebook_entry.ps1 new file mode 100644 index 000000000..e449af8ea --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/library/win_phonebook_entry.ps1 @@ -0,0 +1,523 @@ +#!powershell + +# Copyright: (c) 2019, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#Requires -Module Ansible.ModuleUtils.AddType + +# This is a very basic skeleton of a possible Windows module for managing RAS connections. It is mostly barebones +# to enable testing for win_inet_proxy but I've done a bit of extra work in the PInvoke space to possible expand +# sometime in the future. + +$spec = @{ + options = @{ + device_type = @{ + type = "str" + choices = @("atm", "framerelay", "generic", "rda", "isdn", "modem", "pad", + "parallel", "pppoe", "vpn", "serial", "sonet", "sw56", "x25") + } + device_name = @{ type = "str" } + framing_protocol = @{ type = "str"; choices = @("ppp", "ras", "slip") } + name = @{ type = "str"; required = $true } + options = @{ type = "list" } + state = @{ type = "str"; choices = @("absent", "present"); default = "present" } + type = @{ type = "str"; choices = @("broadband", "direct", "phone", "vpn") } + } + required_if = @( + , @("state", "present", @("type", "device_name", "device_type", "framing_protocol")) + ) + supports_check_mode = $false +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) + +$device_type = $module.Params.device_type +$device_name = $module.Params.device_name +$framing_protocol = $module.Params.framing_protocol +$name = $module.Params.name +$options = $module.Params.options +$state = $module.Params.state +$type = $module.Params.type + +$module.Result.guid = [System.Guid]::Empty + +$win_ras_invoke = @' +using System; +using System.Runtime.InteropServices; + +namespace Ansible.WinPhonebookEntry +{ + public class NativeHelpers + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public class RASENTRYW + { + public UInt32 dwSize; + public RasEntryOptions dwfOptions; + public UInt32 dwCountryId; + public UInt32 dwCountryCode; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)] public string szAreaCode; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] public string szLocalPhoneNumber; + public UInt32 dwAlternateOffset; + public RASIPADDR ipaddr; + public RASIPADDR ipaddrDns; + public RASIPADDR ipaddrDnsAlt; + public RASIPADDR ipaddrWins; + public RASIPADDR ipaddrWinsAlt; + public UInt32 dwFrameSize; + public RasNetProtocols dwfNetProtocols; + public RasFramingProtocol dwFramingProtocol; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szScript; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szAutodialDll; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szAutodialFunc; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)] public string szDeviceType; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] public string szDeviceName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 33)] public string szX25PadType; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string szX25Address; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string szX25Facilities; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string szX25UserData; + public UInt32 dwChannels; + public UInt32 dwReserved1; + public UInt32 dwReserved2; + public UInt32 dwSubEntries; + public RasDialMode dwDialMode; + public UInt32 dwDialExtraPercent; + public UInt32 dwDialExtraSampleSeconds; + public UInt32 dwHangUpExtraPercent; + public UInt32 dwHangUpExtraSampleSeconds; + public UInt32 dwIdleDisconnectSeconds; + public RasEntryTypes dwType; + public RasEntryEncryption dwEntryptionType; + public UInt32 dwCustomAuthKey; + public Guid guidId; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szCustomDialDll; + public RasVpnStrategy dwVpnStrategy; + public RasEntryOptions2 dwfOptions2; + public UInt32 dwfOptions3; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szDnsSuffix; + public UInt32 dwTcpWindowSize; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szPrerequisitePbk; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szPrerequisiteEntry; + public UInt32 dwRedialCount; + public UInt32 dwRedialPause; + public RASIPV6ADDR ipv6addrDns; + public RASIPV6ADDR ipv6addrDnsAlt; + public UInt32 dwIPv4InterfaceMatrix; + public UInt32 dwIPv6InterfaceMatrix; + // Server 2008 R2 / Windows 7+ + // We cannot include these fields when running in Server 2008 as it will break the SizeOf calc of the struct +#if !LONGHORN + public RASIPV6ADDR ipv6addr; + public UInt32 dwIPv6PrefixLength; + public UInt32 dwNetworkOutageTime; +#endif + + public RASENTRYW() + { + this.dwSize = (UInt32)Marshal.SizeOf(this); + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct RASIPADDR + { + public byte a; + public byte b; + public byte c; + public byte d; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RASIPV6ADDR + { + byte a; + byte b; + byte c; + byte d; + byte e; + byte f; + byte g; + byte h; + byte i; + byte j; + byte k; + byte l; + byte m; + byte n; + byte o; + byte p; + } + + public enum RasDialMode : uint + { + RASEDM_DialAll = 1, + RASEDM_DialAsNeeded = 2, + } + + public enum RasEntryEncryption : uint + { + ET_None = 0, + ET_Require = 1, + ET_RequireMax = 2, + ET_Optional = 3 + } + + [Flags] + public enum RasEntryOptions : uint + { + RASEO_UseCountryAndAreaCodes = 0x00000001, + RASEO_SpecificIpAddr = 0x00000002, + RASEO_SpecificNameServers = 0x00000004, + RASEO_IpHeaderCompression = 0x00000008, + RASEO_RemoteDefaultGateway = 0x00000010, + RASEO_DisableLcpExtensions = 0x00000020, + RASEO_TerminalBeforeDial = 0x00000040, + RASEO_TerminalAfterDial = 0x00000080, + RASEO_ModemLights = 0x00000100, + RASEO_SwCompression = 0x00000200, + RASEO_RequireEncrptedPw = 0x00000400, + RASEO_RequireMsEncrptedPw = 0x00000800, + RASEO_RequireDataEncrption = 0x00001000, + RASEO_NetworkLogon = 0x00002000, + RASEO_UseLogonCredentials = 0x00004000, + RASEO_PromoteAlternates = 0x00008000, + RASEO_SecureLocalFiles = 0x00010000, + RASEO_RequireEAP = 0x00020000, + RASEO_RequirePAP = 0x00040000, + RASEO_RequireSPAP = 0x00080000, + RASEO_Custom = 0x00100000, + RASEO_PreviewPhoneNumber = 0x00200000, + RASEO_SharedPhoneNumbers = 0x00800000, + RASEO_PreviewUserPw = 0x01000000, + RASEO_PreviewDomain = 0x02000000, + RASEO_ShowDialingProgress = 0x04000000, + RASEO_RequireCHAP = 0x08000000, + RASEO_RequireMsCHAP = 0x10000000, + RASEO_RequireMsCHAP2 = 0x20000000, + RASEO_RequireW95MSCHAP = 0x40000000, + RASEO_CustomScript = 0x80000000, + } + + [Flags] + public enum RasEntryOptions2 : uint + { + RASEO2_None = 0x00000000, + RASEO2_SecureFileAndPrint = 0x00000001, + RASEO2_SecureClientForMSNet = 0x00000002, + RASEO2_DontNegotiateMultilink = 0x00000004, + RASEO2_DontUseRasCredentials = 0x00000008, + RASEO2_UsePreSharedKey = 0x00000010, + RASEO2_Internet = 0x00000020, + RASEO2_DisableNbtOverIP = 0x00000040, + RASEO2_UseGlobalDeviceSettings = 0x00000080, + RASEO2_ReconnectIfDropped = 0x00000100, + RASEO2_SharePhoneNumbers = 0x00000200, + RASEO2_SecureRoutingCompartment = 0x00000400, + RASEO2_UseTypicalSettings = 0x00000800, + RASEO2_IPv6SpecificNameServers = 0x00001000, + RASEO2_IPv6RemoteDefaultGateway = 0x00002000, + RASEO2_RegisterIpWithDNS = 0x00004000, + RASEO2_UseDNSSuffixForRegistration = 0x00008000, + RASEO2_IPv4ExplicitMetric = 0x00010000, + RASEO2_IPv6ExplicitMetric = 0x00020000, + RASEO2_DisableIKENameEkuCheck = 0x00040000, + // Server 2008 R2 / Windows 7+ + RASEO2_DisableClassBasedStaticRoute = 0x00800000, + RASEO2_SpecificIPv6Addr = 0x01000000, + RASEO2_DisableMobility = 0x02000000, + RASEO2_RequireMachineCertificates = 0x04000000, + // Server 2012 / Windows 8+ + RASEO2_UsePreSharedKeyForIkev2Initiator = 0x00800000, + RASEO2_UsePreSharedKeyForIkev2Responder = 0x01000000, + RASEO2_CacheCredentials = 0x02000000, + // Server 2012 R2 / Windows 8.1+ + RASEO2_AutoTriggerCapable = 0x04000000, + RASEO2_IsThirdPartyProfile = 0x08000000, + RASEO2_AuthTypeIsOtp = 0x10000000, + // Server 2016 / Windows 10+ + RASEO2_IsAlwaysOn = 0x20000000, + RASEO2_IsPrivateNetwork = 0x40000000, + } + + public enum RasEntryTypes : uint + { + RASET_Phone = 1, + RASET_Vpn = 2, + RASET_Direct = 3, + RASET_Internet = 4, + RASET_Broadband = 5, + } + + public enum RasFramingProtocol : uint + { + RASFP_Ppp = 0x00000001, + RASFP_Slip = 0x00000002, + RASFP_Ras = 0x00000004 + } + + [Flags] + public enum RasNetProtocols : uint + { + RASNP_NetBEUI = 0x00000001, + RASNP_Ipx = 0x00000002, + RASNP_Ip = 0x00000004, + RASNP_Ipv6 = 0x00000008 + } + + public enum RasVpnStrategy : uint + { + VS_Default = 0, + VS_PptpOnly = 1, + VS_PptpFirst = 2, + VS_L2tpOnly = 3, + VS_L2tpFirst = 4, + VS_SstpOnly = 5, + VS_SstpFirst = 6, + VS_Ikev2Only = 7, + VS_Ikev2First = 8, + VS_GREOnly = 9, + VS_PptpSstp = 12, + VS_L2tpSstp = 13, + VS_Ikev2Sstp = 14, + } + } + + internal class NativeMethods + { + [DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)] + public static extern UInt32 RasDeleteEntryW( + string lpszPhonebook, + string lpszEntry); + + [DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)] + public static extern UInt32 RasGetEntryPropertiesW( + string lpszPhonebook, + string lpszEntry, + [In, Out] NativeHelpers.RASENTRYW lpRasEntry, + ref UInt32 dwEntryInfoSize, + IntPtr lpbDeviceInfo, + ref UInt32 dwDeviceInfoSize); + + [DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)] + public static extern UInt32 RasSetEntryPropertiesW( + string lpszPhonebook, + string lpszEntry, + NativeHelpers.RASENTRYW lpRasEntry, + UInt32 dwEntryInfoSize, + IntPtr lpbDeviceInfo, + UInt32 dwDeviceInfoSize); + + [DllImport("Rasapi32.dll", CharSet = CharSet.Unicode)] + public static extern UInt32 RasValidateEntryNameW( + string lpszPhonebook, + string lpszEntry); + } + + public class Phonebook + { + public static void CreateEntry(string entry, NativeHelpers.RASENTRYW details) + { + UInt32 res = NativeMethods.RasSetEntryPropertiesW(null, entry, details, + details.dwSize, IntPtr.Zero, 0); + + if (res != 0) + throw new Exception(String.Format("RasSetEntryPropertiesW({0}) failed {1}", entry, res)); + } + + public static void DeleteEntry(string entry) + { + UInt32 res = NativeMethods.RasDeleteEntryW(null, entry); + if (res != 0) + throw new Exception(String.Format("RasDeleteEntryW({0}) failed {1}", entry, res)); + } + + public static NativeHelpers.RASENTRYW GetEntry(string entry) + { + NativeHelpers.RASENTRYW details = new NativeHelpers.RASENTRYW(); + UInt32 dwEntryInfoSize = details.dwSize; + UInt32 dwDeviceInfoSize = 0; + + UInt32 res = NativeMethods.RasGetEntryPropertiesW(null, entry, details, ref dwEntryInfoSize, + IntPtr.Zero, ref dwDeviceInfoSize); + + if (res != 0) + throw new Exception(String.Format("RasGetEntryPropertiesW({0}) failed {1}", entry, res)); + + return details; + } + + public static bool IsValidEntry(string entry) + { + // 183 == ENTRY_ALREADY_EXISTS + return NativeMethods.RasValidateEntryNameW(null, entry) == 183; + } + } +} +'@ + +$add_type_params = @{ + Reference = $win_ras_invoke + AnsibleModule = $module +} +# We need to set a custom compile option when running on Server 2008 due to the change in the RASENTRYW structure +$os_version = [Version](Get-Item -LiteralPath $env:SystemRoot\System32\kernel32.dll).VersionInfo.ProductVersion +if ($os_version -lt [Version]"6.1") { + $add_type_params.CompileSymbols = @("LONGHORN") +} +Add-CSharpType @add_type_params + +$exists = [Ansible.WinPhonebookEntry.Phonebook]::IsValidEntry($name) +if ($exists) { + $entry = [Ansible.WinPhonebookEntry.Phonebook]::GetEntry($name) + $module.Result.guid = $entry.guidId +} + +if ($state -eq "present") { + # Convert the input values to enum values + $expected_type = switch ($type) { + "broadband" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Broadband } + "direct" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Direct } + "phone" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Phone } + "vpn" { [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryTypes]::RASET_Vpn } + } + + $expected_framing_protocol = switch ($framing_protocol) { + "ppp" { [Ansible.WinPhonebookEntry.NativeHelpers+RasFramingProtocol]::RASFP_Ppp } + "ras" { [Ansible.WinPhonebookEntry.NativeHelpers+RasFramingProtocol]::RASFP_Ras } + "slip" { [Ansible.WinPhonebookEntry.NativeHelpers+RasFramingProtocol]::RASFP_Slip } + } + + $expected_options1 = [System.Collections.Generic.List`1[String]]@() + $expected_options2 = [System.Collections.Generic.List`1[String]]@() + $invalid_options = [System.Collections.Generic.List`1[String]]@() + foreach ($option in $options) { + # See https://msdn.microsoft.com/en-us/25c46850-4fb7-47a9-9645-139f0e869559 for more info on the options + # TODO: some of these options are set to indicate entries in RASENTRYW, we should automatically add them + # based on the input values. + switch ($option) { + # dwfOptions + "use_country_and_area_codes" { $expected_options1.Add("RASEO_UseCountryAndAreaCode") } + "specific_ip_addr" { $expected_options1.Add("RASEO_SpecificIpAddr") } + "specific_name_servers" { $expected_options1.Add("RASEO_SpecificNameServers") } + "ip_header_compression" { $expected_options1.Add("RASEO_IpHeaderCompression") } + "remote_default_gateway" { $expected_options1.Add("RASEO_RemoteDefaultGateway") } + "disable_lcp_extensions" { $expected_options1.Add("RASEO_DisableLcpExtensions") } + "terminal_before_dial" { $expected_options1.Add("RASEO_TerminalBeforeDial") } + "terminal_after_dial" { $expected_options1.Add("RASEO_TerminalAfterDial") } + "modem_lights" { $expected_options1.Add("RASEO_ModemLights") } + "sw_compression" { $expected_options1.Add("RASEO_SwCompression") } + "require_encrypted_password" { $expected_options1.Add("RASEO_RequireEncrptedPw") } + "require_ms_encrypted_password" { $expected_options1.Add("RASEO_RequireMsEncrptedPw") } + "require_data_encryption" { $expected_options1.Add("RASEO_RequireDataEncrption") } + "network_logon" { $expected_options1.Add("RASEO_NetworkLogon") } + "use_logon_credentials" { $expected_options1.Add("RASEO_UseLogonCredentials") } + "promote_alternates" { $expected_options1.Add("RASEO_PromoteAlternates") } + "secure_local_files" { $expected_options1.Add("RASEO_SecureLocalFiles") } + "require_eap" { $expected_options1.Add("RASEO_RequireEAP") } + "require_pap" { $expected_options1.Add("RASEO_RequirePAP") } + "require_spap" { $expected_options1.Add("RASEO_RequireSPAP") } + "custom" { $expected_options1.Add("RASEO_Custom") } + "preview_phone_number" { $expected_options1.Add("RASEO_PreviewPhoneNumber") } + "shared_phone_numbers" { $expected_options1.Add("RASEO_SharedPhoneNumbers") } + "preview_user_password" { $expected_options1.Add("RASEO_PreviewUserPw") } + "preview_domain" { $expected_options1.Add("RASEO_PreviewDomain") } + "show_dialing_progress" { $expected_options1.Add("RASEO_ShowDialingProgress") } + "require_chap" { $expected_options1.Add("RASEO_RequireCHAP") } + "require_ms_chap" { $expected_options1.Add("RASEO_RequireMsCHAP") } + "require_ms_chap2" { $expected_options1.Add("RASEO_RequireMsCHAP2") } + "require_w95_ms_chap" { $expected_options1.Add("RASEO_RequireW95MSCHAP") } + "custom_script" { $expected_options1.Add("RASEO_CustomScript") } + # dwfOptions2 + "secure_file_and_print" { $expected_options2.Add("RASEO2_SecureFileAndPrint") } + "secure_client_for_ms_net" { $expected_options2.Add("RASEO2_SecureClientForMSNet") } + "dont_negotiate_multilink" { $expected_options2.Add("RASEO2_DontNegotiateMultilink") } + "dont_use_ras_credential" { $expected_options2.Add("RASEO2_DontUseRasCredentials") } + "use_pre_shared_key" { $expected_options2.Add("RASEO2_UsePreSharedKey") } + "internet" { $expected_options2.Add("RASEO2_Internet") } + "disable_nbt_over_ip" { $expected_options2.Add("RASEO2_DisableNbtOverIP") } + "use_global_device_settings" { $expected_options2.Add("RASEO2_UseGlobalDeviceSettings") } + "reconnect_if_dropped" { $expected_options2.Add("RASEO2_ReconnectIfDropped") } + "share_phone_numbers" { $expected_options2.Add("RASEO2_SharePhoneNumbers") } + "secure_routing_compartment" { $expected_options2.Add("RASEO2_SecureRoutingCompartment") } + "use_typical_settings" { $expected_options2.Add("RASEO2_UseTypicalSettings") } + "ipv6_specific_name_servers" { $expected_options2.Add("RASEO2_IPv6SpecificNameServers") } + "ipv6_remote_default_gateway" { $expected_options2.Add("RASEO2_IPv6RemoteDefaultGateway") } + "register_ip_with_dns" { $expected_options2.Add("RASEO2_RegisterIpWithDNS") } + "use_dns_suffix_for_registration" { $expected_options2.Add("RASEO2_UseDNSSuffixForRegistration") } + "ipv4_explicit_metric" { $expected_options2.Add("RASEO2_IPv4ExplicitMetric") } + "ipv6_explicit_metric" { $expected_options2.Add("RASEO2_IPv6ExplicitMetric") } + "disable_ike_name_eku_check" { $expected_options2.Add("RASEO2_DisableIKENameEkuCheck") } + # TODO: Version check for the below, OS Version >= 6.1 + "disable_class_based_static_route" { $expected_options2.Add("RASEO2_DisableClassBasedStaticRoute") } + "specific_ipv6_addr" { $expected_options2.Add("RASEO2_SpecificIPv6Addr") } + "disable_mobility" { $expected_options2.Add("RASEO2_DisableMobility") } + "require_machine_certificates" { $expected_options2.Add("RASEO2_RequireMachineCertificates") } + # TODO: Version check for the below, OS Version >= 6.2 + "use_pre_shared_key_for_ikev2_initiator" { $expected_options2.Add("RASEO2_UsePreSharedKeyForIkev2Initiator") } + "use_pre_shared_key_for_ikev2_responder" { $expected_options2.Add("RASEO2_UsePreSharedKeyForIkev2Responder") } + "cache_credentials" { $expected_options2.Add("RASEO2_CacheCredentials") } + # TODO: Version check for the below, OS Version >= 6.3 + "auto_trigger_capable" { $expected_options2.Add("RASEO2_AutoTriggerCapable") } + "is_third_party_profile" { $expected_options2.Add("RASEO2_IsThirdPartyProfile") } + "auth_type_is_otp" { $expected_options2.Add("RASEO2_AuthTypeIsOtp") } + # TODO: Version check for the below, OS Version >= 10.0 + "is_always_on" { $expected_options2.Add("RASEO2_IsAlwaysOn") } + "is_private_network" { $expected_options2.Add("RASEO2_IsPrivateNetwork") } + default { $invalid_options.Add($option) } + } + } + if ($invalid_options.Count -gt 0) { + $module.FailJson("Encountered invalid options: $($invalid_options -join ", ")") + } + $expected_options1 = [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryOptions]($expected_options1 -join ", ") + $expected_options2 = [Ansible.WinPhonebookEntry.NativeHelpers+RasEntryOptions2]($expected_options2 -join ", ") + + $property_map = @{ + szDeviceName = $device_name + szDeviceType = $device_type + dwFramingProtocol = $expected_framing_protocol + dwfOptions = $expected_options1 + dwfOptions2 = $expected_options2 + dwType = $expected_type + } + + if (-not $exists) { + $entry = New-Object -TypeName Ansible.WinPhonebookEntry.NativeHelpers+RASENTRYW + foreach ($kvp in $property_map.GetEnumerator()) { + $entry."$($kvp.Key)" = $kvp.Value + } + + [Ansible.WinPhonebookEntry.Phonebook]::CreateEntry($name, $entry) + $module.Result.changed = $true + + # Once created we then get the entry object again to retrieve the unique GUID ID to return + $entry = [Ansible.WinPhonebookEntry.Phonebook]::GetEntry($name) + $module.Result.guid = $entry.guidId + } + else { + $entry = [Ansible.WinPhonebookEntry.Phonebook]::GetEntry($name) + $changed = $false + foreach ($kvp in $property_map.GetEnumerator()) { + $key = $kvp.Key + $actual_value = $entry.$key + if ($actual_value -ne $kvp.Value) { + $entry.$key = $kvp.Value + $changed = $true + } + } + + if ($changed) { + [Ansible.WinPhonebookEntry.Phonebook]::CreateEntry($name, $entry) + $module.Result.changed = $true + } + } +} +else { + if ($exists) { + [Ansible.WinPhonebookEntry.Phonebook]::DeleteEntry($name) + $module.Result.changed = $true + } +} + +$module.ExitJson() diff --git a/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/tasks/main.yml new file mode 100644 index 000000000..f92a60eab --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: make sure we start the tests with the defaults + win_inet_proxy: + +- block: + - name: run tests + include_tasks: tests.yml + + always: + - name: reset proxy back to defaults + win_inet_proxy: + + - name: remove phonebook entry + win_phonebook_entry: + name: Ansible Test Dialup + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/tasks/tests.yml new file mode 100644 index 000000000..47ddfbe2e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_inet_proxy/tasks/tests.yml @@ -0,0 +1,308 @@ +--- +- name: ensure we fail when proxy is not set with bypass + win_inet_proxy: + bypass: abc + register: fail_bypass + failed_when: 'fail_bypass.msg != "missing parameter(s) required by ''bypass'': proxy"' + +- name: ensure we fail if an invalid protocol is specified + win_inet_proxy: + proxy: + fail1: fail + fail2: fail + register: fail_proxy + failed_when: 'fail_proxy.msg != "Invalid keys found in proxy: fail1, fail2. Valid keys are http, https, ftp, socks."' + +- name: ensure we fail if invalid value is set + win_inet_proxy: + proxy: fake=proxy + register: fail_invalid + failed_when: fail_invalid.msg != "Unknown error when trying to set auto_config_url '', proxy 'fake=proxy', or bypass ''" + +- name: ensure we fail if an invalid connection is set + win_inet_proxy: + connection: Fake Connection + register: fail_connection + failed_when: fail_connection.msg != "The connection 'Fake Connection' does not exist." + +- name: check proxy is still set to Direct access + win_inet_proxy_info: + register: fail_invalid_actual + failed_when: fail_invalid_actual.proxy == 'fake=proxy' + +- name: disable auto detect (check) + win_inet_proxy: + auto_detect: no + register: disable_auto_detect_check + check_mode: yes + +- name: get result of disable auto detect (check) + win_inet_proxy_info: + register: disable_auto_detect_actual_check + +- name: assert disable auto detect (check) + assert: + that: + - disable_auto_detect_check is changed + - disable_auto_detect_actual_check.auto_detect + +- name: disable auto detect + win_inet_proxy: + auto_detect: no + register: disable_auto_detect + +- name: get result of disable auto detect + win_inet_proxy_info: + register: disable_auto_detect_actual + +- name: assert disable auto detect + assert: + that: + - disable_auto_detect is changed + - not disable_auto_detect_actual.auto_detect + +- name: disable auto detect (idempotent) + win_inet_proxy: + auto_detect: no + register: disable_auto_detect_again + +- name: assert disable auto detect (idempotent) + assert: + that: + - not disable_auto_detect_again is changed + +- name: set auto config url + win_inet_proxy: + auto_config_url: http://ansible.com/proxy.pac + register: set_auto_url + +- name: get result of set auto config url + win_inet_proxy_info: + register: set_auto_url_actual + +- name: assert set auto config url + assert: + that: + - set_auto_url is changed + - set_auto_url_actual.auto_detect + - set_auto_url_actual.auto_config_url == 'http://ansible.com/proxy.pac' + +- name: set auto config url (idempotent) + win_inet_proxy: + auto_config_url: http://ansible.com/proxy.pac + register: set_auto_url_again + +- name: set auto config url (idempotent) + assert: + that: + - not set_auto_url_again is changed + +- name: set a proxy using a string + win_inet_proxy: + proxy: proxyhost + register: proxy_str + +- name: get result of set a proxy using a string + win_inet_proxy_info: + register: proxy_str_actual + +- name: assert set a proxy using a string + assert: + that: + - proxy_str is changed + - proxy_str_actual.auto_detect + - proxy_str_actual.auto_config_url == None + - proxy_str_actual.proxy == 'proxyhost' + +- name: set a proxy using a string (idempotent) + win_inet_proxy: + proxy: proxyhost + register: proxy_str_again + +- name: assert set a proxy using a string (idempotent) + assert: + that: + - not proxy_str_again is changed + +- name: change a proxy and set bypass + win_inet_proxy: + proxy: proxyhost:8080 + bypass: + - abc + - def + - <local> + register: change_proxy + +- name: get result of change a proxy and set bypass + win_inet_proxy_info: + register: change_proxy_actual + +- name: assert change a proxy and set bypass + assert: + that: + - change_proxy is changed + - change_proxy_actual.proxy == 'proxyhost:8080' + - change_proxy_actual.bypass == 'abc;def;<local>' + +- name: change a proxy and set bypass (idempotent) + win_inet_proxy: + proxy: proxyhost:8080 + bypass: abc,def,<local> + register: change_proxy_again + +- name: assert change a proxy and set bypass (idempotent) + assert: + that: + - not change_proxy_again is changed + +- name: change bypass list + win_inet_proxy: + proxy: proxyhost:8080 + bypass: + - abc + - <-loopback> + register: change_bypass + +- name: get reuslt of change bypass list + win_inet_proxy_info: + register: change_bypass_actual + +- name: assert change bypass list + assert: + that: + - change_bypass is changed + - change_bypass_actual.proxy == 'proxyhost:8080' + - change_bypass_actual.bypass == 'abc;<-loopback>' + +- name: remove proxy without options + win_inet_proxy: + register: remove_proxy + +- name: get result of remove proxy without options + win_inet_proxy_info: + register: remove_proxy_actual + +- name: assert remove proxy without options + assert: + that: + - remove_proxy is changed + - remove_proxy_actual.auto_detect == True + - remove_proxy_actual.auto_config_url == None + - remove_proxy_actual.proxy == None + - remove_proxy_actual.bypass == None + +- name: remove proxy without options (idempotent) + win_inet_proxy: + register: remove_proxy_again + +- name: assert remove proxy without options (idempotent) + assert: + that: + - not remove_proxy_again is changed + +- name: set proxy with dictionary + win_inet_proxy: + proxy: + http: proxy:8080 + https: proxy:8443 + ftp: proxy:821 + socks: proxy:888 + register: set_dict + +- name: get result of set proxy with dictionary + win_inet_proxy_info: + register: set_dict_actual + +- name: assert set proxy with dictionary + assert: + that: + - set_dict is changed + - set_dict_actual.proxy == 'http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888' + +- name: set proxy protocol with str + win_inet_proxy: + proxy: http=proxy:8080;https=proxy:8443;ftp=proxy:821;socks=proxy:888 + register: set_str_protocol + +- name: assert set proxy protocol with str + assert: + that: + - not set_str_protocol is changed + +- name: remove proxy with empty string + win_inet_proxy: + proxy: '' + register: remove_empty_str + +- name: get result of remove proxy with empty string + win_inet_proxy_info: + register: remove_empty_str_actual + +- name: assert remove proxy with empty string + assert: + that: + - remove_empty_str is changed + - remove_empty_str_actual.proxy == None + +- name: create test phonebook entry + win_phonebook_entry: + name: Ansible Test Dialup + device_type: pppoe + device_name: WAN Miniport (PPPOE) + framing_protocol: ppp + options: + - remote_default_gateway + - require_pap + - internet + type: broadband + state: present + +- name: set proxy for specific connection + win_inet_proxy: + connection: Ansible Test Dialup + auto_detect: no + auto_config_url: proxy.com + proxy: proxyhost:8080 + bypass: proxyhost + register: set_connection + +- name: get result for set proxy for specific connection + win_inet_proxy_info: + connection: Ansible Test Dialup + register: set_connection_actual + +- name: get result for LAN connection proxy + win_inet_proxy_info: + register: set_connection_lan_actual + +- name: assert set proxy for specific connection + assert: + that: + - set_connection is changed + - set_connection_actual.auto_detect == False + - set_connection_actual.auto_config_url == 'proxy.com' + - set_connection_actual.proxy == 'proxyhost:8080' + - set_connection_actual.bypass == 'proxyhost' + - set_connection_lan_actual.auto_detect == True + - set_connection_lan_actual.auto_config_url == None + - set_connection_lan_actual.proxy == None + - set_connection_lan_actual.bypass == None + +- name: remove proxy for specific connection + win_inet_proxy: + connection: Ansible Test Dialup + register: remove_connection + +- name: get result of remove proxy for specific connection + win_inet_proxy_info: + connection: Ansible Test Dialup + register: remove_connection_actual + +- name: assert remove proxy for specific connection + assert: + that: + - remove_connection is changed + - remove_connection_actual.auto_detect == True + - remove_connection_actual.auto_config_url == None + - remove_connection_actual.proxy == None + - remove_connection_actual.bypass == None diff --git a/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/aliases b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/defaults/main.yml new file mode 100644 index 000000000..9cb54a30c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/defaults/main.yml @@ -0,0 +1 @@ +AnsibleVhdx: C:\win_initialize_disk_tests\AnsiblePart.vhdx diff --git a/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/tasks/main.yml new file mode 100644 index 000000000..b752a7913 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- name: Create the temp directory + ansible.windows.win_file: + path: C:\win_initialize_disk_tests + state: directory + +- name: Copy VHDX scripts + ansible.windows.win_template: + src: "{{ item.src }}" + dest: C:\win_initialize_disk_tests\{{ item.dest }} + loop: + - { src: vhdx_creation_script.j2, dest: vhdx_creation_script.txt } + - { src: vhdx_deletion_script.j2, dest: vhdx_deletion_script.txt } + +- name: Create VHD + ansible.windows.win_command: diskpart.exe /s C:\win_initialize_disk_tests\vhdx_creation_script.txt + +- name: Run tests + block: + - include: tests.yml + always: + - name: Detach disk + ansible.windows.win_command: diskpart.exe /s C:\win_initialize_disk_tests\vhdx_deletion_script.txt + + - name: Cleanup files + ansible.windows.win_file: + path: C:\win_initialize_disk_tests + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/tasks/tests.yml new file mode 100644 index 000000000..b9bc1d70b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/tasks/tests.yml @@ -0,0 +1,104 @@ +--- +- name: Initialize the disk with the default partition style (check mode) + win_initialize_disk: + disk_number: 1 + register: default_part_style_check + check_mode: yes + +- name: Get result of default initialization (check mode) + ansible.windows.win_command: powershell.exe "if ( (Get-Disk -Number 1).PartitionStyle -eq 'RAW' ) {'true'} else {'false'}" + register: default_part_style_actual_check + +- name: assert default initialization (check mode) + assert: + that: + - default_part_style_check is changed + - default_part_style_actual_check.stdout == 'true\r\n' + +- name: Initialize the disk with the default partition style + win_initialize_disk: + disk_number: 1 + register: default_part_style + +- name: Get result of default initialization + ansible.windows.win_command: powershell.exe "if ( (Get-Disk -Number 1).PartitionStyle -eq 'GPT' ) {'true'} else {'false'}" + register: default_part_style_actual + +- name: assert default initialization + assert: + that: + - default_part_style is changed + - default_part_style_actual.stdout == 'true\r\n' + +- name: Initialize the disk with the default partition style (idempotence) + win_initialize_disk: + disk_number: 1 + register: default_part_style_idempotence + +- name: Get result of default initialization (idempotence) + ansible.windows.win_command: powershell.exe "if ( (Get-Disk -Number 1).PartitionStyle -eq 'GPT' ) {'true'} else {'false'}" + register: default_part_style_actual_idempotence + +- name: assert default initialization (idempotence) + assert: + that: + - not default_part_style_idempotence is changed + - default_part_style_actual_idempotence.stdout == 'true\r\n' + +- name: Partition style change without force fails + win_initialize_disk: + disk_number: 1 + style: mbr + register: change_part_style + ignore_errors: True + +- name: assert failed partition style change + assert: + that: + - change_part_style is failed + +- name: Partition style change with force is successful (check mode) + win_initialize_disk: + disk_number: 1 + style: mbr + force: yes + register: change_part_style_forced_check + check_mode: yes + +- name: Get result of forced initialization (check mode) + ansible.windows.win_command: powershell.exe "if ( (Get-Disk -Number 1).PartitionStyle -eq 'GPT' ) {'true'} else {'false'}" + register: change_part_style_forced_actual_check + +- name: assert forced initialization (check mode) + assert: + that: + - change_part_style_forced_check is changed + - change_part_style_forced_actual_check.stdout == 'true\r\n' + +- name: Partition style change with force is successful + win_initialize_disk: + disk_number: 1 + style: mbr + force: yes + register: change_part_style_forced + +- name: Get result of forced initialization + ansible.windows.win_command: powershell.exe "if ( (Get-Disk -Number 1).PartitionStyle -eq 'MBR' ) {'true'} else {'false'}" + register: change_part_style_forced_actual + +- name: assert forced initialization + assert: + that: + - change_part_style_forced is changed + - change_part_style_forced_actual.stdout == 'true\r\n' + +- name: Unknown disk number fails + win_initialize_disk: + disk_number: 3 + register: unknown_disk_number + ignore_errors: True + +- name: assert unknown disk number fails + assert: + that: + - unknown_disk_number is failed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/templates/vhdx_creation_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/templates/vhdx_creation_script.j2 new file mode 100644 index 000000000..4089bf379 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/templates/vhdx_creation_script.j2 @@ -0,0 +1,5 @@ +create vdisk file="{{ AnsibleVhdx }}" maximum=2000 type=fixed + +select vdisk file="{{ AnsibleVhdx }}" + +attach vdisk diff --git a/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/templates/vhdx_deletion_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/templates/vhdx_deletion_script.j2 new file mode 100644 index 000000000..c2be9cd14 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_initialize_disk/templates/vhdx_deletion_script.j2 @@ -0,0 +1,3 @@ +select vdisk file="{{ AnsibleVhdx }}" + +detach vdisk diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/aliases b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/.gitattributes b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/.gitattributes new file mode 100644 index 000000000..d3f27225a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/.gitattributes @@ -0,0 +1,4 @@ +*.text text eol=LF +*.txt text eol=CRLF +*.txt16 text working-tree-encoding=UTF-16 eol=CRLF +*.txt32 text working-tree-encoding=UTF-32 eol=CRLF diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/01_new_line_at_bof.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/01_new_line_at_bof.txt new file mode 100644 index 000000000..01a263dbc --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/01_new_line_at_bof.txt @@ -0,0 +1,6 @@ +New line at the beginning +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/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/02_new_line_at_eof.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/02_new_line_at_eof.txt new file mode 100644 index 000000000..ff0ffa89d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/02_new_line_at_eof.txt @@ -0,0 +1,7 @@ +New line at the beginning +This is line 1 +This is line 2 +REF this is a line for backrefs REF +This is line 4 +This is line 5 +New line at the end diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/03_new_line_after_1.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/03_new_line_after_1.txt new file mode 100644 index 000000000..c13010c68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/03_new_line_after_1.txt @@ -0,0 +1,8 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +REF this is a line for backrefs REF +This is line 4 +This is line 5 +New line at the end diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/04_new_line_before_5.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/04_new_line_before_5.txt new file mode 100644 index 000000000..9e021050b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/04_new_line_before_5.txt @@ -0,0 +1,9 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +REF this is a line for backrefs REF +This is line 4 +New line before line 5 +This is line 5 +New line at the end diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/05_new_line_at_REF.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/05_new_line_at_REF.txt new file mode 100644 index 000000000..4a5379ae0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/05_new_line_at_REF.txt @@ -0,0 +1,9 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +This is line 3 +This is line 4 +New line before line 5 +This is line 5 +New line at the end diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/06_remove_middle_line.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/06_remove_middle_line.txt new file mode 100644 index 000000000..bd0e8f595 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/06_remove_middle_line.txt @@ -0,0 +1,8 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +This is line 4 +New line before line 5 +This is line 5 +New line at the end diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/07_remove_line_5.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/07_remove_line_5.txt new file mode 100644 index 000000000..ae0b0ab98 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/07_remove_line_5.txt @@ -0,0 +1,7 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +This is line 4 +New line before line 5 +New line at the end diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/08_no_expected_change.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/08_no_expected_change.txt new file mode 100644 index 000000000..ae0b0ab98 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/08_no_expected_change.txt @@ -0,0 +1,7 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +This is line 4 +New line before line 5 +New line at the end diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/09_new_file.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/09_new_file.txt new file mode 100644 index 000000000..6dfa057f0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/09_new_file.txt @@ -0,0 +1 @@ +This is a new file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/10_no_eof_new_at_eof.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/10_no_eof_new_at_eof.txt new file mode 100644 index 000000000..31b06636a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/10_no_eof_new_at_eof.txt @@ -0,0 +1,3 @@ +This is line 1 +This is line 2 +New line at the end
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/11_multiline_at_eof.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/11_multiline_at_eof.txt new file mode 100644 index 000000000..fa15cdba3 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/11_multiline_at_eof.txt @@ -0,0 +1,9 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +This is line 4 +New line before line 5 +New line at the end +This is a line +with newline character diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/12_empty_file_add_at_eof.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/12_empty_file_add_at_eof.txt new file mode 100644 index 000000000..1467ce9ea --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/12_empty_file_add_at_eof.txt @@ -0,0 +1 @@ +New line at the end
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/13_new_4_with_backref.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/13_new_4_with_backref.txt new file mode 100644 index 000000000..3c86758a0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/13_new_4_with_backref.txt @@ -0,0 +1,9 @@ +New line at the beginning +This is line 1 +New line after line 1 +This is line 2 +New line 4 created with the backref +New line before line 5 +New line at the end +This is a line +with newline character diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/14_quoting_code.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/14_quoting_code.txt new file mode 100644 index 000000000..3575ba049 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/14_quoting_code.txt @@ -0,0 +1,3 @@ +var dotenv = require('dotenv'); +dotenv.load(); +'foo'
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/15_single_quote.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/15_single_quote.txt new file mode 100644 index 000000000..e1e1cf090 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/15_single_quote.txt @@ -0,0 +1,4 @@ +var dotenv = require('dotenv'); +dotenv.load(); +'foo' +import g'
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/16_multiple_quotes.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/16_multiple_quotes.txt new file mode 100644 index 000000000..55a32a6f2 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/16_multiple_quotes.txt @@ -0,0 +1,5 @@ +var dotenv = require('dotenv'); +dotenv.load(); +'foo' +import g' +"quote" and "unquote"
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/17_new_file_win.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/17_new_file_win.txt new file mode 100644 index 000000000..6dfa057f0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/17_new_file_win.txt @@ -0,0 +1 @@ +This is a new file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/18_sep_win.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/18_sep_win.txt new file mode 100644 index 000000000..b2f86910d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/18_sep_win.txt @@ -0,0 +1,2 @@ +This is a new file +This is the last line diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/19_new_file_unix.text b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/19_new_file_unix.text new file mode 100644 index 000000000..6dfa057f0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/19_new_file_unix.text @@ -0,0 +1 @@ +This is a new file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/20_sep_unix.text b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/20_sep_unix.text new file mode 100644 index 000000000..b2f86910d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/20_sep_unix.text @@ -0,0 +1,2 @@ +This is a new file +This is the last line diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/21_utf8_no_bom.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/21_utf8_no_bom.txt new file mode 100644 index 000000000..cd753877d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/21_utf8_no_bom.txt @@ -0,0 +1 @@ +This is a new utf-8 file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/22_utf8_no_bom_line_added.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/22_utf8_no_bom_line_added.txt new file mode 100644 index 000000000..3c3116a3e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/22_utf8_no_bom_line_added.txt @@ -0,0 +1,2 @@ +This is a new utf-8 file +This is the last line diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/23_utf8_bom.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/23_utf8_bom.txt new file mode 100644 index 000000000..0873b7649 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/23_utf8_bom.txt @@ -0,0 +1 @@ +This is a new utf-8 file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/24_utf8_bom_line_added.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/24_utf8_bom_line_added.txt new file mode 100644 index 000000000..484d75488 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/24_utf8_bom_line_added.txt @@ -0,0 +1,2 @@ +This is a new utf-8 file +This is the last line diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/25_utf16.txt16 b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/25_utf16.txt16 new file mode 100644 index 000000000..b9a0d1ae2 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/25_utf16.txt16 @@ -0,0 +1 @@ +This is a new utf-16 file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/26_utf16_line_added.txt16 b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/26_utf16_line_added.txt16 new file mode 100644 index 000000000..0a949dc4e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/26_utf16_line_added.txt16 @@ -0,0 +1,2 @@ +This is a new utf-16 file +This is the last line diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/27_utf32.txt32 b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/27_utf32.txt32 new file mode 100644 index 000000000..62e697f88 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/27_utf32.txt32 @@ -0,0 +1 @@ +This is a new utf-32 file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/28_utf32_line_added.txt32 b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/28_utf32_line_added.txt32 new file mode 100644 index 000000000..3d9af1c40 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/28_utf32_line_added.txt32 @@ -0,0 +1,2 @@ +This is a new utf-32 file +This is the last line diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/29_no_linebreak.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/29_no_linebreak.txt new file mode 100644 index 000000000..dff2fa088 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/29_no_linebreak.txt @@ -0,0 +1 @@ +c:\return\new
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/30_linebreaks_checksum_bad.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/30_linebreaks_checksum_bad.txt new file mode 100644 index 000000000..ffaa8767f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/30_linebreaks_checksum_bad.txt @@ -0,0 +1,3 @@ +c:\return\new +c:
eturn +ew
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/31_relative_path.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/31_relative_path.txt new file mode 100644 index 000000000..01a263dbc --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/31_relative_path.txt @@ -0,0 +1,6 @@ +New line at the beginning +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/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/99_README.md b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/99_README.md new file mode 100644 index 000000000..798b9c02e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/expectations/99_README.md @@ -0,0 +1,36 @@ +***WIN LINEINFILE Expectations*** + +This folder contains expected files as the tests in this playbook executes on the +files in 'files'. + +To get the checksum as would win_stat in the tests, go to this folder in powershell and +execute + +```powershell +Get-ChildItem | ForEach-Object { + $fp = [System.IO.File]::Open("$pwd/$($_.Name)", [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) + Write-Output $_.Name + try { + [System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower() + } finally { + $fp.Dispose() + } + Write-Output "" +} +``` + +There is one exception right now: 30_linebreaks_checksum_bad.txt which requires mixed line endings that +git cannot handle without turning the file binary. The file should read + +``` +c:\return\newCRLF +c:CR +eturnLF +ew +``` +where CR and LF denote carriage return (\r) and line feed (\n) respectively, to get the correct checksum. + +Also, the .gitattributes files is important as it assures that the EOL characters +for the files are correct, regardless of environment. The files may be checked out on +linux but the resulting files will be created using windows EOL, and the comparison must +match.
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test.txt new file mode 100644 index 000000000..8187db9f0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/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/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test_linebreak.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test_linebreak.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test_linebreak.txt diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test_quoting.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test_quoting.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/test_quoting.txt diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/testempty.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/testempty.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/testempty.txt diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/testnoeof.txt b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/files/testnoeof.txt new file mode 100644 index 000000000..152780b9f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/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/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/tasks/main.yml new file mode 100644 index 000000000..87748e595 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_lineinfile/tasks/main.yml @@ -0,0 +1,803 @@ +# 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 + ansible.windows.win_copy: src=test.txt dest={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert that the test file was deployed + assert: + that: + - "result.changed == true" + +- name: stat the test file + # Note: The test.txt is copied from a Linux host, therefore it will retain + # unix line ending when stat'd here. This affects checksum, meaning that + # when this repo is checked out to Windows, checksum does not match checked- + # out file checksum unless line-endings are altered. + ansible.windows.win_stat: path={{ remote_tmp_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 + # 01 + win_lineinfile: dest={{ remote_tmp_dir }}/test.txt state=present line="New line at the beginning" insertbefore="BOF" backup=yes + register: result + +- name: check backup_file + ansible.windows.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 + ansible.windows.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 + ansible.windows.win_stat: path={{ remote_tmp_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 == 'c61233b0ee2038aab41b5f30683c57b2a013b376' + +- name: insert a line at the end of the file + # 02 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after the insert at the end + assert: + that: + - result.stat.checksum == 'a91260ad67b00609cc0737ff70ac5170c6a519a8' + +- name: insert a line after the first line + # 03 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after the insert after the first line + assert: + that: + - result.stat.checksum == '6ffbabdaa21ecb3593d32144de535598cfd7c6ea' + +- name: insert a line before the last line + # 04 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after the insert before the last line + assert: + that: + - result.stat.checksum == '49d988ad97fb4cce3ad795c8459f1cde231a891b' + +- name: replace a line with backrefs + # 05 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - result.stat.checksum == '6f9c2128f4c886f3c40c1f1cf50241d74f160437' + +- name: remove the middle line + # 06 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after the middle line was removed + assert: + that: + - result.stat.checksum == '4b7910c84bd1177b9013f277c85d5be55f384a36' + +- name: run a validation script that succeeds + # 07 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after the validation succeeded + assert: + that: + - result.stat.checksum == '938afecdcde51fda42ddbea6f2e92876e710f289' + +- name: run a validation script that fails + # 08 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches the previous after the validation failed + assert: + that: + - result.stat.checksum == '938afecdcde51fda42ddbea6f2e92876e710f289' + +- name: use create=yes + # 09 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + ansible.windows.win_copy: src=testnoeof.txt dest={{ remote_tmp_dir }}/testnoeof.txt + register: result + +- name: testnoeof insert a line at the end of the file + # 10 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 11 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after inserting multiple lines + assert: + that: + - result.stat.checksum == 'd10d70cb77a16fb54f143bccbf711f3177acd310' + + + +# Test EOF with empty file to make sure no unnecessary newline is added +- name: testempty deploy the testempty file for lineinfile + ansible.windows.win_copy: src=testempty.txt dest={{ remote_tmp_dir }}/testempty.txt + register: result + +- name: testempty insert a line at the end of the file + # 12 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 13 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - result.stat.checksum == '9c1a1451b50665e59be666c5d8c08fb99603d4f1' + +################################################################### +# issue 8535 + +- name: create a new file for testing quoting issues + # 14 + ansible.windows.win_copy: src=test_quoting.txt dest={{ remote_tmp_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={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 15 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 16 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 17 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 18 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test_windows_sep.txt + register: result + +- name: assert the file checksum matches expected checksum + assert: + that: + - result.stat.checksum == '6c6f51f98eb499852fbb7ef3b212c26752c25c31' + + +- name: Create unix test file with initial line + # 19 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 20 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test_unix_sep.txt + register: result + +- name: assert the file checksum matches expected checksum + assert: + that: + - result.stat.checksum == '4aa2ad771bb1453406760eadee8234265d599dcf' + + +# 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 + # 21 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 22 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test_auto_utf8.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - result.stat.checksum == 'aeb246a40f614889534f4983f47c5567625ade53' + + +# UTF-8 explicit (with BOM) +- name: Test create file with explicit utf-8 encoding results in utf-8 with a BOM + # 23 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 24 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test_utf8.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - result.stat.checksum == '64c62066d06ea6c807a8fe98bc40c4903cf4c119' + + +# UTF-16 explicit +- name: Test create file with explicit utf-16 encoding + # 25 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 26 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test_utf16.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - result.stat.checksum == '7f790f323e496b7138883a3634514cc2a3426919' + +# UTF-32 explicit +- name: Test create file with explicit utf-32 encoding + # 27 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_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 + # 28 + win_lineinfile: dest={{ remote_tmp_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 + ansible.windows.win_stat: path={{ remote_tmp_dir }}/test_utf32.txt + register: result + +- name: assert the file checksum matches + assert: + that: + - result.stat.checksum == 'd13e135c7d466cc2f985d72f12ffaa73567772e6' + +######################################################################### +# issue #33858 +# \r\n causes line break instead of printing literally which breaks paths. + +- name: create testing file + ansible.windows.win_copy: + src: test_linebreak.txt + dest: "{{ remote_tmp_dir }}/test_linebreak.txt" + +- name: stat the test file + ansible.windows.win_stat: + path: "{{ remote_tmp_dir }}/test_linebreak.txt" + register: result + +- 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 + # 29 + win_lineinfile: + dest: "{{ remote_tmp_dir }}/test_linebreak.txt" + line: c:\return\new + register: result_literal + +- name: insert path "c:\return\new" to test file, will cause line breaks + # 30 + win_lineinfile: + dest: "{{ remote_tmp_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 + ansible.windows.win_stat: + path: "{{ remote_tmp_dir }}/test_linebreak.txt" + register: result + +- name: assert that one line is literal and the other has breaks + assert: + that: + - result.stat.checksum == 'd2dfd11bc70526ff13a91153c76a7ae5595a845b' + +- name: Get current working directory + win_shell: $pwd.Path + register: pwd + +- name: create a relative tmp dir + ansible.windows.win_tempfile: + path: "{{ pwd.stdout | trim | win_dirname }}" + state: directory + register: relative_tmp_dir + +- name: Check that relative paths work + block: + - name: deploy the test file for lineinfile + ansible.windows.win_copy: src=test.txt dest={{ relative_tmp_dir.path }}/test.txt + register: result + + - name: assert that the test file was deployed + assert: + that: + - "result.changed == true" + + - name: stat the test file + ansible.windows.win_stat: path={{ relative_tmp_dir.path }}/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 + # 31 + win_lineinfile: dest=../{{ relative_tmp_dir.path | win_basename }}/test.txt state=present line="New line at the beginning" insertbefore="BOF" backup=yes + register: result + + - name: check backup_file + ansible.windows.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 + ansible.windows.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 + ansible.windows.win_stat: path={{ relative_tmp_dir.path }}/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 == 'c61233b0ee2038aab41b5f30683c57b2a013b376' + + always: + - ansible.windows.win_file: + path: "{{ relative_tmp_dir.path }}" + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_listen_ports_facts/aliases b/ansible_collections/community/windows/tests/integration/targets/win_listen_ports_facts/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_listen_ports_facts/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_listen_ports_facts/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_listen_ports_facts/tasks/main.yml new file mode 100644 index 000000000..77649d8f1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_listen_ports_facts/tasks/main.yml @@ -0,0 +1,45 @@ +# This file is part of Ansible + +# Copyright: (c) 2022, DataDope (@datadope-io) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Gather facts with invalid tcp_filter state and null date format + win_listen_ports_facts: + tcp_filter: + - Error + date_format: null + register: invalid_state_date_run + ignore_errors: True + +- name: Gather facts with only some invalid tcp_filter states and null date format + win_listen_ports_facts: + tcp_filter: + - Listen + - Closed + - Error + date_format: null + register: partial_invalid_state_date_run + ignore_errors: True + +- name: Gather facts with multiple filters and custom date format + win_listen_ports_facts: + tcp_filter: + - Listen + - Closed + date_format: yyyy + register: custom_state_date_run + ignore_errors: True + +- name: Gather facts with default filters and date format + win_listen_ports_facts: + register: default_run + ignore_errors: True + +- assert: + that: + - invalid_state_date_run.failed is defined and invalid_state_date_run.failed + - partial_invalid_state_date_run.failed is defined and partial_invalid_state_date_run.failed + - custom_state_date_run.failed is not defined or not custom_state_date_run.failed + - default_run.failed is not defined or not default_run.failed + - tcp_listen is defined + - udp_listen is defined diff --git a/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/aliases b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/defaults/main.yml new file mode 100644 index 000000000..6e8ed002e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/defaults/main.yml @@ -0,0 +1,9 @@ +test_win_mapped_drive_letter: M +test_win_mapped_drive_path: share1 +test_win_mapped_drive_path2: share2 + +test_win_mapped_drive_local_path: C:\ansible\win_mapped_drive\share1 +test_win_mapped_drive_local_path2: C:\ansible\win_mapped_drive\share2 + +test_win_mapped_drive_temp_user: TestMappedUser +test_win_mapped_drive_temp_password: aZ293jgkdslgj4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/tasks/main.yml new file mode 100644 index 000000000..19d5acb2c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/tasks/main.yml @@ -0,0 +1,99 @@ +--- +# test setup +- name: gather facts required by the tests + ansible.windows.setup: + gather_subset: platform + +- name: ensure mapped drive is deleted before test + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + state: absent + +- name: ensure temp mapped drive user exist + ansible.windows.win_user: + name: '{{test_win_mapped_drive_temp_user}}' + password: '{{test_win_mapped_drive_temp_password}}' + state: present + groups: + - Administrators + +- name: ensure temp folders exist + ansible.windows.win_file: + path: '{{item}}' + state: directory + with_items: + - '{{test_win_mapped_drive_local_path}}' + - '{{test_win_mapped_drive_local_path2}}' + +# can't use win_share as it doesnt't support Server 2008 and 2008 R2 +- name: ensure shares exist + ansible.windows.win_shell: $share = Get-WmiObject -Class Win32_Share | Where-Object { $_.Name -eq '{{item.name}}' }; if (-not $share) { $share = [wmiClass]'Win32_Share'; $share.Create('{{item.path}}', '{{item.name}}', 0) } + with_items: + - { name: '{{test_win_mapped_drive_path}}', path: '{{test_win_mapped_drive_local_path}}' } + - { name: '{{test_win_mapped_drive_path2}}', path: '{{test_win_mapped_drive_local_path2}}' } + +# This ensures we test out the split token/become behaviour +- name: ensure builtin Administrator has a split token + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System + name: FilterAdministratorToken + data: 1 + type: dword + register: admin_uac + +- name: reboot to apply Admin approval mode setting + ansible.windows.win_reboot: + when: admin_uac is changed + +- block: + # tests + - include_tasks: tests.yml + + # test cleanup + always: + - name: remove stored credential + win_credential: + name: '{{ ansible_hostname }}' + type: domain_password + state: absent + vars: + ansible_become: yes + ansible_become_method: runas + ansible_become_user: '{{ ansible_user }}' + ansible_become_pass: '{{ ansible_password }}' + + - name: ensure mapped drive is deleted at the end of the test + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + state: absent + + - name: ensure shares are removed + ansible.windows.win_shell: $share = Get-WmiObject -Class Win32_Share | Where-Object { $_.Name -eq '{{item}}' }; if ($share) { $share.Delete() } + with_items: + - '{{test_win_mapped_drive_path}}' + - '{{test_win_mapped_drive_path2}}' + + - name: ensure temp folders are deleted + ansible.windows.win_file: + path: '{{item}}' + state: absent + with_items: + - '{{test_win_mapped_drive_local_path}}' + - '{{test_win_mapped_drive_local_path2}}' + + - name: ensure temp mapped driver user is deleted + ansible.windows.win_user: + name: '{{test_win_mapped_drive_temp_user}}' + state: absent + + - name: disable Admin approval mode if changed in test + ansible.windows.win_regedit: + path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System + name: FilterAdministratorToken + data: 0 + type: dword + when: admin_uac is changed + + - name: reboot to apply Admin approval mode setting + ansible.windows.win_reboot: + when: admin_uac is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/tasks/tests.yml new file mode 100644 index 000000000..2529b8ba1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_mapped_drive/tasks/tests.yml @@ -0,0 +1,344 @@ +--- +- name: fail with invalid path + win_mapped_drive: + letter: invalid + state: absent + register: fail_invalid_letter + failed_when: "fail_invalid_letter.msg != 'letter must be a single letter from A-Z, was: invalid'" + +- name: fail without specify path when creating drive + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + state: present + register: fail_path_missing + failed_when: "fail_path_missing.msg != 'state is present but all of the following are missing: path'" + +- name: fail when specifying letter with existing physical path + win_mapped_drive: + letter: c + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + register: fail_local_letter + failed_when: fail_local_letter.msg != 'failed to create mapped drive c, this letter is in use and is pointing to a non UNC path' + +- name: create mapped drive check + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + register: create_drive_check + check_mode: yes + +- name: get actual of create mapped drive check + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' # Get-PSDrive/Get-WmiObject/Get-CimInstance doesn't work over WinRM + register: create_drive_actual_check + failed_when: False + +- name: assert create mapped drive check + assert: + that: + - create_drive_check is changed + - create_drive_actual_check.rc == 2 # should fail with this error code when it isn't found + +- name: create mapped drive + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + register: create_drive + +- name: get actual of create mapped drive + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: create_drive_actual + +- name: assert create mapped drive + assert: + that: + - create_drive is changed + - create_drive_actual.rc == 0 + - create_drive_actual.stdout_lines[1] == "Remote name \\\\{{ansible_hostname}}\\{{test_win_mapped_drive_path}}" + +- name: create mapped drive again + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + register: create_drive_again + +- name: assert create mapped drive again + assert: + that: + - create_drive_again is not changed + +- name: change mapped drive target check + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path2}} + state: present + register: change_drive_target_check + check_mode: yes + +- name: get actual of change mapped drive target check + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: change_drive_target_actual_check + +- name: assert change mapped drive target check + assert: + that: + - change_drive_target_check is changed + - change_drive_target_actual_check.rc == 0 + - change_drive_target_actual_check.stdout_lines[1] == "Remote name \\\\{{ansible_hostname}}\\{{test_win_mapped_drive_path}}" + +- name: change mapped drive target + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path2}} + state: present + register: change_drive_target + +- name: get actual of change mapped drive target + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: change_drive_target_actual + +- name: assert change mapped drive target + assert: + that: + - change_drive_target is changed + - change_drive_target_actual.rc == 0 + - change_drive_target_actual.stdout_lines[1] == "Remote name \\\\{{ansible_hostname}}\\{{test_win_mapped_drive_path2}}" + +- name: fail to delete mapped drive if target doesn't match + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: absent + register: fail_delete_incorrect_target + failed_when: fail_delete_incorrect_target.msg != 'did not delete mapped drive ' + test_win_mapped_drive_letter + ', the target path is pointing to a different location at \\\\' + ansible_hostname + '\\' + test_win_mapped_drive_path2 + +- name: delete mapped drive check + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path2}} + state: absent + register: delete_drive_check + check_mode: yes + +- name: get actual of delete mapped drive check + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: delete_drive_actual_check + +- name: assert delete mapped drive check + assert: + that: + - delete_drive_check is changed + - delete_drive_actual_check.rc == 0 + - delete_drive_actual_check.stdout_lines[1] == "Remote name \\\\{{ansible_hostname}}\\{{test_win_mapped_drive_path2}}" + +- name: delete mapped drive + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path2}} + state: absent + register: delete_drive + +- name: get actual of delete mapped drive + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: delete_drive_actual + failed_when: False + +- name: assert delete mapped drive + assert: + that: + - delete_drive is changed + - delete_drive_actual.rc == 2 + +- name: delete mapped drive again + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path2}} + state: absent + register: delete_drive_again + +- name: assert delete mapped drive again + assert: + that: + - delete_drive_again is not changed + +# not much we can do to test out the credentials except that it sets it, winrm +# makes it hard to actually test out we can still access the mapped drive +- name: map drive with current credentials check + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + username: '{{ansible_hostname}}\{{test_win_mapped_drive_temp_user}}' + password: '{{test_win_mapped_drive_temp_password}}' + register: map_with_credentials_check + check_mode: yes + +- name: get actual of map drive with current credentials check + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: map_with_credentials_actual_check + failed_when: False + +- name: assert map drive with current credentials check + assert: + that: + - map_with_credentials_check is changed + - map_with_credentials_actual_check.rc == 2 + +- name: map drive with current credentials + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + username: '{{ansible_hostname}}\{{test_win_mapped_drive_temp_user}}' + password: '{{test_win_mapped_drive_temp_password}}' + register: map_with_credentials + +- name: get actual of map drive with current credentials + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: map_with_credentials_actual + +- name: get username of mapped network drive with credentials + ansible.windows.win_reg_stat: + path: HKCU:\Network\{{test_win_mapped_drive_letter}} + name: UserName + register: map_with_credential_actual_username + +- name: assert map drive with current credentials + assert: + that: + - map_with_credentials is changed + - map_with_credentials_actual.rc == 0 + - map_with_credential_actual_username.value == '' # we explicitly remove the username part in the module + +- name: map drive with current credentials again + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + username: '{{ansible_hostname}}\{{test_win_mapped_drive_temp_user}}' + password: '{{test_win_mapped_drive_temp_password}}' + register: map_with_credentials_again + +- name: assert map drive with current credentials again + assert: + that: + - not map_with_credentials_again is changed + +- name: delete mapped drive without path check + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + state: absent + register: delete_without_path_check + check_mode: yes + +- name: get actual delete mapped drive without path check + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: delete_without_path_actual_check + +- name: assert delete mapped drive without path check + assert: + that: + - delete_without_path_check is changed + - delete_without_path_actual_check.rc == 0 + +- name: delete mapped drive without path + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + state: absent + register: delete_without_path + +- name: get actual delete mapped drive without path + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: delete_without_path_actual + failed_when: False + +- name: assert delete mapped drive without path check + assert: + that: + - delete_without_path is changed + - delete_without_path_actual.rc == 2 + +- name: delete mapped drive without path again + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + state: absent + register: delete_without_path_again + +- name: assert delete mapped drive without path check again + assert: + that: + - delete_without_path_again is not changed + +- name: store credential for test network account + win_credential: + name: '{{ ansible_hostname }}' + type: domain_password + username: '{{ test_win_mapped_drive_temp_user }}' + secret: '{{ test_win_mapped_drive_temp_password }}' + state: present + vars: &become_vars + ansible_become: yes + ansible_become_method: runas + ansible_become_user: '{{ ansible_user }}' + ansible_become_pass: '{{ ansible_password }}' + +- name: map drive with stored cred (check mode) + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + check_mode: yes + vars: *become_vars + register: map_with_stored_cred_check + +- name: get actual of map drive with stored cred (check mode) + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: map_with_stored_cred_actual_check + failed_when: False + +- name: assert map drive with stored cred (check mode) + assert: + that: + - map_with_stored_cred_check is changed + - map_with_stored_cred_actual_check.rc == 2 + +- name: map drive with stored cred + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + vars: *become_vars + register: map_with_stored_cred + +- name: get actual of map drive with stored cred + ansible.windows.win_command: 'net use {{test_win_mapped_drive_letter}}:' + register: map_with_stored_cred_actual + +- name: get username of mapped network drive with stored cred + ansible.windows.win_reg_stat: + path: HKCU:\Network\{{test_win_mapped_drive_letter}} + name: UserName + register: map_with_stored_cred_actual_username + +- name: assert map drive with stored cred + assert: + that: + - map_with_stored_cred is changed + - map_with_stored_cred_actual.rc == 0 + - map_with_stored_cred_actual_username.value == '' + +- name: map drive with stored cred again + win_mapped_drive: + letter: '{{test_win_mapped_drive_letter}}' + path: \\{{ansible_hostname}}\{{test_win_mapped_drive_path}} + state: present + vars: *become_vars + register: map_with_stored_cred_again + +- name: assert map drive with stored cred again + assert: + that: + - not map_with_stored_cred_again is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_msg/aliases b/ansible_collections/community/windows/tests/integration/targets/win_msg/aliases new file mode 100644 index 000000000..98b74ac98 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_msg/aliases @@ -0,0 +1,2 @@ +shippable/windows/group2 +unstable diff --git a/ansible_collections/community/windows/tests/integration/targets/win_msg/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_msg/tasks/main.yml new file mode 100644 index 000000000..17051239f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_msg/tasks/main.yml @@ -0,0 +1,33 @@ +- name: Warn user + win_msg: + display_seconds: 10 + msg: Keep calm and carry on. + register: msg_result + +- name: Test msg_result + assert: + that: + - msg_result is not failed + - msg_result is changed + - msg_result.runtime_seconds < 10 + +- name: Warn user and wait for it + win_msg: + display_seconds: 5 + msg: Keep calm and carry on. + #to: '{{ ansible_user }}' + wait: yes + register: msg_wait_result + +- name: Test msg_wait_result + assert: + that: + - msg_wait_result is not failed + - msg_wait_result is changed + - msg_wait_result.runtime_seconds > 5 + +- name: fail to send a message > 255 characters + win_msg: + msg: "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456" + register: fail_too_long + failed_when: "fail_too_long.msg != 'msg length must be less than 256 characters, current length: 256'" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/aliases b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/meta/main.yml new file mode 100644 index 000000000..e7f499ee3 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_win_device
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/tasks/main.yml new file mode 100644 index 000000000..a40e0e049 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/tasks/main.yml @@ -0,0 +1,27 @@ +# We create the first adapter by depending on setup_win_device but we need a 2nd one for our tests +- name: create 2nd dummy network adapter device + win_device: + path: '%WinDir%\Inf\netloop.inf' + hardware_id: '*msloop' + state: present + register: network_device_name_raw2 + +- block: + - set_fact: + network_device_name2: '{{ network_device_name_raw2.name }}' + + - name: get name of the dummy network adapter + ansible.windows.win_shell: (Get-CimInstance -Class Win32_NetworkAdapter -Filter "Name='{{ network_device_name2 }}'").NetConnectionID + changed_when: False + register: network_adapter_name_raw2 + + - set_fact: + network_adapter_name2: '{{ network_adapter_name_raw2.stdout | trim }}' + + - include_tasks: tests.yml + + always: + - name: remove 2nd dummy network adapter device + win_device: + name: '{{ network_device_name_raw2.name }}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/tasks/tests.yml new file mode 100644 index 000000000..188182da3 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_net_adapter_feature/tasks/tests.yml @@ -0,0 +1,284 @@ +- name: fail when interface isn't set + win_net_adapter_feature: + state: enabled + component_id: ms_tcpip6 + register: failed_task + failed_when: not failed_task is failed + +- name: fail when component_id isn't set + win_net_adapter_feature: + interface: '{{ network_adapter_name }}' + state: enabled + register: failed_task + failed_when: not failed_task is failed + +- name: fail when interface doesn't exist + win_net_adapter_feature: + interface: Ethernet10 + state: enabled + component_id: ms_tcpip6 + register: failed_task + failed_when: not failed_task is failed + +- name: fail when state is inapppropriate + win_net_adapter_feature: + interface: '{{ network_adapter_name }}' + state: disabled_inappropriate + component_id: ms_tcpip6 + register: failed_task + failed_when: not failed_task is failed + +- name: fail when component_id is inapppropriate + win_net_adapter_feature: + interface: '{{ network_adapter_name }}' + state: disabled + component_id: inappropriate_component + register: failed_task + failed_when: not failed_task is failed + +- name: disable ms_tcpip6 before execution of normal tests. + ansible.windows.win_shell: Disable-NetAdapterBinding -Name '{{ network_adapter_name }}' -ComponentID ms_tcpip6 + +- name: get the status of ms_tcpip6 before execution of normal tests. + ansible.windows.win_shell: (Get-NetAdapterBinding -Name '{{ network_adapter_name }}' -ComponentID ms_tcpip6).Enabled + register: before_normal_tests + +- name: assert that the status of ms_tcpip6 is 'disabled' before execution of normal tests. + assert: + that: + - before_normal_tests.stdout_lines[0] == 'False' + +- name: enable an interface of ms_tpip6 when state isn't set (because the default state is 'enabled') + win_net_adapter_feature: + interface: '{{ network_adapter_name }}' + component_id: ms_tcpip6 + register: enable_single + +- name: get the status of enable an interface of ms_tpip6 + ansible.windows.win_shell: (Get-NetAdapterBinding -Name '{{ network_adapter_name }}' -ComponentID ms_tcpip6).Enabled + register: enable_single_actual + +- name: assert that changed is true and the status turns 'enabled'. + assert: + that: + - enable_single is changed + - enable_single_actual.stdout_lines[0] == 'True' + +- name: enable an interface of ms_tcpip6 + win_net_adapter_feature: + interface: '{{ network_adapter_name }}' + state: enabled + component_id: ms_tcpip6 + register: enable_single_again + +- name: get the status of enable an interface of ms_tcpip6 + ansible.windows.win_shell: (Get-NetAdapterBinding -Name '{{ network_adapter_name }}' -ComponentID ms_tcpip6).Enabled + register: enable_single_again_actual + +- name: assert that changed is false and the status is still remained 'enabled'. + assert: + that: + - enable_single_again is not changed + - enable_single_again_actual.stdout_lines[0] == 'True' + +- name: disable an interface of ms_tcpip6 + win_net_adapter_feature: + interface: '{{ network_adapter_name }}' + state: disabled + component_id: ms_tcpip6 + register: disable_single + +- name: get the status of disable an interface of ms_tcpip6 + ansible.windows.win_shell: (Get-NetAdapterBinding -Name '{{ network_adapter_name }}' -ComponentID ms_tcpip6).Enabled + register: disable_single_actual + +- name: assert that changed is true and the status turns 'disabled'. + assert: + that: + - disable_single is changed + - disable_single_actual.stdout_lines[0] == 'False' + +- name: enable single component_id of multiple interface + win_net_adapter_feature: + interface: + - '{{ network_adapter_name }}' + - '{{ network_adapter_name2 }}' + state: enabled + component_id: ms_tcpip6 + register: enable_multiple + +- name: get the status of disable multiple interfaces of multiple interfaces - check mode + ansible.windows.win_shell: | + $info = Get-NetAdapterBinding -Name '{{ network_adapter_name }}', '{{ network_adapter_name2 }}' + @{ + '{{ network_adapter_name }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + } + '{{ network_adapter_name2 }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name2 }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + } + } | ConvertTo-Json + register: enable_multiple_actual + +- name: assert that changed is true and each status turns 'enabled'. + assert: + that: + - enable_multiple is changed + - (enable_multiple_actual.stdout | from_json)[network_adapter_name]['ms_tcpip6'] == True + - (enable_multiple_actual.stdout | from_json)[network_adapter_name2]['ms_tcpip6'] == True + +- name: disable multiple component_id of multiple interfaces + win_net_adapter_feature: + interface: + - '{{ network_adapter_name }}' + - '{{ network_adapter_name2 }}' + state: disabled + component_id: + - ms_tcpip6 + - ms_server + register: mutliple_multiple_disable + +- name: get the status of disable multiple component_id of multiple interfaces + ansible.windows.win_shell: | + $info = Get-NetAdapterBinding -Name '{{ network_adapter_name }}', '{{ network_adapter_name2 }}' + @{ + '{{ network_adapter_name }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + ms_server = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name }}' -and $_.ComponentID -eq 'ms_server'}).Enabled + } + '{{ network_adapter_name2 }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name2 }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + ms_server = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name2 }}' -and $_.ComponentID -eq 'ms_server'}).Enabled + } + } | ConvertTo-Json + register: mutliple_multiple_disable_actual + +- name: assert that changed is true and each status turns 'disabled'. + assert: + that: + - mutliple_multiple_disable is changed + - (mutliple_multiple_disable_actual.stdout | from_json)[network_adapter_name]['ms_tcpip6'] == False + - (mutliple_multiple_disable_actual.stdout | from_json)[network_adapter_name]['ms_server'] == False + - (mutliple_multiple_disable_actual.stdout | from_json)[network_adapter_name2]['ms_tcpip6'] == False + - (mutliple_multiple_disable_actual.stdout | from_json)[network_adapter_name2]['ms_server'] == False + +- name: enable multiple interfaces of multiple interfaces + win_net_adapter_feature: + interface: + - '{{ network_adapter_name }}' + - '{{ network_adapter_name2 }}' + state: enabled + component_id: + - ms_tcpip6 + - ms_server + register: multiple_multiple_enable + +- name: get the status of disable multiple component_id of multiple interfaces + ansible.windows.win_shell: | + $info = Get-NetAdapterBinding -Name '{{ network_adapter_name }}', '{{ network_adapter_name2 }}' + @{ + '{{ network_adapter_name }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + ms_server = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name }}' -and $_.ComponentID -eq 'ms_server'}).Enabled + } + '{{ network_adapter_name2 }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name2 }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + ms_server = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name2 }}' -and $_.ComponentID -eq 'ms_server'}).Enabled + } + } | ConvertTo-Json + register: multiple_multiple_enable_actual + +- name: assert that changed is true and each status turns 'enabled'. + assert: + that: + - multiple_multiple_enable is changed + - (multiple_multiple_enable_actual.stdout | from_json)[network_adapter_name]['ms_tcpip6'] == True + - (multiple_multiple_enable_actual.stdout | from_json)[network_adapter_name]['ms_server'] == True + - (multiple_multiple_enable_actual.stdout | from_json)[network_adapter_name2]['ms_tcpip6'] == True + - (multiple_multiple_enable_actual.stdout | from_json)[network_adapter_name2]['ms_server'] == True + +- name: enable multiple interfaces of multiple interfaces - idempotent + win_net_adapter_feature: + interface: + - '{{ network_adapter_name }}' + - '{{ network_adapter_name2 }}' + state: enabled + component_id: + - ms_tcpip6 + - ms_server + register: multiple_multiple_enable_again + +- name: assert that changed is true and each status turns 'enabled'. + assert: + that: + - multiple_multiple_enable_again is not changed + +- name: disable multiple interfaces of multiple interfaces - check mode + win_net_adapter_feature: + interface: + - '{{ network_adapter_name }}' + - '{{ network_adapter_name2 }}' + state: disabled + component_id: + - ms_tcpip6 + - ms_server + check_mode: yes + register: mutliple_multiple_disable_check + +- name: get the status of disable multiple interfaces of multiple interfaces - check mode + ansible.windows.win_shell: | + $info = Get-NetAdapterBinding -Name '{{ network_adapter_name }}', '{{ network_adapter_name2 }}' + @{ + '{{ network_adapter_name }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + ms_server = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name }}' -and $_.ComponentID -eq 'ms_server'}).Enabled + } + '{{ network_adapter_name2 }}' = @{ + ms_tcpip6 = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name2 }}' -and $_.ComponentID -eq 'ms_tcpip6'}).Enabled + ms_server = ($info | Where-Object { $_.Name -eq '{{ network_adapter_name2 }}' -and $_.ComponentID -eq 'ms_server'}).Enabled + } + } | ConvertTo-Json + register: mutliple_multiple_disable_check_actual + +- name: assert disable multiple interfaces of multiple interfaces - check mode + assert: + that: + - mutliple_multiple_disable_check is changed + - (mutliple_multiple_disable_check_actual.stdout | from_json)[network_adapter_name]['ms_tcpip6'] == True + - (mutliple_multiple_disable_check_actual.stdout | from_json)[network_adapter_name]['ms_server'] == True + - (mutliple_multiple_disable_check_actual.stdout | from_json)[network_adapter_name2]['ms_tcpip6'] == True + - (mutliple_multiple_disable_check_actual.stdout | from_json)[network_adapter_name2]['ms_server'] == True + +- name: disable all the interfaces of ms_tcpip6 + win_net_adapter_feature: + interface: '*' + state: disabled + component_id: ms_tcpip6 + register: disable_all + +- name: get the status of disable all the interfaces of ms_tcpip6 + ansible.windows.win_shell: (Get-NetAdapterBinding -Name * -ComponentID ms_tcpip6).Enabled -eq $true + register: disable_all_actual + +- name: assert that changed is true and each status turns 'enabled'. + assert: + that: + - disable_all is changed + - disable_all_actual.stdout_lines == [] + +- name: enable all the interfaces of ms_tcpip6 + win_net_adapter_feature: + interface: '*' + state: enabled + component_id: ms_tcpip6 + register: enable_all + +- name: get the status of enable all the interfaces of ms_tcpip6 + ansible.windows.win_shell: (Get-NetAdapterBinding -Name * -ComponentID ms_tcpip6).Enabled -eq $false + register: enable_all_actual + +- name: assert that changed is true and each status turns 'enabled'. + assert: + that: + - enable_all is changed + - enable_all_actual.stdout_lines == [] diff --git a/ansible_collections/community/windows/tests/integration/targets/win_netbios/aliases b/ansible_collections/community/windows/tests/integration/targets/win_netbios/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_netbios/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_netbios/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_netbios/meta/main.yml new file mode 100644 index 000000000..e7f499ee3 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_netbios/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_win_device
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_netbios/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_netbios/tasks/main.yml new file mode 100644 index 000000000..2f96287bb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_netbios/tasks/main.yml @@ -0,0 +1,30 @@ +# Test code for win_netbios module +# Copyright: (c) 2019, Thomas Moore <hi@tmmr.uk> + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# 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: ensure netbios is set to default to start with + win_netbios: + state: default + +- block: + - name: run tests + include_tasks: tests.yml + + always: + - name: set netbios back to default after tests + win_netbios: + state: default
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_netbios/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_netbios/tasks/tests.yml new file mode 100644 index 000000000..f0d7dd858 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_netbios/tasks/tests.yml @@ -0,0 +1,159 @@ +# Test code for win_netbios module +# Copyright: (c) 2019, Thomas Moore <hi@tmmr.uk> + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# +# 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/>. + +- set_fact: + get_netbios_script: | + $adapter = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetConnectionID='{{ network_adapter_name }}'" + $config = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index=$($adapter.DeviceID)" + $config.TcpipNetbiosOptions + +- name: disable netbios single adapter (check mode) + win_netbios: + adapter_names: '{{ network_adapter_name }}' + state: disabled + register: set_single_check + check_mode: yes + +- name: get result of disable a single adapter test (check mode) + ansible.windows.win_shell: '{{ get_netbios_script }}' + changed_when: no + register: set_single_actual_check + +- name: assert disable a single adapter (check mode) + assert: + that: + - set_single_check is changed + - set_single_actual_check.stdout_lines == ["0"] + +- name: disable netbios single adapter + win_netbios: + adapter_names: '{{ network_adapter_name }}' + state: disabled + register: set_single + +- name: get result of disable a single adapter test + ansible.windows.win_shell: '{{ get_netbios_script }}' + changed_when: no + register: set_single_actual + +- name: assert disable a single adapter + assert: + that: + - set_single_check is changed + - set_single_actual.stdout_lines == ["2"] + +- name: fail with invalid network adapter name + win_netbios: + state: disabled + adapter_names: + - FakeAdapterName + register: invalid_adapter + failed_when: invalid_adapter.msg != "Not all of the target adapter names could be found on the system. No configuration changes have been made. FakeAdapterName" + +- name: disable netbios all adapters (check mode) + win_netbios: + state: disabled + check_mode: yes + register: disable_check + +- name: assert disable netbios (check mode) + assert: + that: + - disable_check.changed + +- name: disable netbios all adapters + win_netbios: + state: disabled + register: netbios_disable + +- name: assert netbios disabled + assert: + that: + - netbios_disable.changed + +- name: test disable idempotence + win_netbios: + state: disabled + register: netbios_disable + +- name: test disable idempotence + assert: + that: + - not netbios_disable.changed + +- name: enable netbios all adapters (check mode) + win_netbios: + state: enabled + check_mode: yes + register: enable_check + +- name: assert enable netbios all adapters (check mode) + assert: + that: + - enable_check.changed + +- name: enable netbios all adapters + win_netbios: + state: enabled + register: netbios_enable + +- name: assert netbios enabled + assert: + that: + - netbios_enable.changed + +- name: test enable idempotence + win_netbios: + state: enabled + register: netbios_enable + +- name: assert enable idempotence + assert: + that: + - not netbios_enable.changed + +- name: default netbios all adapters (check mode) + win_netbios: + state: default + check_mode: yes + register: default_check + +- name: assert default netbios (check mode) + assert: + that: + - default_check.changed + +- name: default netbios all adapters + win_netbios: + state: default + register: default_enable + +- name: assert netbios default all adapters + assert: + that: + - default_enable.changed + +- name: test default idempotence + win_netbios: + state: default + register: netbios_default + +- name: assert default idempotence + assert: + that: + - not netbios_default.changed
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_nssm/aliases b/ansible_collections/community/windows/tests/integration/targets/win_nssm/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_nssm/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_nssm/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_nssm/defaults/main.yml new file mode 100644 index 000000000..005759965 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_nssm/defaults/main.yml @@ -0,0 +1,4 @@ +test_service_name: ansible_nssm_test +test_win_nssm_path: '{{ remote_tmp_dir }}\win_nssm' +test_win_nssm_username: testnssmuser +test_win_nssm_password: Password123!
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_nssm/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_nssm/meta/main.yml new file mode 100644 index 000000000..45806c8dc --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_nssm/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_nssm/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_nssm/tasks/main.yml new file mode 100644 index 000000000..7bd4b493c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_nssm/tasks/main.yml @@ -0,0 +1,56 @@ +--- +- name: install NSSM + chocolatey.chocolatey.win_chocolatey: + name: NSSM + state: present + +- name: ensure testing folder exists + ansible.windows.win_file: + path: '{{test_win_nssm_path}}' + state: directory + +- name: create test user for service execution + ansible.windows.win_user: + name: '{{test_win_nssm_username}}' + password: '{{test_win_nssm_password}}' + state: present + groups_action: add + groups: + - Users + register: user_info + +# Run actual tests +- block: + - name: normalise test account name + ansible.windows.win_powershell: + parameters: + SID: '{{ user_info.sid }}' + script: | + [CmdletBinding()] + param ([String]$SID) + + $Ansible.Changed = $false + ([System.Security.Principal.SecurityIdentifier]$SID).Translate([System.Security.Principal.NTAccount]).Value + register: test_win_nssm_normalised_username + + - set_fact: + test_win_nssm_normalised_username: '{{ test_win_nssm_normalised_username.output[0] }}' + + - include_tasks: tests.yml + + always: + - name: ensure test service is absent + ansible.windows.win_service: + name: '{{ test_service_name }}' + state: absent + + - name: remove test user + ansible.windows.win_user: + name: '{{test_win_nssm_username}}' + state: absent + + - name: uninstall NSSM + chocolatey.chocolatey.win_chocolatey: + name: NSSM + state: absent + failed_when: false diff --git a/ansible_collections/community/windows/tests/integration/targets/win_nssm/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_nssm/tasks/tests.yml new file mode 100644 index 000000000..cf8f0cfbe --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_nssm/tasks/tests.yml @@ -0,0 +1,615 @@ +--- +- name: get register cmd that will get service info + set_fact: + test_service_cmd: | + $res = @{} + $srvobj = Get-WmiObject Win32_Service -Filter "Name=""$service""" | Select Name,DisplayName,Description,PathName,StartMode,StartName,State + if ($srvobj) { + $srvobj | Get-Member -MemberType *Property | % { $res.($_.name) = $srvobj.($_.name) } + + $startName = $res.StartName + $candidates = @(if ($startName -eq "LocalSystem") { + "NT AUTHORITY\SYSTEM" + } + elseif ($startName.Contains('\')) { + $nameSplit = $startName.Split('\', 2) + + if ($nameSplit[0] -eq '.') { + ,@($env:COMPUTERNAME, $nameSplit[1]) + $nameSplit[1] + } else { + ,$nameSplit + } + } + else { + $startName + }) + + $sid = for ($i = 0; $i -lt $candidates.Length; $i++) { + $candidate = $candidates[$i] + $ntAccount = New-Object -TypeName System.Security.Principal.NTAccount -ArgumentList $candidate + try { + $ntAccount.Translate([System.Security.Principal.SecurityIdentifier]) + break + } + catch [System.Security.Principal.IdentityNotMappedException] { + if ($i -eq ($candidates.Length - 1)) { + throw + } + continue + } + } + + $res.StartName = $sid.Translate([System.Security.Principal.NTAccount]).Value + + $res.Exists = $true + $res.Dependencies = @(Get-WmiObject -Query "Associators of {Win32_Service.Name=""$service""} Where AssocClass=Win32_DependentService" | select -ExpandProperty Name) + $res.Parameters = @{} + $srvkey = "HKLM:\SYSTEM\CurrentControlSet\Services\$service\Parameters" + Get-Item "$srvkey" | Select-Object -ExpandProperty property | % { $res.Parameters.$_ = (Get-ItemProperty -Path "$srvkey" -Name $_).$_} + } else { + $res.Exists = $false + } + ConvertTo-Json -InputObject $res -Compress + +- name: install service (check mode) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + state: present + register: install_service_check + check_mode: yes + +- name: get result of install service (check mode) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_check_actual + +- name: assert results of install service (check mode) + assert: + that: + - install_service_check.changed == true + - (install_service_check_actual.stdout|from_json).Exists == false + +- name: install service + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + state: present + register: install_service + +- name: get result of install service + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_actual + +- name: assert results of install service + assert: + that: + - install_service.changed == true + - (install_service_actual.stdout|from_json).Exists == true + - (install_service_actual.stdout|from_json).State == 'Stopped' + - (install_service_actual.stdout|from_json).StartMode == 'Auto' + - (install_service_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_service_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32" + +- name: test install service (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + state: present + register: install_service_again + +- name: get result of install service (idempotent) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_again_actual + +- name: assert results of install service (idempotent) + assert: + that: + - install_service_again.changed == false + - (install_service_again_actual.stdout|from_json).Exists == true + - (install_service_again_actual.stdout|from_json).State == 'Stopped' + - (install_service_again_actual.stdout|from_json).StartMode == 'Auto' + - (install_service_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_service_again_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32" + +- name: install and start service + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + state: started + register: install_start_service + +- name: get result of install and start service + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_start_service_actual + +- name: assert results of install and start service + assert: + that: + - install_start_service.changed == true + - (install_start_service_actual.stdout|from_json).Exists == true + - (install_start_service_actual.stdout|from_json).State == 'Running' + - (install_start_service_actual.stdout|from_json).StartMode == 'Auto' + - (install_start_service_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_start_service_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32" + +- name: install and start service with more parameters (check mode) + win_nssm: + name: '{{ test_service_name }}' + display_name: Ansible testing + description: win_nssm test service + application: C:\Windows\System32\cmd.exe + start_mode: manual + working_directory: '{{ test_win_nssm_path }}' + dependencies: 'tcpip,dnscache' + username: '{{ test_win_nssm_username }}' + password: '{{ test_win_nssm_password }}' + stdout_file: '{{ test_win_nssm_path }}\log.txt' + stderr_file: '{{ test_win_nssm_path }}\error.txt' + state: started + register: install_service_complex_check + check_mode: yes + +- name: get result of install and start service with more parameters (check mode) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_complex_check_actual + +- name: assert results of install and start service with more parameters (check mode) + assert: + that: + - install_service_complex_check.changed == true + - (install_service_complex_check_actual.stdout|from_json).Exists == true + - (install_service_complex_check_actual.stdout|from_json).DisplayName == '{{ test_service_name }}' + - (install_service_complex_check_actual.stdout|from_json).Description is none + - (install_service_complex_check_actual.stdout|from_json).StartMode != 'Manual' + - (install_service_complex_check_actual.stdout|from_json).StartName != test_win_nssm_normalised_username + - (install_service_complex_check_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_service_complex_check_actual.stdout|from_json).Parameters.AppDirectory == "C:\Windows\System32" + - '"AppStdout" not in (install_service_complex_check_actual.stdout|from_json).Parameters' + - '"AppStderr" not in (install_service_complex_check_actual.stdout|from_json).Parameters' + - (install_service_complex_check_actual.stdout|from_json).Dependencies|length == 0 + +- name: install and start service with more parameters + win_nssm: + name: '{{ test_service_name }}' + display_name: Ansible testing + description: win_nssm test service + application: C:\Windows\System32\cmd.exe + start_mode: manual + working_directory: '{{ test_win_nssm_path }}' + dependencies: 'tcpip,dnscache' + username: '{{ test_win_nssm_username }}' + password: '{{ test_win_nssm_password }}' + stdout_file: '{{ test_win_nssm_path }}\log.txt' + stderr_file: '{{ test_win_nssm_path }}\error.txt' + state: started + register: install_service_complex + +- name: get result of install and start service with more parameters + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_complex_actual + +- name: assert results of install and start service with more parameters + assert: + that: + - install_service_complex.changed == true + - (install_service_complex_actual.stdout|from_json).Exists == true + - (install_service_complex_actual.stdout|from_json).DisplayName == 'Ansible testing' + - (install_service_complex_actual.stdout|from_json).Description == 'win_nssm test service' + - (install_service_complex_actual.stdout|from_json).State == 'Running' + - (install_service_complex_actual.stdout|from_json).StartMode == 'Manual' + - (install_service_complex_actual.stdout|from_json).StartName == test_win_nssm_normalised_username + - (install_service_complex_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_service_complex_actual.stdout|from_json).Parameters.AppDirectory == test_win_nssm_path + - (install_service_complex_actual.stdout|from_json).Parameters.AppStdout == test_win_nssm_path + '\\log.txt' + - (install_service_complex_actual.stdout|from_json).Parameters.AppStderr == test_win_nssm_path + '\\error.txt' + - (install_service_complex_actual.stdout|from_json).Dependencies|length == 2 + - '"Tcpip" in (install_service_complex_actual.stdout|from_json).Dependencies' + - '"Dnscache" in (install_service_complex_actual.stdout|from_json).Dependencies' + +- name: install and start service with more parameters (idempotent) + win_nssm: + name: '{{ test_service_name }}' + display_name: Ansible testing + description: win_nssm test service + application: C:\Windows\System32\cmd.exe + start_mode: manual + working_directory: '{{ test_win_nssm_path }}' + # Dependencies order should not trigger a change + dependencies: 'dnscache,tcpip' + username: '{{ test_win_nssm_username }}' + password: '{{ test_win_nssm_password }}' + stdout_file: '{{ test_win_nssm_path }}\log.txt' + stderr_file: '{{ test_win_nssm_path }}\error.txt' + state: started + register: install_service_complex_again + +- name: get result of install and start service with more parameters (idempotent) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: install_service_complex_again_actual + +- name: assert results of install and start service with more parameters (idempotent) + assert: + that: + - install_service_complex_again.changed == false + - (install_service_complex_again_actual.stdout|from_json).Exists == true + - (install_service_complex_again_actual.stdout|from_json).DisplayName == 'Ansible testing' + - (install_service_complex_again_actual.stdout|from_json).Description == 'win_nssm test service' + - (install_service_complex_again_actual.stdout|from_json).State == 'Running' + - (install_service_complex_again_actual.stdout|from_json).StartMode == 'Manual' + - (install_service_complex_again_actual.stdout|from_json).StartName == test_win_nssm_normalised_username + - (install_service_complex_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + - (install_service_complex_again_actual.stdout|from_json).Parameters.AppDirectory == test_win_nssm_path + - (install_service_complex_again_actual.stdout|from_json).Parameters.AppStdout == test_win_nssm_path + '\\log.txt' + - (install_service_complex_again_actual.stdout|from_json).Parameters.AppStderr == test_win_nssm_path + '\\error.txt' + - (install_service_complex_again_actual.stdout|from_json).Dependencies|length == 2 + - '"Tcpip" in (install_service_complex_again_actual.stdout|from_json).Dependencies' + - '"Dnscache" in (install_service_complex_again_actual.stdout|from_json).Dependencies' + +- name: install service with string form parameters + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + arguments: '-v -Dtest.str=value "C:\with space\\"' + state: present + register: str_params + +- name: get result of install service with string form parameters + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: str_params_actual + +- name: assert results of install service with string form parameters + assert: + that: + - str_params.changed == true + - (str_params_actual.stdout|from_json).Exists == true + - (str_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: -v -Dtest.str=value "C:\with space\\" (backslashes doubled for jinja) + - (str_params_actual.stdout|from_json).Parameters.AppParameters == '-v -Dtest.str=value "C:\\with space\\\\"' + +- name: install service with string form parameters (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + arguments: '-v -Dtest.str=value "C:\with space\\"' + state: present + register: str_params_again + +- name: get result of install service with string form parameters (idempotent) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: str_params_again_actual + +- name: assert results of install service with string form parameters (idempotent) + assert: + that: + - str_params_again.changed == false + - (str_params_again_actual.stdout|from_json).Exists == true + - (str_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: -v -Dtest.str=value "C:\with space\\" (backslashes doubled for jinja) + - (str_params_again_actual.stdout|from_json).Parameters.AppParameters == '-v -Dtest.str=value "C:\\with space\\\\"' + +- name: install service with extra environment vars (check mode) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + start_mode: manual + state: present + app_environment: + foo: bar + baz: 2 + register: install_service_appenv_check + check_mode: yes + +- name: get result of install service with extra environment vars (check mode) + ansible.windows.win_shell: nssm.exe get '{{ test_service_name }}' AppEnvironmentExtra + register: install_service_appenv_check_actual + + ## note: this could fail (in theory) when the service is not yet + ## installed (diff mode), but because of side effects of earlier + ## tests this will actually not fail in practice, however, it is + ## not a real issue in any case + failed_when: false + +- name: assert results of install service with extra environment vars (check mode) + assert: + that: + - install_service_appenv_check.changed == true + - install_service_appenv_check_actual.stdout == '\r\n' or install_service_appenv_check_actual.stdout == '' + +- name: install service with extra environment vars + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + start_mode: manual + state: present + app_environment: + foo: bar + baz: 2 + register: install_service_appenv + +- name: get result of install service with extra environment vars + ansible.windows.win_shell: nssm.exe get '{{ test_service_name }}' AppEnvironmentExtra + register: install_service_appenv_actual + +- name: assert results of install service with extra environment vars + assert: + that: + - install_service_appenv.changed == true + - (install_service_appenv_actual.stdout_lines|length) == 3 + - (install_service_appenv_actual.stdout_lines[0]) == 'baz=2' + - (install_service_appenv_actual.stdout_lines[2]) == 'foo=bar' + +- name: install service with extra environment vars (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + start_mode: manual + state: present + app_environment: + foo: bar + baz: 2 + register: install_service_appenv_idem + +- name: get result of install service with extra environment vars (idempotent) + ansible.windows.win_shell: nssm.exe get '{{ test_service_name }}' AppEnvironmentExtra + register: install_service_appenv_idem_actual + +- name: assert results of install service with extra environment vars (idempotent) + assert: + that: + - install_service_appenv_idem.changed == false + - (install_service_appenv_idem_actual.stdout_lines|length) == 3 + - (install_service_appenv_idem_actual.stdout_lines[0]) == 'baz=2' + - (install_service_appenv_idem_actual.stdout_lines[2]) == 'foo=bar' + +- name: install service dont change app_env if not explicitly requested + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + start_mode: manual + state: present + register: install_service_appenv_implicit + +- name: get result of install service dont change app_env if not explicitly requested + ansible.windows.win_shell: nssm.exe get '{{ test_service_name }}' AppEnvironmentExtra + register: install_service_appenv_implicit_actual + +- name: assert results of install service dont change app_env if not explicitly requested + assert: + that: + - install_service_appenv_implicit.changed == false + - (install_service_appenv_implicit_actual.stdout_lines|length) == 3 + - (install_service_appenv_implicit_actual.stdout_lines[0]) == 'baz=2' + - (install_service_appenv_implicit_actual.stdout_lines[2]) == 'foo=bar' + +- name: install service resetting env vars + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + start_mode: manual + state: present + app_environment: {} + register: install_service_reset_appenv + +- name: get result of install service resetting env vars + ansible.windows.win_shell: nssm.exe get '{{ test_service_name }}' AppEnvironmentExtra + register: install_service_reset_appenv_actual + +- name: assert results of install service resetting env vars + assert: + that: + - install_service_reset_appenv.changed == true + - install_service_reset_appenv_actual.stdout == '\r\n' + +# deprecated in 2.12 +- name: install service with dict-as-string parameters + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + app_parameters: foo=true; -file.out=output.bat; -path=C:\with space\; -str=test"quotes; _=bar + register: mixed_params + +# deprecated in 2.12 +- name: get result of install service with dict-as-string parameters + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: mixed_params_actual + +# deprecated in 2.12 +- name: assert results of install service with dict-as-string parameters + assert: + that: + - mixed_params.changed == true + - (mixed_params_actual.stdout|from_json).Exists == true + - (mixed_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: bar -file.out output.bat -str "test\"quotes" foo true -path "C:\with space\\" (backslashes doubled for jinja) + - (mixed_params_actual.stdout|from_json).Parameters.AppParameters == 'bar -file.out output.bat -str "test\\"quotes" foo true -path "C:\\with space\\\\"' + +# deprecated in 2.12 +- name: install service with dict-as-string parameters (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + app_parameters: foo=true; -file.out=output.bat; -path=C:\with space\; -str=test"quotes; _=bar + register: mixed_params_again + +# deprecated in 2.12 +- name: get result of install service with dict-as-string parameters (idempotent) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: mixed_params_again_actual + +# deprecated in 2.12 +- name: assert results of install service with dict-as-string parameters (idempotent) + assert: + that: + - mixed_params_again.changed == false + - (mixed_params_again_actual.stdout|from_json).Exists == true + - (mixed_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: bar -file.out output.bat -str "test\"quotes" foo true -path "C:\with space\\" (backslashes doubled for jinja) + - (mixed_params_again_actual.stdout|from_json).Parameters.AppParameters == 'bar -file.out output.bat -str "test\\"quotes" foo true -path "C:\\with space\\\\"' + +- name: install service with list of parameters + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + arguments: + - -foo=bar + - -day + # Test non-string value + - 14 + # Test if dot is not interpreted as separator (see #44079) + - -file.out + # Test if spaces are escaped + - C:\with space\output.bat + - -str + # Test if quotes and backslashes are escaped + - test"quotes\ + register: list_params + +- name: get result of install service with list of parameters + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: list_params_actual + +- name: assert results of install service with list of parameters + assert: + that: + - list_params.changed == true + - (list_params_actual.stdout|from_json).Exists == true + - (list_params_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: -foo=bar -day 14 -file.out "C:\with space\output.bat" -str "test\"quotes\\" (backslashes doubled for jinja) + - (list_params_actual.stdout|from_json).Parameters.AppParameters == '-foo=bar -day 14 -file.out "C:\\with space\\output.bat" -str "test\\"quotes\\\\"' + +- name: install service with list of parameters (idempotent) + win_nssm: + name: '{{ test_service_name }}' + application: C:\Windows\System32\cmd.exe + arguments: + - -foo=bar + - -day + - 14 + - -file.out + - C:\with space\output.bat + - -str + - test"quotes\ + register: list_params_again + +- name: get result of install service with list of parameters (idempotent) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: list_params_again_actual + +- name: assert results of install service with list of parameters (idempotent) + assert: + that: + - list_params_again.changed == false + - (list_params_again_actual.stdout|from_json).Exists == true + - (list_params_again_actual.stdout|from_json).Parameters.Application == "C:\Windows\System32\cmd.exe" + # Expected value: -foo=bar -day 14 -file.out "C:\with space\output.bat" -str "test\"quotes\\" (backslashes doubled for jinja) + - (list_params_again_actual.stdout|from_json).Parameters.AppParameters == '-foo=bar -day 14 -file.out "C:\\with space\\output.bat" -str "test\\"quotes\\\\"' + +- name: set service username to SYSTEM + win_nssm: + name: '{{ test_service_name }}' + username: LocalSystem + application: C:\Windows\System32\cmd.exe + register: service_system + +- name: get service account for SYSTEM + ansible.windows.win_service_info: + name: '{{ test_service_name }}' + register: service_system_actual + +- name: assert set service username to SYSTEM + assert: + that: + - service_system is changed + - service_system_actual.services[0].username == 'NT AUTHORITY\\SYSTEM' + +- name: set service username to SYSTEM (idempotent) + win_nssm: + name: '{{ test_service_name }}' + username: SYSTEM + application: C:\Windows\System32\cmd.exe + register: service_system_again + +- name: assert set service username to SYSTEM (idempotent) + assert: + that: + - not service_system_again is changed + +- name: set service username to NETWORK SERVICE + win_nssm: + name: '{{ test_service_name }}' + username: NETWORK SERVICE + application: C:\Windows\System32\cmd.exe + register: service_network + +- name: get service account for NETWORK SERVICE + ansible.windows.win_service_info: + name: '{{ test_service_name }}' + register: service_network_actual + +- name: assert set service username to NETWORK SERVICE + assert: + that: + - service_network is changed + - service_network_actual.services[0].username == 'NT Authority\\NetworkService' + +- name: set service username to NETWORK SERVICE (idempotent) + win_nssm: + name: '{{ test_service_name }}' + username: NT AUTHORITY\NETWORK SERVICE + application: C:\Windows\System32\cmd.exe + register: service_network_again + +- name: assert set service username to NETWORK SERVICE (idempotent) + assert: + that: + - not service_network_again is changed + +- name: remove service (check mode) + win_nssm: + name: '{{ test_service_name }}' + state: absent + register: remove_service_check + check_mode: yes + +- name: get result of remove service (check mode) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: remove_service_check_actual + +- name: assert results of remove service (check mode) + assert: + that: + - remove_service_check.changed == true + - (remove_service_check_actual.stdout|from_json).Exists == true + +- name: remove service + win_nssm: + name: '{{ test_service_name }}' + state: absent + register: remove_service + +- name: get result of remove service + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: remove_service_actual + +- name: assert results of remove service + assert: + that: + - remove_service.changed == true + - (remove_service_actual.stdout|from_json).Exists == false + +- name: remove service (idempotent) + win_nssm: + name: '{{ test_service_name }}' + state: absent + register: remove_service_again + +- name: get result of remove service (idempotent) + ansible.windows.win_shell: '$service = ''{{ test_service_name }}''; {{ test_service_cmd }}' + register: remove_service_again_actual + +- name: assert results of remove service (idempotent) + assert: + that: + - remove_service_again.changed == false + - (remove_service_again_actual.stdout|from_json).Exists == false diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pagefile/aliases b/ansible_collections/community/windows/tests/integration/targets/win_pagefile/aliases new file mode 100644 index 000000000..a4da730ea --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pagefile/aliases @@ -0,0 +1,2 @@ +shippable/windows/group3 +unstable diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pagefile/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_pagefile/tasks/main.yml new file mode 100644 index 000000000..0cde27bfa --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pagefile/tasks/main.yml @@ -0,0 +1,241 @@ +--- +# Get current pagefiles status +- name: Get original pagefile settings + win_pagefile: + state: query + register: original_pagefile_settings + +# Remove all original pagefiles +- name: Remove all original pagefiles + win_pagefile: + remove_all: true + register: remove_all_pagefiles + +# Test 1: Set c pagefile with inital and maximum size +- name: Set C pagefile as 1024-2048MB + win_pagefile: + remove_all: yes + drive: C + initial_size: 1024 + maximum_size: 2048 + override: yes + state: present + register: c_pagefile + +- name: Test set c pagefile + assert: + that: + - c_pagefile.changed == true + +- name: Query all pagefiles + win_pagefile: + state: query + register: pagefiles_query + +- name: Set fact for pagefile expected result + set_fact: + expected: + pagefiles: + - caption: "C:\\ 'pagefile.sys'" + description: "'pagefile.sys' @ C:\\" + initial_size: 1024 + maximum_size: 2048 + name: "C:\\pagefile.sys" + +- name: Test query - c pagefile 1024-2048 + assert: + that: + - pagefiles_query.changed == false + - pagefiles_query.pagefiles == expected.pagefiles + - pagefiles_query.automatic_managed_pagefiles == false + + +# Test 2: Remove c pagefile +- name: Remove C pagefile + win_pagefile: + drive: C + state: absent + register: delete_c_pagefile + +- name: Test removal of c pagefile + assert: + that: + - delete_c_pagefile.changed == true + +- name: Query all pagefiles + win_pagefile: + state: query + register: pagefiles_query + +- name: Set fact for pagefile expected result + set_fact: + expected: + pagefiles: [] + +- name: Test query - no c pagefile + assert: + that: + - pagefiles_query.changed == false + - pagefiles_query.pagefiles == expected.pagefiles + - pagefiles_query.automatic_managed_pagefiles == false + + +# Test 3: Set automatic managed pagefile as true +- name: Set automatic managed pagefiles as true + win_pagefile: + automatic: yes + register: set_automatic_true + +- name: Test removal of c pagefile + assert: + that: + - set_automatic_true.changed == true + - set_automatic_true.automatic_managed_pagefiles == true + + +# Test 4: Set c pagefile as system managed pagefile +- name: Set c pagefile as system managed pagefile + win_pagefile: + drive: C + system_managed: yes + state: present + register: c_pagefile_system_managed + +- name: Test set c pagefile as system managed + assert: + that: + - c_pagefile_system_managed.changed == true + +- name: Query all pagefiles + win_pagefile: + state: query + register: pagefiles_query + +- name: Set fact for pagefile expected result + set_fact: + expected: + pagefiles: + - caption: "C:\\ 'pagefile.sys'" + description: "'pagefile.sys' @ C:\\" + initial_size: 0 + maximum_size: 0 + name: "C:\\pagefile.sys" + +- name: Test query - c pagefile 0-0 (system managed) + assert: + that: + - pagefiles_query.changed == false + - pagefiles_query.pagefiles == expected.pagefiles + - pagefiles_query.automatic_managed_pagefiles == false + +# Test 5: Test no override +- name: Set c pagefile 1024-1024, no override + win_pagefile: + drive: C + initial_size: 1024 + maximum_size: 1024 + override: no + state: present + register: c_pagefile_no_override + +- name: Test set c pagefile no override + assert: + that: + - c_pagefile_no_override.changed == false + +- name: Query all pagefiles + win_pagefile: + state: query + register: pagefiles_query + +- name: Test query - c pagefile unchanged + assert: + that: + - pagefiles_query.changed == false + - pagefiles_query.pagefiles == expected.pagefiles + - pagefiles_query.automatic_managed_pagefiles == false + + +# Test 6: Test override +- name: Set c pagefile 1024-1024, override + win_pagefile: + drive: C + initial_size: 1024 + maximum_size: 1024 + state: present + register: c_pagefile_override + +- name: Test set c pagefile no override + assert: + that: + - c_pagefile_override.changed == true + + # Test 7: Test idempotent +- name: Set c pagefile 1024-1024, idempotent + win_pagefile: + drive: C + initial_size: 1024 + maximum_size: 1024 + override: no + state: present + register: c_pagefile_idempotent + +- name: Test set c pagefile idempotent + assert: + that: + - c_pagefile_idempotent.changed == false + +- name: Query all pagefiles + win_pagefile: + state: query + register: pagefiles_query + +- name: Set fact for pagefile expected result + set_fact: + expected: + pagefiles: + - caption: "C:\\ 'pagefile.sys'" + description: "'pagefile.sys' @ C:\\" + initial_size: 1024 + maximum_size: 1024 + name: "C:\\pagefile.sys" + +- name: Test query - c pagefile 1024-1024 + assert: + that: + - pagefiles_query.changed == false + - pagefiles_query.pagefiles == expected.pagefiles + - pagefiles_query.automatic_managed_pagefiles == false + +# Test 7: Remove all pagefiles +- name: Remove all pagefiles + win_pagefile: + remove_all: true + register: remove_all_pagefiles + +- name: Set fact for pagefile expected result + set_fact: + expected: + pagefiles: [] + +- name: Test query - no pagefiles + assert: + that: + - remove_all_pagefiles.changed == true + - remove_all_pagefiles.pagefiles == expected.pagefiles + - pagefiles_query.automatic_managed_pagefiles == false + +# Return all pagefile settings to its original state +- name: Remove all pagefiles and return automatic to its original state + win_pagefile: + remove_all: yes + automatic: "{{ original_pagefile_settings.automatic_managed_pagefiles }}" + +- name: Return all previous pagefiles settings + win_pagefile: + drive: "{{ item.name[0] }}" + initial_size: "{{ item.initial_size }}" + maximum_size: "{{ item.maximum_size }}" + test_path: no + state: present + with_items: "{{ original_pagefile_settings.pagefiles }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_partition/aliases b/ansible_collections/community/windows/tests/integration/targets/win_partition/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_partition/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_partition/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_partition/defaults/main.yml new file mode 100644 index 000000000..ca221d09b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_partition/defaults/main.yml @@ -0,0 +1 @@ +AnsibleVhdx: '{{ remote_tmp_dir }}\AnsiblePart.vhdx' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_partition/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_partition/meta/main.yml new file mode 100644 index 000000000..45806c8dc --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_partition/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_partition/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_partition/tasks/main.yml new file mode 100644 index 000000000..50d086ae1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_partition/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: Copy VHDX scripts + ansible.windows.win_template: + src: "{{ item.src }}" + dest: '{{ remote_tmp_dir }}\{{ item.dest }}' + loop: + - { src: vhdx_creation_script.j2, dest: vhdx_creation_script.txt } + - { src: vhdx_deletion_script.j2, dest: vhdx_deletion_script.txt } + +- name: Create VHD + ansible.windows.win_command: diskpart.exe /s "{{ remote_tmp_dir }}\vhdx_creation_script.txt" + +- name: Run tests + block: + - include: tests.yml + always: + - name: Detach disk + ansible.windows.win_command: diskpart.exe /s "{{ remote_tmp_dir }}\vhdx_deletion_script.txt" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_partition/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_partition/tasks/tests.yml new file mode 100644 index 000000000..83f189a6b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_partition/tasks/tests.yml @@ -0,0 +1,261 @@ +--- +- name: Since partition is not present, disk_number is required to create a new partition. + win_partition: + drive_letter: D + register: incorrect_attempt_1 + ignore_errors: True + +- assert: + that: + - incorrect_attempt_1 is failed + - '"Missing required parameter: disk_number" in incorrect_attempt_1.msg' + +- name: Added disk_number but size is still absent + win_partition: + drive_letter: D + disk_number: 0 + register: incorrect_attempt_2 + ignore_errors: True + +- assert: + that: + - incorrect_attempt_2 is failed + - '"Missing required parameter: partition_size" in incorrect_attempt_2.msg' + +- name: Added size but the disk we specified earlier doesn't have enough space + win_partition: + drive_letter: D + disk_number: 1 + partition_size: 20 GiB + register: incorrect_attempt_3 + ignore_errors: True + +- assert: + that: + - incorrect_attempt_3 is failed + - '"Partition size is not supported by disk" in incorrect_attempt_3.msg' + +- name: Create 1 gib partition using drive_letter and default (huge) mbr type (check mode) + win_partition: + drive_letter: D + state: present + partition_size: 1 GiB + disk_number: 1 + active: True + register: create_small_part_check + check_mode: True + +- name: Create 1 gib partition using drive_letter and default (huge) mbr type + win_partition: + drive_letter: D + state: present + partition_size: 1 GiB + disk_number: 1 + active: True + register: create_small_part + +- name: Create 1 gib partition using drive_letter and default (huge) mbr type (idempotence) + win_partition: + drive_letter: D + state: present + partition_size: 1 GiB + disk_number: 1 + active: True + register: create_small_part_idempotence + +- name: "Check if partition was created successfully" + ansible.windows.win_shell: $AnsiPart = Get-Partition -DriveLetter D; "$($AnsiPart.DriveLetter),$($AnsiPart.Size),$($AnsiPart.IsActive),$($AnsiPart.MbrType)" + register: get_small_part + +- assert: + that: + - create_small_part_check is changed + - create_small_part is changed + - create_small_part_idempotence is not changed + - get_small_part.stdout | trim == "D,1073741824,True,6" + +- name: "Change drive letter, maximize partition size and set partition to read only (check mode)" + win_partition: + drive_letter: E + state: present + partition_size: -1 + disk_number: 1 + partition_number: 1 + read_only: True + register: upgrade_small_part_check + check_mode: True + +- name: "Change drive letter, maximize partition size and set partition to read only" + win_partition: + drive_letter: E + state: present + partition_size: -1 + disk_number: 1 + partition_number: 1 + read_only: True + register: upgrade_small_part + +- name: "Change drive letter, maximize partition size and set partition to read only (idempotence)" + win_partition: + drive_letter: E + state: present + partition_size: -1 + disk_number: 1 + partition_number: 1 + read_only: True + register: upgrade_small_part_idempotence + +- ansible.windows.win_shell: $AnsiPart = Get-Partition -DriveLetter E; "$($AnsiPart.DriveLetter),$($AnsiPart.Size),$($AnsiPart.IsReadOnly)" + register: get_max_part + +- name: Check if creation and updation were successful + assert: + that: + - upgrade_small_part_check is changed + - upgrade_small_part is changed + - upgrade_small_part_idempotence is not changed + - get_max_part.stdout | trim == "E,2096037888,True" + +- name: "Changing size of a read only partition" + win_partition: + drive_letter: E + partition_size: 1 GiB + register: modify_read_only_partition + ignore_errors: True + +- assert: + that: + - modify_read_only_partition is failed + +- name: "Delete partition (check mode)" + win_partition: + disk_number: 1 + partition_number: 1 + state: absent + register: delete_partition_check + check_mode: True + +- name: "Delete partition" + win_partition: + disk_number: 1 + partition_number: 1 + state: absent + register: delete_partition + +- name: "Delete partition (idempotence)" + win_partition: + disk_number: 1 + partition_number: 1 + state: absent + register: delete_partition_idempotence + +- name: "Confirm that the partition is absent" + ansible.windows.win_shell: Get-Partition -DiskNumber 1 -PartitionNumber 1 + register: confirm_partition_deletion + ignore_errors: True + +- assert: + that: + - delete_partition_check is changed + - delete_partition is changed + - delete_partition_idempotence is not changed + - '"No matching MSFT_Partition objects found" in confirm_partition_deletion.stderr' + +- name: "Create new partition without drive letter and ifs mbr type (check mode)" + win_partition: + disk_number: 1 + partition_size: -1 + mbr_type: ifs + offline: True + register: recreate_partition_check + check_mode: True + +- name: "Create new partition without drive letter and ifs mbr type" + win_partition: + disk_number: 1 + partition_size: -1 + mbr_type: ifs + offline: True + register: recreate_partition + +- name: "Create new partition without drive letter and ifs mbr type (idempotence failure)" # Disk is full now; no idempotence without drive letters + win_partition: + disk_number: 1 + partition_size: -1 + mbr_type: ifs + offline: True + register: recreate_partition_idempotence_failure + ignore_errors: True + +- name: "Confirm that new partition is created with maximum size, is offline and is IFS" + ansible.windows.win_shell: $AnsiPart = Get-Partition -DiskNumber 1 -PartitionNumber 1; "$($AnsiPart.Size),$($AnsiPart.IsOffline),$($AnsiPart.MbrType)" + register: confirm_recreate_partition + +- assert: + that: + - recreate_partition_check is changed + - recreate_partition is changed + - recreate_partition_idempotence_failure is failed + - confirm_recreate_partition.stdout | trim == "2096037888,True,7" + +- name: "Adding a drive letter to our partition should bring it back online (check mode)" + win_partition: + drive_letter: D + disk_number: 1 + partition_number: 1 + register: add_drive_letter_check + ignore_errors: True + check_mode: True + +- name: "Adding a drive letter to our partition should bring it back online" + win_partition: + drive_letter: D + disk_number: 1 + partition_number: 1 + register: add_drive_letter + ignore_errors: True + +- name: "Adding a drive letter to our partition should bring it back online (idempotence)" + win_partition: + drive_letter: D + disk_number: 1 + partition_number: 1 + register: add_drive_letter_idempotence + ignore_errors: True + +- name: "Confirm that drive is back online" + ansible.windows.win_shell: $AnsiPart = Get-Partition -DiskNumber 1 -PartitionNumber 1; "$($AnsiPart.DriveLetter),$($AnsiPart.IsOffline)" + register: confirm_add_drive_letter + ignore_errors: True + +- assert: + that: + - add_drive_letter_check is changed + - add_drive_letter is changed + - add_drive_letter_idempotence is not changed + - confirm_add_drive_letter.stdout | trim == "D,False" + +- name: "Remove partition again (check mode)" + win_partition: + drive_letter: D + state: absent + register: delete_partition_again_check + check_mode: True + +- name: "Remove partition again" + win_partition: + drive_letter: D + state: absent + register: delete_partition_again + +- name: "Remove partition again (idempotence)" + win_partition: + drive_letter: D + state: absent + register: delete_partition_again_idempotence + +- assert: + that: + - delete_partition_again_check is changed + - delete_partition_again is changed + - delete_partition_again_idempotence is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_partition/templates/vhdx_creation_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_partition/templates/vhdx_creation_script.j2 new file mode 100644 index 000000000..905373be1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_partition/templates/vhdx_creation_script.j2 @@ -0,0 +1,7 @@ +create vdisk file="{{ AnsibleVhdx }}" maximum=2000 type=fixed + +select vdisk file="{{ AnsibleVhdx }}" + +attach vdisk + +convert mbr diff --git a/ansible_collections/community/windows/tests/integration/targets/win_partition/templates/vhdx_deletion_script.j2 b/ansible_collections/community/windows/tests/integration/targets/win_partition/templates/vhdx_deletion_script.j2 new file mode 100644 index 000000000..c2be9cd14 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_partition/templates/vhdx_deletion_script.j2 @@ -0,0 +1,3 @@ +select vdisk file="{{ AnsibleVhdx }}" + +detach vdisk diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/aliases b/ansible_collections/community/windows/tests/integration/targets/win_pester/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_pester/defaults/main.yml new file mode 100644 index 000000000..3507a0fda --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/defaults/main.yml @@ -0,0 +1,3 @@ +--- +test_win_pester_path: C:\ansible\win_pester +test_report_file: c:\ansible\win_pester\test_report.xml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/files/fail.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/fail.ps1 new file mode 100644 index 000000000..4bd20a601 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/fail.ps1 @@ -0,0 +1,2 @@ +# This makes sure that a file that does not end with *.test.ps1 does not run +throw "should never fail" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test01.tests.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test01.tests.ps1 new file mode 100644 index 000000000..6248b2f77 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test01.tests.ps1 @@ -0,0 +1,5 @@ +Describe -Name 'Test01' { + It -name 'First Test' { + { Get-Service } | Should Not Throw + } +}
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test02.tests.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test02.tests.ps1 new file mode 100644 index 000000000..6d2477cc8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test02.tests.ps1 @@ -0,0 +1,5 @@ +Describe -Name 'Test02' { + It -name 'Second Test' { + { Get-Service } | Should Throw + } +}
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test03.tests.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test03.tests.ps1 new file mode 100644 index 000000000..8dafbc6ff --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test03.tests.ps1 @@ -0,0 +1,11 @@ +Describe -Name 'Test03 without tag' { + It -name 'Third Test without tag' { + { Get-Service } | Should Not Throw + } +} + +Describe -Name 'Test03 with tag' -Tag tag1 { + It -name 'Third Test with tag' { + { Get-Service } | Should Not Throw + } +}
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test04.tests.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test04.tests.ps1 new file mode 100644 index 000000000..07d1bd43c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/files/test04.tests.ps1 @@ -0,0 +1,18 @@ +Param( + $Service, + $Process +) + +Describe "Process should exist" { + it "Process $Process should be running" -Skip:([String]::IsNullOrEmpty($Process)) { + Get-Process -Name $Process -ErrorAction SilentlyContinue | Should Not BeNullOrEmpty + } +} + + + +Describe "Service should exist" -tag Service { + it "Service $Service should exist" -Skip:([String]::IsNullOrEmpty($Service)) { + Get-Service -Name $Service -ErrorAction SilentlyContinue | Should Not BeNullOrEmpty + } +} diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_pester/tasks/main.yml new file mode 100644 index 000000000..baf6b0c50 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/tasks/main.yml @@ -0,0 +1,60 @@ +--- +- name: create test folder(s) + ansible.windows.win_file: + path: '{{test_win_pester_path}}\{{item}}' + state: directory + with_items: + - Modules + - Tests + +- name: download Pester module from S3 bucket + ansible.windows.win_get_url: + # this was downloaded straight off the Pester GitHub release page and uploaded to the S3 bucket + # https://github.com/pester/Pester/releases + url: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_pester/Pester-4.3.1.zip' + dest: '{{test_win_pester_path}}\Pester-4.3.1.zip' + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + +- name: unzip Pester module + win_unzip: + src: '{{test_win_pester_path}}\Pester-4.3.1.zip' + dest: '{{test_win_pester_path}}\Modules' + +- name: rename extracted zip to match module name + ansible.windows.win_shell: Rename-Item -Path '{{test_win_pester_path}}\Modules\Pester-4.3.1' -NewName Pester + +- name: add custom Pester location to the PSModulePath + ansible.windows.win_path: + name: PSModulePath + scope: machine + state: present + elements: + - '{{test_win_pester_path}}\Modules' + +- name: copy test files + ansible.windows.win_copy: + src: files/ + dest: '{{test_win_pester_path}}\Tests' + +- block: + - name: run Pester tests + include_tasks: test.yml + vars: + test_path: '{{ test_win_pester_path }}\Tests' + + always: + - name: remove custom pester location on the PSModulePath + ansible.windows.win_path: + name: PSModulePath + scope: machine + state: absent + elements: + - '{{test_win_pester_path}}\Modules' + + - name: delete test folder + ansible.windows.win_file: + path: '{{test_win_pester_path}}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pester/tasks/test.yml b/ansible_collections/community/windows/tests/integration/targets/win_pester/tasks/test.yml new file mode 100644 index 000000000..cbcbd4cbe --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pester/tasks/test.yml @@ -0,0 +1,134 @@ +--- +- name: Run Pester test(s) specifying a fake test file + win_pester: + path: '{{test_path}}\fakefile.ps1' + register: fake_file + failed_when: '"Cannot find file or directory: ''" + test_path + "\\fakefile.ps1'' as it does not exist" not in fake_file.msg' + +- name: Run Pester test(s) specifying a fake folder + win_pester: + path: '{{test_path }}\fakedir' + register: fake_folder + failed_when: '"Cannot find file or directory: ''" + test_path + "\\fakedir'' as it does not exist" not in fake_folder.msg' + +- name: Run Pester test(s) specifying a test file and a higher pester version + win_pester: + path: '{{test_path}}\test01.tests.ps1' + minimum_version: '6.0.0' + register: invalid_version + failed_when: '"Pester version is not greater or equal to 6.0.0" not in invalid_version.msg' + +- name: Run Pester test(s) specifying a test file + win_pester: + path: '{{test_path}}\test01.tests.ps1' + register: file_result + +- name: assert Run Pester test(s) specify a test file + assert: + that: + - file_result.changed + - not file_result.failed + - file_result.output.TotalCount == 1 + +- name: Run Pester test(s) specifying a test file and with a minimum mandatory Pester version + win_pester: + path: '{{test_path}}\test01.tests.ps1' + minimum_version: 3.0.0 + register: file_result_with_version + +- name: assert Run Pester test(s) specifying a test file and a minimum mandatory Pester version + assert: + that: + - file_result_with_version.changed + - not file_result_with_version.failed + - file_result_with_version.output.TotalCount == 1 + +- name: Run Pester test(s) located in a folder. Folder path end with '\' + win_pester: + path: '{{test_path}}\' + register: dir_with_ending_slash + +- name: assert Run Pester test(s) located in a folder. Folder path end with '\' + assert: + that: + - dir_with_ending_slash.changed + - not dir_with_ending_slash.failed + - dir_with_ending_slash.output.TotalCount == 6 + +- name: Run Pester test(s) located in a folder. Folder path does not end with '\' + win_pester: + path: '{{test_path}}' + register: dir_without_ending_slash + +- name: assert Run Pester test(s) located in a folder. Folder does not end with '\' + assert: + that: + - dir_without_ending_slash.changed + - not dir_without_ending_slash.failed + - dir_without_ending_slash.output.TotalCount == 6 + +- name: Run Pester test(s) located in a folder and with a minimum mandatory Pester version + win_pester: + path: '{{test_path}}' + minimum_version: 3.0.0 + register: dir_with_version + +- name: assert Run Pester test(s) located in a folder and with a minimum mandatory Pester version + assert: + that: + - dir_with_version.changed + - not dir_with_version.failed + - dir_with_version.output.TotalCount == 6 + +- name: Run Pester test(s) specifying a test file without specifying tag + win_pester: + path: '{{test_path}}\test03.tests.ps1' + register: test_no_tag + +- name: assert Run Pester test(s) specifying a test file and all tests executed + assert: + that: + - test_no_tag.changed + - test_no_tag.output.TotalCount == 2 + +- name: Run Pester test(s) specifying a test file with tag + win_pester: + path: '{{test_path}}\test03.tests.ps1' + tags: tag1 + register: test_with_tag + +- name: Run Pester test(s) specifying a test file and only test with sepecified tag executed + assert: + that: + - test_with_tag.changed + - test_with_tag.output.TotalCount == 1 + +- name: Run Pester test(s) specifying a test file with parameters + win_pester: + path: '{{test_path}}\test04.tests.ps1' + test_parameters: + Process: lsass + Service: bits + register: test_with_parameter + +- name: Run Pester test(s) specifying a test file with parameters + assert: + that: + - test_with_parameter.changed + - test_with_parameter.output.PassedCount == 2 + - test_with_parameter.output.TotalCount == 2 + +- name: Run Pester test(s) specifying a test file by generating test result xml + win_pester: + path: '{{test_path}}\test03.tests.ps1' + output_file: '{{test_report_file}}' + +- name: Checks if the output result file exists + ansible.windows.win_stat: + path: '{{test_report_file}}' + register: test_output_file + +- name: Checks if the output result file exists + assert: + that: + - test_output_file.stat.exists diff --git a/ansible_collections/community/windows/tests/integration/targets/win_power_plan/aliases b/ansible_collections/community/windows/tests/integration/targets/win_power_plan/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_power_plan/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_power_plan/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_power_plan/tasks/main.yml new file mode 100644 index 000000000..cffc8447c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_power_plan/tasks/main.yml @@ -0,0 +1,128 @@ +# I dislike this but 2008 doesn't support the Win32_PowerPlan WMI provider +- name: get current plan details + ansible.windows.win_shell: | + $plan_info = powercfg.exe /list + ($plan_info | Select-String -Pattern '\(([\w\s]*)\) \*$').Matches.Groups[1].Value + ($plan_info | Select-String -Pattern '\(([\w\s]*)\)$').Matches.Groups[1].Value + register: plan_info + +- set_fact: + original_plan: '{{ plan_info.stdout_lines[0] }}' + name: '{{ plan_info.stdout_lines[1] }}' + +- block: + #Test that plan detects change is needed, but doesn't actually apply change + - name: set power plan (check mode) + win_power_plan: + name: "{{ name }}" + register: set_plan_check + check_mode: yes + + - name: get result of set power plan (check mode) + ansible.windows.win_shell: (powercfg.exe /list | Select-String -Pattern '\({{ name }}\)').Line + register: set_plan_check_result + changed_when: False + + # verify that the powershell check is showing the plan as still inactive on the system + - name: assert setting plan (check mode) + assert: + that: + - set_plan_check is changed + - not set_plan_check_result.stdout_lines[0].endswith('*') + + #Test that setting plan and that change is applied + - name: set power plan + win_power_plan: + name: "{{ name }}" + register: set_plan + + - name: get result of set power plan + ansible.windows.win_shell: (powercfg.exe /list | Select-String -Pattern '\({{ name }}\)').Line + register: set_plan_result + changed_when: False + + - name: assert setting plan + assert: + that: + - set_plan is changed + - set_plan_result.stdout_lines[0].endswith('*') + + #Test that plan doesn't apply change if it is already set + - name: set power plan (idempotent) + win_power_plan: + name: "{{ name }}" + register: set_plan_idempotent + + - name: assert setting plan (idempotent) + assert: + that: + - set_plan_idempotent is not changed + + always: + - name: always change back plan to the original when done testing + win_power_plan: + name: '{{ original_plan }}' + +- name: get current plan guid details + ansible.windows.win_shell: | + $plan_info = powercfg.exe /list + ($plan_info | Select-String -Pattern '([a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+).+\*').Matches.Groups[1].Value + ($plan_info | Select-String -Pattern '([a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+\-[a-z0-9]+)').Matches.Groups[1].Value + register: guid_plan_info + +- set_fact: + original_plan: '{{ guid_plan_info.stdout_lines[0] }}' + name: '{{ guid_plan_info.stdout_lines[1] }}' + +- block: + #Test that plan detects change is needed, but doesn't actually apply change + - name: set power plan guid (check mode) + win_power_plan: + guid: "{{ name }}" + register: set_plan_check_guid + check_mode: yes + + - name: get result of set power plan guid (check mode) + ansible.windows.win_shell: (powercfg.exe /list | Select-String -Pattern '{{ name }}').Line + register: set_plan_check_result_guid + changed_when: False + + # verify that the powershell check is showing the plan as still inactive on the system + - name: assert setting plan guid (check mode) + assert: + that: + - set_plan_check_guid is changed + - not set_plan_check_result_guid.stdout_lines[0].endswith('*') + + #Test that setting plan and that change is applied + - name: set power plan guid + win_power_plan: + guid: "{{ name }}" + register: set_plan_guid + + - name: get result of set power plan guid + ansible.windows.win_shell: (powercfg.exe /list | Select-String -Pattern '{{ name }}').Line + register: set_plan_result_guid + changed_when: False + + - name: assert setting plan guid + assert: + that: + - set_plan_guid is changed + - set_plan_result_guid.stdout_lines[0].endswith('*') + + #Test that plan doesn't apply change if it is already set + - name: set power plan guid (idempotent) + win_power_plan: + guid: "{{ name }}" + register: set_plan_idempotent_guid + + - name: assert setting plan guid (idempotent) + assert: + that: + - set_plan_idempotent_guid is not changed + + always: + - name: always change back plan to the original when done testing guid + win_power_plan: + guid: '{{ original_plan }}' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_product_facts/aliases b/ansible_collections/community/windows/tests/integration/targets/win_product_facts/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_product_facts/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_product_facts/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_product_facts/tasks/main.yml new file mode 100644 index 000000000..b1427eeaa --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_product_facts/tasks/main.yml @@ -0,0 +1,11 @@ +# This file is part of Ansible + +# Copyright: (c) 2017, Dag Wieers (dagwieers) <dag@wieers.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- win_product_facts: + +- assert: + that: + - ansible_os_product_id is defined + - ansible_os_product_key is defined diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psexec/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psexec/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psexec/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psexec/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psexec/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psexec/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psexec/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psexec/tasks/main.yml new file mode 100644 index 000000000..5f4be3ebf --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psexec/tasks/main.yml @@ -0,0 +1,102 @@ +# 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 + ansible.windows.win_file: + path: '{{ testing_dir }}' + state: directory + +- name: Download PsExec + ansible.windows.win_get_url: + url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_psexec/PsExec.exe + dest: '{{ testing_dir }}\PsExec.exe' + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + +- name: Get the existing PATH env var + ansible.windows.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 +#- ansible.windows.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\"" + +- name: Run command with password + win_psexec: + command: whoami.exe + username: fake_user + password: '{{ item }}' + executable: '{{ testing_dir }}\PsExec.exe' + ignore_errors: yes # The username/password isn't valid so it will fail + loop: + - Testing123 + - Testing"123 # This makes sure the escaped password for the cmd is also masked + register: psexec_pass + +- name: Test password not in psexec_command output + assert: + that: + - psexec_pass.results[0].psexec_command.endswith("-u fake_user -p *PASSWORD_REPLACED* -accepteula whoami.exe") + - psexec_pass.results[1].psexec_command.endswith("-u fake_user -p *PASSWORD_REPLACED* -accepteula whoami.exe") diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/license.txt b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/license.txt new file mode 100644 index 000000000..81f2d4566 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Ansible + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.nuspec b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.nuspec new file mode 100644 index 000000000..cb52fb42d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.nuspec @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> + <metadata> + <id>--- NAME ---</id> + <version>--- VERSION ---</version> + <authors>Ansible</authors> + <owners>Ansible</owners> + <requireLicenseAcceptance>--- LICACC ---</requireLicenseAcceptance> + <licenseUrl>https://choosealicense.com/licenses/mit/</licenseUrl> + <description>Test for Ansible win_ps* modules</description> + <releaseNotes></releaseNotes> + <copyright>Copyright (c) 2019 Ansible, licensed under MIT.</copyright> + <tags>PowerShellGetFormatVersion_2.0 PSModule PSIncludes_Function PSFunction_--- FUNCTION --- PSCommand_--- FUNCTION ---</tags> + </metadata> +</package> diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.psd1 b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.psd1 new file mode 100644 index 000000000..cd6709722 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.psd1 @@ -0,0 +1,17 @@ +@{ + RootModule = '--- NAME ---.psm1' + ModuleVersion = '--- VERSION ---' + GUID = '--- GUID ---' + Author = 'Ansible' + Copyright = 'Copyright (c) 2019 Ansible, licensed under MIT.' + Description = "Test for Ansible win_ps* modules" + PowerShellVersion = '3.0' + FunctionsToExport = @( + "--- FUNCTION ---" + ) + PrivateData = @{ + PSData = @{ +--- PS_DATA --- + } + } +} diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.psm1 b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.psm1 new file mode 100644 index 000000000..ac38fb5ed --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/module/template.psm1 @@ -0,0 +1,10 @@ +Function --- FUNCTION --- { + return [PSCustomObject]@{ + Name = "--- NAME ---" + Version = "--- VERSION ---" + Repo = "--- REPO ---" + } +} + +Export-ModuleMember -Function --- FUNCTION --- + diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/openssl.conf b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/openssl.conf new file mode 100644 index 000000000..2b5685b43 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/openssl.conf @@ -0,0 +1,9 @@ +distinguished_name = req_distinguished_name + +[req_distinguished_name] + +[req_sign] +subjectKeyIdentifier=hash +basicConstraints = CA:FALSE +keyUsage = digitalSignature +extendedKeyUsage = codeSigning diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/setup_certs.sh b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/setup_certs.sh new file mode 100644 index 000000000..258567316 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/setup_certs.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# Generate key used for CA cert +openssl genrsa -aes256 -out ca.key -passout pass:password 2048 + +# Generate CA certificate +openssl req -new -x509 -days 365 -key ca.key -out ca.pem -subj "/CN=Ansible Root" -passin pass:password + +# Generate key used for signing cert +openssl genrsa -aes256 -out sign.key -passout pass:password 2048 + +# Generate CSR for signing cert that includes CodeSiging extension +openssl req -new -key sign.key -out sign.csr -subj "/CN=Ansible Sign" -config openssl.conf -reqexts req_sign -passin pass:password + +# Generate signing certificate +openssl x509 -req -in sign.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out sign.pem -days 365 -extfile openssl.conf -extensions req_sign -passin pass:password + +# Create pfx that includes signing cert and cert with the pass 'password' +openssl pkcs12 -export -out sign.pfx -inkey sign.key -in sign.pem -passin pass:password -passout pass:password diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/setup_modules.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/setup_modules.ps1 new file mode 100644 index 000000000..48d55b574 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/files/setup_modules.ps1 @@ -0,0 +1,89 @@ +$ErrorActionPreference = "Stop" + +$template_path = $args[0] +$template_manifest = Join-Path -Path $template_path -ChildPath template.psd1 +$template_script = Join-Path -Path $template_path -ChildPath template.psm1 +$template_nuspec = Join-Path -Path $template_path -ChildPath template.nuspec +$template_license = Join-Path -Path $template_path -ChildPath license.txt +$nuget_exe = Join-Path -Path $template_path -ChildPath nuget.exe +$sign_cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @( + (Join-Path -Path $template_path -ChildPath sign.pfx), + 'password', + # We need to use MachineKeySet so we can load the pfx without using become + # EphemeralKeySet would be better but it is only available starting with .NET 4.7.2 + [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet +) + +$packages = @( + @{ name = "ansible-test1"; version = "1.0.0"; repo = "PSRepo 1"; function = "Get-AnsibleTest1" } + @{ name = "ansible-test1"; version = "1.0.5"; repo = "PSRepo 1"; function = "Get-AnsibleTest1" } + @{ name = "ansible-test1"; version = "1.1.0"; repo = "PSRepo 1"; function = "Get-AnsibleTest1" } + @{ name = "ansible-test2"; version = "1.0.0"; repo = "PSRepo 1"; function = "Get-AnsibleTest2" } + @{ name = "ansible-test2"; version = "1.0.0"; repo = "PSRepo 2"; function = "Get-AnsibleTest2" } + @{ name = "ansible-test2"; version = "1.0.1"; repo = "PSRepo 1"; function = "Get-AnsibleTest2"; signed = $false } + @{ name = "ansible-test2"; version = "1.1.0"; prerelease = "beta1"; repo = "PSRepo 1"; function = "Get-AnsibleTest2" } + @{ name = "ansible-clobber"; version = "0.1.0"; repo = "PSRepo 1"; function = "Enable-PSTrace" } + @{ name = "ansible-licensed" ; version = "1.1.1"; repo = "PSRepo 1" ; require_license = $true; function = "Get-AnsibleLicensed" } +) + +foreach ($package in $packages) { + $tmp_dir = Join-Path -Path $template_path -ChildPath $package.name + if (Test-Path -Path $tmp_dir) { + Remove-Item -Path $tmp_dir -Force -Recurse + } + New-Item -Path $tmp_dir -ItemType Directory > $null + + Copy-Item -LiteralPath $template_license -Destination $tmp_dir -Force + + try { + $ps_data = @("LicenseUri = 'https://choosealicense.com/licenses/mit/'") + $nuget_version = $package.version + if ($package.ContainsKey("prerelease")) { + $ps_data += "Prerelease = '$($package.prerelease)'" + $nuget_version = "$($package.version)-$($package.prerelease)" + } + if ($package.ContainsKey("require_license")) { + $ps_data += "RequireLicenseAcceptance = `$$($package.require_license)" + } + + $manifest = [System.IO.File]::ReadAllText($template_manifest) + $manifest = $manifest.Replace('--- NAME ---', $package.name).Replace('--- VERSION ---', $package.version) + $manifest = $manifest.Replace('--- GUID ---', [Guid]::NewGuid()).Replace('--- FUNCTION ---', $package.function) + + $manifest = $manifest.Replace('--- PS_DATA ---', $ps_data -join "`n") + $manifest_path = Join-Path -Path $tmp_dir -ChildPath "$($package.name).psd1" + Set-Content -Path $manifest_path -Value $manifest + + $script = [System.IO.File]::ReadAllText($template_script) + $script = $script.Replace('--- NAME ---', $package.name).Replace('--- VERSION ---', $package.version) + $script = $script.Replace('--- REPO ---', $package.repo).Replace('--- FUNCTION ---', $package.function) + $script_path = Join-Path -Path $tmp_dir -ChildPath "$($package.name).psm1" + Set-Content -Path $script_path -Value $script + + $signed = if ($package.ContainsKey("signed")) { $package.signed } else { $true } + if ($signed) { + Set-AuthenticodeSignature -Certificate $sign_cert -LiteralPath $manifest_path > $null + Set-AuthenticodeSignature -Certificate $sign_cert -LiteralPath $script_path > $null + } + + # We should just be able to use Publish-Module but it fails when running over WinRM for older hosts and become + # does not fix this. It fails to respond to nuget.exe push errors when it canno find the .nupkg file. We will + # just manually do that ourselves. This also has the added benefit of being a lot quicker than Publish-Module + # which seems to take forever to publish the module. + $nuspec = [System.IO.File]::ReadAllText($template_nuspec) + $nuspec = $nuspec.Replace('--- NAME ---', $package.name).Replace('--- VERSION ---', $nuget_version) + $nuspec = $nuspec.Replace('--- FUNCTION ---', $package.function) + $nuspec = $nuspec.Replace('--- LICACC ---', ($package.require_license -as [bool]).ToString().ToLower()) + Set-Content -Path (Join-Path -Path $tmp_dir -ChildPath "$($package.name).nuspec") -Value $nuspec + + &$nuget_exe pack "$tmp_dir\$($package.name).nuspec" -outputdirectory $tmp_dir + + $repo_path = Join-Path -Path $template_path -ChildPath $package.repo + $nupkg_filename = "$($package.name).$($nuget_version).nupkg" + Copy-Item -Path (Join-Path -Path $tmp_dir -ChildPath $nupkg_filename) ` + -Destination (Join-Path -Path $repo_path -ChildPath $nupkg_filename) + } + finally { + Remove-Item -Path $tmp_dir -Force -Recurse + } +} diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/handlers/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/handlers/main.yml new file mode 100644 index 000000000..13797f236 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/handlers/main.yml @@ -0,0 +1,34 @@ +--- +- name: re-add PSGallery repository + ansible.windows.win_shell: Register-PSRepository -Default -InstallationPolicy Untrusted + +- name: remove registered repos + win_psrepository: + name: '{{ item }}' + state: absent + loop: + - PSRepo 1 + - PSRepo 2 + +- name: remove CA cert from trusted root store + ansible.windows.win_certificate_store: + thumbprint: '{{ ca_cert_import.thumbprints[0] }}' + store_location: LocalMachine + store_name: Root + state: absent + +- name: remove signing key from trusted publisher store + ansible.windows.win_certificate_store: + thumbprint: '{{ sign_cert_import.thumbprints[0] }}' + store_location: LocalMachine + store_name: TrustedPublisher + state: absent + +- name: remove test packages + win_psmodule: + name: '{{ item }}' + state: absent + loop: + - ansible-test1 + - ansible-test2 + - ansible-clobber
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/meta/main.yml new file mode 100644 index 000000000..f0920878a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: +- setup_remote_tmp_dir +- setup_win_psget diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/tasks/main.yml new file mode 100644 index 000000000..ffe1a0978 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/tasks/main.yml @@ -0,0 +1,513 @@ +# test code for the win_psmodule module when using winrm connection +# Copyright: (c) 2018, Wojciech Sciesinski <wojciech[at]sciesinski[dot]net> +# Copyright: (c) 2017, Daniele Lazzari <lazzari@mailup.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup test repos and modules + import_tasks: setup.yml + +# Remove the below task in Ansible 2.12 +- name: ensure warning is fired when adding a repo + win_psmodule: + name: ansible-test1 + repository: some repo + url: '{{ remote_tmp_dir }}' + state: present + register: dep_repo_add + ignore_errors: yes # will fail because this repo doesn't actually have this module + check_mode: yes + +- name: assert warning is fired when adding a repo + assert: + that: + - dep_repo_add is changed + - dep_repo_add.deprecations|length == 1 + - dep_repo_add.deprecations[0].msg == 'Adding a repo with this module is deprecated, the repository parameter should only be used to select a repo. Use community.windows.win_psrepository to manage repos' + - dep_repo_add.deprecations[0].date == '2021-07-01' + +### licensed module checks +# it is not known in check mode that a module requires +# license acceptance, so we don't do check mode tests +# for that scenario + +- name: install module requiring license acceptance (no param) + win_psmodule: + name: ansible-licensed + state: present + register: install_licensed + ignore_errors: yes + +- name: get result of install package + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-licensed | Measure-Object).Count + register: install_actual_check + +- name: assert install package (failure) + assert: + that: + - install_licensed is failed + - '"License Acceptance is required for module" in install_licensed.msg' + - install_actual_check.stdout | trim | int == 0 + +- name: install module requiring license acceptance + win_psmodule: + name: ansible-licensed + state: present + accept_license: true + register: install_licensed + +- name: get result of install package + ansible.windows.win_shell: Import-Module -Name ansible-licensed; Get-AnsibleLicensed | ConvertTo-Json + register: install_actual + +- name: assert install package + assert: + that: + - install_licensed is changed + - install_actual.stdout | from_json == {"Name":"ansible-licensed","Version":"1.1.1","Repo":"PSRepo 1"} + +- name: install module requiring license acceptance (idempotence) + win_psmodule: + name: ansible-licensed + state: present + accept_license: true + register: install_licensed + +- name: get result of install package (idempotence) + ansible.windows.win_shell: Import-Module -Name ansible-licensed; Get-AnsibleLicensed | ConvertTo-Json + register: install_actual + +- name: assert install package (idempotence) + assert: + that: + - install_licensed is not changed + - install_actual.stdout | from_json == {"Name":"ansible-licensed","Version":"1.1.1","Repo":"PSRepo 1"} + +### end licensed module checks + +- name: install package (check mode) + win_psmodule: + name: ansible-test1 + state: present + register: install_check + check_mode: yes + +- name: get result of install package (check mode) + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-test1 | Measure-Object).Count + register: install_actual_check + +- name: assert install package (check mode) + assert: + that: + - install_check is changed + - install_actual_check.stdout | trim | int == 0 + +- name: install package + win_psmodule: + name: ansible-test1 + state: present + register: install + +- name: get result of install package + ansible.windows.win_shell: Import-Module -Name ansible-test1; Get-AnsibleTest1 | ConvertTo-Json + register: install_actual + +- name: assert install package + assert: + that: + - install is changed + - install_actual.stdout | from_json == {"Name":"ansible-test1","Version":"1.1.0","Repo":"PSRepo 1"} + +- name: install package (idempotent) + win_psmodule: + name: ansible-test1 + state: present + register: install_again + +- name: assert install package (idempotent) + assert: + that: + - not install_again is changed + +- name: remove package (check mode) + win_psmodule: + name: ansible-test1 + state: absent + register: remove_check + check_mode: yes + +- name: get result of remove package (check mode) + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-test1 | Measure-Object).Count + register: remove_actual_check + +- name: remove package (check mode) + assert: + that: + - remove_check is changed + - remove_actual_check.stdout | trim | int == 1 + +- name: remove package + win_psmodule: + name: ansible-test1 + state: absent + register: remove + +- name: get result of remove package + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-test1 | Measure-Object).Count + register: remove_actual + +- name: assert remove package + assert: + that: + - remove is changed + - remove_actual.stdout | trim | int == 0 + +- name: remove package (idempotent) + win_psmodule: + name: ansible-test1 + state: absent + register: remove_again + +- name: assert remove package (idempotent) + assert: + that: + - not remove_again is changed + +- name: fail to install module that exists in multiple repos + win_psmodule: + name: ansible-test2 + state: present + register: fail_multiple_pkg + failed_when: 'fail_multiple_pkg.msg != "Problems installing ansible-test2 module: Unable to install, multiple modules matched ''ansible-test2''. Please specify a single -Repository."' + +- name: install module with specific repository + win_psmodule: + name: ansible-test2 + repository: PSRepo 2 + state: present + register: install_repo + +- name: get result of install module with specific repository + ansible.windows.win_shell: Import-Module -Name ansible-test2; Get-AnsibleTest2 | ConvertTo-Json + register: install_repo_actual + +- name: assert install module with specific repository + assert: + that: + - install_repo is changed + - install_repo_actual.stdout | from_json == {"Name":"ansible-test2","Version":"1.0.0","Repo":"PSRepo 2"} + +- name: install module with specific repository (idempotent) + win_psmodule: + name: ansible-test2 + repository: PSRepo 2 + state: present + register: install_repo_again + +- name: assert install module with specific repository (idempotent) + assert: + that: + - not install_repo_again is changed + +- name: remove package that was installed from specific repository + win_psmodule: + name: ansible-test2 + state: absent + register: remove_repo_without_source + +- name: get result of remove package that was installed from specific repository + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-test2 | Measure-Object).Count + register: remove_repo_without_source_actual + +- name: assert remove package that was installed from specific repository + assert: + that: + - remove_repo_without_source is changed + - remove_repo_without_source_actual.stdout | trim | int == 0 + +- name: fail to install required version that is missing + win_psmodule: + name: ansible-test1 + required_version: 0.9.0 + state: present + register: fail_missing_req + failed_when: '"Problems installing ansible-test1 module: No match was found for the specified search criteria" not in fail_missing_req.msg' + +- name: install required version + win_psmodule: + name: ansible-test1 + required_version: 1.0.0 + state: present + register: install_req_version + +- name: get result of install required version + ansible.windows.win_shell: Import-Module -Name ansible-test1; Get-AnsibleTest1 | ConvertTo-Json + register: install_req_version_actual + +- name: assert install required version + assert: + that: + - install_req_version is changed + - install_req_version_actual.stdout | from_json == {"Name":"ansible-test1","Version":"1.0.0","Repo":"PSRepo 1"} + +- name: install required version (idempotent) + win_psmodule: + name: ansible-test1 + required_version: 1.0.0 + state: present + register: install_req_version_again + +- name: assert install required version (idempotent) + assert: + that: + - not install_req_version_again is changed + +- name: remove required version + win_psmodule: + name: ansible-test1 + required_version: 1.0.0 + state: absent + register: remove_req_version + +- name: get result of remove required version + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-test1 | Measure-Object).Count + register: remove_req_version_actual + +- name: assert remove required version + assert: + that: + - remove_req_version is changed + - remove_req_version_actual.stdout | trim | int == 0 + +- name: remove required version (idempotent) + win_psmodule: + name: ansible-test1 + required_version: 1.0.0 + state: absent + register: remove_req_version_again + +- name: assert remove required version (idempotent) + assert: + that: + - not remove_req_version_again is changed + +- name: install min max version + win_psmodule: + name: ansible-test1 + minimum_version: 1.0.1 + maximum_version: 1.0.9 + state: present + register: install_min_max + +- name: get result of install min max version + ansible.windows.win_shell: Import-Module -Name ansible-test1; Get-AnsibleTest1 | ConvertTo-Json + register: install_min_max_actual + +- name: assert install min max version + assert: + that: + - install_min_max is changed + - install_min_max_actual.stdout | from_json == {"Name":"ansible-test1","Version":"1.0.5","Repo":"PSRepo 1"} + +- name: install min max version (idempotent) + win_psmodule: + name: ansible-test1 + minimum_version: 1.0.1 + maximum_version: 1.0.9 + state: present + register: install_min_max_again + +- name: assert install min max version (idempotent) + assert: + that: + - not install_min_max_again is changed + +- name: update package to latest version + win_psmodule: + name: ansible-test1 + state: latest + register: update_module + +- name: get result of update package to latest version + ansible.windows.win_shell: Import-Module -Name ansible-test1; Get-AnsibleTest1 | ConvertTo-Json + register: update_module_actual + +- name: assert update package to latest version + assert: + that: + - update_module is changed + - update_module_actual.stdout | from_json == {"Name":"ansible-test1","Version":"1.1.0","Repo":"PSRepo 1"} + +- name: update package to latest version (idempotent) + win_psmodule: + name: ansible-test1 + state: latest + register: update_module_again + +- name: assert update package to latest version (idempotent) + assert: + that: + - not update_module_again is changed + +- name: remove package that does not match min version + win_psmodule: + name: ansible-test1 + minimum_version: 2.0.0 + state: absent + register: remove_min_no_change + +- name: assert remove package that does not match min version + assert: + that: + - not remove_min_no_change is changed + +- name: remove package that does not match max version + win_psmodule: + name: ansible-test1 + maximum_version: 0.9.0 + state: absent + register: remove_max_no_change + +- name: assert remove package that does not match max version + assert: + that: + - not remove_max_no_change is changed + +- name: uninstall package to clear tests + win_psmodule: + name: ansible-test1 + state: absent + +- name: install package with max version + win_psmodule: + name: ansible-test2 + maximum_version: 1.0.0 + repository: PSRepo 1 + state: present + register: install_max + +- name: get result of install package with max version + ansible.windows.win_shell: Import-Module -Name ansible-test2; Get-AnsibleTest2 | ConvertTo-Json + register: install_max_actual + +- name: assert install package with max version + assert: + that: + - install_max is changed + - install_max_actual.stdout | from_json == {"Name":"ansible-test2","Version":"1.0.0","Repo":"PSRepo 1"} + +- name: fail to install updated package without skip publisher + win_psmodule: + name: ansible-test2 + required_version: 1.0.1 # This version has been pureposefully not been signed for testing + repository: PSRepo 1 + state: present + register: fail_skip_pub + failed_when: '"The version ''1.0.1'' of the module ''ansible-test2'' being installed is not catalog signed" not in fail_skip_pub.msg' + +- name: install updated package provider with skip publisher + win_psmodule: + name: ansible-test2 + required_version: 1.0.1 + repository: PSRepo 1 + state: present + skip_publisher_check: yes + register: install_skip_pub + +- name: get result of install updated package provider with skip publisher + ansible.windows.win_shell: Import-Module -Name ansible-test2; Get-AnsibleTest2 | ConvertTo-Json + register: install_skip_pub_actual + +- name: assert install updated package provider with skip publisher + assert: + that: + - install_skip_pub is changed + - install_skip_pub_actual.stdout | from_json == {"Name":"ansible-test2","Version":"1.0.1","Repo":"PSRepo 1"} + +- name: remove test package 2 for clean test + win_psmodule: + name: ansible-test2 + state: absent + +- name: fail to install clobbered module + win_psmodule: + name: ansible-clobber + state: present + register: fail_clobbering_time + failed_when: '"If you still want to install this module ''ansible-clobber'', use -AllowClobber parameter." not in fail_clobbering_time.msg' + +- name: install clobbered module + win_psmodule: + name: ansible-clobber + allow_clobber: yes + state: present + register: install_clobber + +- name: get result of install clobbered module + ansible.windows.win_shell: Import-Module -Name ansible-clobber; Enable-PSTrace | ConvertTo-Json + register: install_clobber_actual + +- name: assert install clobbered module + assert: + that: + - install_clobber is changed + - install_clobber_actual.stdout | from_json == {"Name":"ansible-clobber","Version":"0.1.0","Repo":"PSRepo 1"} + +- name: remove clobbered module + win_psmodule: + name: ansible-clobber + state: absent + register: remove_clobber + +- name: get result of remove clobbered module + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-clobber | Measure-Object).Count + register: remove_clobber_actual + +- name: assert remove clobbered module + assert: + that: + - remove_clobber is changed + - remove_clobber_actual.stdout | trim | int == 0 + +- name: fail to install prerelese module + win_psmodule: + name: ansible-test2 + repository: PSRepo 1 + required_version: 1.1.0-beta1 + state: present + register: fail_install_prerelease + failed_when: '"The ''-AllowPrerelease'' parameter must be specified when using the Prerelease string" not in fail_install_prerelease.msg' + +- name: install prerelease module + win_psmodule: + name: ansible-test2 + repository: PSRepo 1 + required_version: 1.1.0-beta1 + allow_prerelease: yes + state: present + register: install_prerelease + +- name: get result of install prerelease module + ansible.windows.win_shell: Import-Module -Name ansible-test2; Get-AnsibleTest2 | ConvertTo-Json + register: install_prerelease_actual + +- name: assert install prerelease module + assert: + that: + - install_prerelease is changed + - install_prerelease_actual.stdout | from_json == {"Name":"ansible-test2","Version":"1.1.0","Repo":"PSRepo 1"} + +- name: remove prerelease module + win_psmodule: + name: ansible-test2 + state: absent + register: remove_prerelease + +- name: get result of remove prerelease module + ansible.windows.win_shell: (Get-Module -ListAvailable -Name ansible-test2 | Measure-Object).Count + register: remove_prerelease_actual + +- name: assert remove prerelease module + assert: + that: + - remove_prerelease is changed + - remove_prerelease_actual.stdout | trim | int == 0 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule/tasks/setup.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/tasks/setup.yml new file mode 100644 index 000000000..42049a3e3 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule/tasks/setup.yml @@ -0,0 +1,119 @@ +# Sets up 2 local repos that contains mock packages for testing. +# +# PSRepo 1 contains +# ansible-test1 - 1.0.0 +# ansible-test1 - 1.0.5 +# ansible-test1 - 1.1.0 +# ansible-test2 - 1.0.0 +# ansible-test2 - 1.0.1 (Not signed for skip_publisher tests) +# ansible-test2 - 1.1.0-beta1 +# ansible-clobber - 0.1.0 +# ansible-licensed - 1.1.1 (requires license acceptance) +# PSRepo 2 contains +# ansible-test2 - 1.0.0 +# +# These modules will have the following cmdlets +# ansible-test1 +# Get-AnsibleTest1 +# +# ansible-test2 +# Get-AnsibleTest2 +# +# ansible-clobber +# Enable-PSTrace (clobbers the Enable-PSTrace cmdlet) +# +# All cmdlets return +# [PSCustomObject]@{ +# Name = "the name of the module" +# Version = "the version of the module" +# Repo = "the repo where the module was sourced from" +# } +--- +- name: create test repo folders + ansible.windows.win_file: + path: '{{ remote_tmp_dir }}\{{ item }}' + state: directory + loop: + - PSRepo 1 + - PSRepo 2 + +- name: register test repos + win_psrepository: + name: '{{ item.name }}' + source: '{{ remote_tmp_dir }}\{{ item.name }}' + installation_policy: '{{ item.policy }}' + notify: remove registered repos + loop: + - name: PSRepo 1 + policy: trusted + - name: PSRepo 2 + policy: untrusted + +- name: remove PSGallery repository + win_psrepository: + name: PSGallery + state: absent + notify: re-add PSGallery repository + +- name: create custom openssl conf + copy: + src: openssl.conf + dest: '{{ output_dir }}/openssl.conf' + delegate_to: localhost + +- name: get absolute path of output_dir for script + shell: echo {{ output_dir }} + delegate_to: localhost + register: output_dir_abs + +- name: create certificates for code signing + script: setup_certs.sh + args: + chdir: '{{ output_dir_abs.stdout }}' + delegate_to: localhost + +- name: copy the CA and sign certificates + ansible.windows.win_copy: + src: '{{ output_dir }}/{{ item }}' + dest: '{{ remote_tmp_dir }}\' + loop: + - ca.pem + - sign.pem + - sign.pfx + +- name: import the CA key to the trusted root store + ansible.windows.win_certificate_store: + path: '{{ remote_tmp_dir }}\ca.pem' + state: present + store_location: LocalMachine + store_name: Root + register: ca_cert_import + notify: remove CA cert from trusted root store + +- name: import the sign key to the trusted publisher store + ansible.windows.win_certificate_store: + path: '{{ remote_tmp_dir }}\sign.pem' + state: present + store_location: LocalMachine + store_name: TrustedPublisher + register: sign_cert_import + notify: remove signing key from trusted publisher store + +- name: copy across module template files + ansible.windows.win_copy: + src: module/ + dest: '{{ remote_tmp_dir }}' + +# Used in the script below to create the .nupkg for each test module +- name: download NuGet binary for module publishing + ansible.windows.win_get_url: + url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_psmodule/nuget.exe + dest: '{{ remote_tmp_dir }}' + register: download_res + until: download_res is successful + retries: 3 + delay: 5 + +- name: create test PowerShell modules + script: setup_modules.ps1 "{{ remote_tmp_dir }}" + notify: remove test packages diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/defaults/main.yml new file mode 100644 index 000000000..9c9c9c078 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/defaults/main.yml @@ -0,0 +1,70 @@ +--- +run_check_mode: False +suffix: "{{ '(check mode)' if run_check_mode else '' }}" +repository_name: Repo +repo_path: "{{ remote_tmp_dir }}\\repo\\" + +modules_to_install: + - AnsibleVault + - PSCSharpInvoker + - PInvokeHelper + +builtin_modules: + - Microsoft.PowerShell.Utility + - Microsoft.PowerShell.Host + - Microsoft.PowerShell.Management + +sample_modules: "{{ builtin_modules + modules_to_install }}" + +expected_fields: + - prefix + - private_data + - scripts + - author + - description + - exported_type_files + - exported_workflows + - nested_modules + - exported_dsc_resources + - updated_date + - root_module + - power_shell_host_name + - module_base + - path + - exported_format_files + - dependencies + - release_notes + - installed_date + - exported_variables + - published_date + - required_assemblies + - version + - icon_uri + - project_uri + - dot_net_framework_version + - processor_architecture + - license_uri + - required_modules + - module_type + - compatible_ps_editions + - company_name + - copyright + - tags + - access_mode + - package_management_provider + - exported_aliases + - power_shell_host_version + - clr_version + - file_list + - help_info_uri + - module_list + - exported_functions + - power_shell_version + - guid + - repository_source_location + - exported_cmdlets + - exported_commands + - repository + - installed_location + - name + - log_pipeline_execution_details diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/ansiblevault.0.3.0.nupkg b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/ansiblevault.0.3.0.nupkg Binary files differnew file mode 100644 index 000000000..52fe289ed --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/ansiblevault.0.3.0.nupkg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/pinvokehelper.0.1.0.nupkg b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/pinvokehelper.0.1.0.nupkg Binary files differnew file mode 100644 index 000000000..05d30e55c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/pinvokehelper.0.1.0.nupkg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/pscsharpinvoker.0.1.0.nupkg b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/pscsharpinvoker.0.1.0.nupkg Binary files differnew file mode 100644 index 000000000..77bd15f0e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/files/pscsharpinvoker.0.1.0.nupkg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/meta/main.yml new file mode 100644 index 000000000..acc7fbcae --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - setup_remote_tmp_dir + - setup_win_psget diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/common.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/common.yml new file mode 100644 index 000000000..290f9efbd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/common.yml @@ -0,0 +1,37 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Assert that the correct structure is returned {{ suffix }} + assert: + that: + - module_info.modules is defined + - module_info.modules is sequence() + quiet: yes + +- name: Assert that the correct number of modules are returned {{ suffix }} + assert: + that: module_info.modules | length >= expected_modules | length + fail_msg: >- + Expected {{ expected_modules | length }} modules, got {{ module_info.modules | map(attribute='name') | join(',') }} ({{ module_info.modules | length}}) + quiet: yes + +- name: Assert that all expected modules are present {{ suffix }} + assert: + that: item in (module_info.modules | map(attribute='name')) + fail_msg: "Expected module '{{ item }}' not found in results." + quiet: yes + loop: "{{ expected_modules }}" + loop_control: + label: "Assert '{{ item }}' in result." + +- include_tasks: contains_all_fields.yml + vars: + dict_to_check: "{{ item }}" + loop: "{{ + only_check_first + | default(True) + | bool + | ternary([ module_info.modules[0] ], module_info.modules) + }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/contains_all_fields.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/contains_all_fields.yml new file mode 100644 index 000000000..20750e003 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/contains_all_fields.yml @@ -0,0 +1,13 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Check for key ('{{ key }}') in result + assert: + that: key in dict_to_check + quiet: yes + fail_msg: "'{{ key }}' not found in dict." + loop_control: + loop_var: key + loop: "{{ expected_fields }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/main.yml new file mode 100644 index 000000000..21ed04a21 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/main.yml @@ -0,0 +1,49 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Reset repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + Register-PSRepository -Default + +- name: Set up directory repository + ansible.windows.win_copy: + src: "{{ role_path }}/files/" + dest: "{{ repo_path }}" + force: no + +- name: Register repository + community.windows.win_psrepository: + name: "{{ repository_name }}" + source_location: "{{ repo_path }}" + installation_policy: trusted + +- block: + - name: Install Modules + community.windows.win_psmodule: + name: "{{ item }}" + state: latest + repository: "{{ repository_name }}" + loop: "{{ modules_to_install }}" + + - name: Run Tests + import_tasks: tests.yml + + - name: Run Tests (check mode) + import_tasks: tests.yml + vars: + run_check_mode: True + + always: + - name: Remove Modules + community.windows.win_psmodule: + name: "{{ item }}" + state: absent + loop: "{{ modules_to_install }}" + + - name: Reset repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + Register-PSRepository -Default diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/tests.yml new file mode 100644 index 000000000..688c994d2 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psmodule_info/tasks/tests.yml @@ -0,0 +1,85 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Get info on a named module + vars: + expected_modules: + - "{{ builtin_modules[0] }}" + block: + - name: Get single module {{ suffix }} + community.windows.win_psmodule_info: + name: "{{ expected_modules[0] }}" + register: module_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on modules by wildcard match + vars: + wildcard: "Microsoft.PowerShell.*" + expected_modules: "{{ builtin_modules }}" + block: + - name: Get multiple modules by wildcard {{ suffix }} + community.windows.win_psmodule_info: + name: "{{ wildcard }}" + register: module_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on modules by repository + vars: + repository: "{{ repository_name }}" + expected_modules: "{{ modules_to_install }}" + block: + - name: Get multiple modules by repository {{ suffix }} + community.windows.win_psmodule_info: + repository: "{{ repository }}" + register: module_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on modules by repository and name pattern + vars: + wildcard: "*t" + repository: "{{ repository_name }}" + expected_modules: + - "AnsibleVault" + block: + - name: Get multiple modules by wildcard and repository {{ suffix }} + community.windows.win_psmodule_info: + name: "{{ wildcard }}" + repository: "{{ repository }}" + register: module_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on all modules + vars: + expected_modules: "{{ sample_modules }}" + block: + - name: Get all modules {{ suffix }} + community.windows.win_psmodule_info: + register: module_info + + - include_tasks: common.yml + + # one last time checking all the fields + - include_tasks: common.yml + vars: + only_check_first: False + + # block + check_mode: "{{ run_check_mode }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/aliases new file mode 100644 index 000000000..0d6b4f220 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/aliases @@ -0,0 +1,2 @@ +shippable/windows/group4 +needs/httptester diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/defaults/main.yml new file mode 100644 index 000000000..55e53a535 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/defaults/main.yml @@ -0,0 +1,25 @@ +--- +repository_name: My Get +repository_source_location: https://www.nuget.org/api/v2 +repository_publish_location: '{{ repository_source_location }}/fake/publish' +repository_script_source_location: '{{ repository_source_location }}/fake/script' +repository_script_publish_location: '{{ repository_source_location }}/fake/script/publish' + +repository_source_location2: '{{ remote_tmp_dir }}' + +redirect_url: http://{{ httpbin_host }}/redirect +redirect_expected_target: http://{{ httpbin_host }}/get + +redirect_publish_url: http://{{ httpbin_host }}/redirect-to?url=http%3A%2F%2F{{ httpbin_host }}%2Fcache&status_code=307 +redirect_publish_expected_target: http://{{ httpbin_host }}/cache + +redirect_script_source_url: http://{{ httpbin_host }}/redirect-to?url=http%3A%2F%2F{{ httpbin_host }}%2Fxml&status_code=301 +redirect_script_source_expected_target: http://{{ httpbin_host }}/xml + +redirect_script_publish_url: http://{{ httpbin_host }}/redirect-to?url=http%3A%2F%2F{{ httpbin_host }}%2Fjson&status_code=302 +redirect_script_publish_expected_target: http://{{ httpbin_host }}/json + +repository_source_location_alt: http://{{ httpbin_host }}/response-headers +repository_publish_location_alt: http://{{ httpbin_host }}/gzip +repository_script_source_location_alt: http://{{ httpbin_host }}/html +repository_script_publish_location_alt: http://{{ httpbin_host }}/deflate diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/meta/main.yml new file mode 100644 index 000000000..9e7a71888 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: +- setup_http_tests +- setup_remote_tmp_dir +- setup_win_psget diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/get_repo_info.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/get_repo_info.yml new file mode 100644 index 000000000..999febcc6 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/get_repo_info.yml @@ -0,0 +1,17 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Retrieve Repository Manually + ansible.windows.win_shell: | + $repo = Get-PSRepository -Name {{ repository_name | quote }} -ErrorAction SilentlyContinue + @{ + repo = $repo + exists = $repo -as [bool] + } | ConvertTo-Json -Depth 5 + register: _retrieve_repo_result + +- name: Set Repo Response Variable + set_fact: + "{{ repo_result_var | default('repo_result') }}": "{{ _retrieve_repo_result.stdout | from_json }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/main.yml new file mode 100644 index 000000000..0afdfbd2a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/main.yml @@ -0,0 +1,18 @@ +# This file is part of Ansible + +# Copyright: (c) 2018, Wojciech Sciesinski <wojciech[at]sciesinski[dot]net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- +- name: unregister the repository + ansible.windows.win_shell: Unregister-PSRepository {{ repository_name | quote }} -ErrorAction SilentlyContinue + +- block: + - name: run all tests + include_tasks: tests.yml + + - name: run update and force behavior tests + include_tasks: update_and_force.yml + always: + - name: ensure test repo is unregistered + ansible.windows.win_shell: Unregister-PSRepository {{ repository_name | quote }} -ErrorAction SilentlyContinue diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/tests.yml new file mode 100644 index 000000000..0fa63d9d0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/tests.yml @@ -0,0 +1,200 @@ +# This file is part of Ansible + +# Copyright: (c) 2018, Wojciech Sciesinski <wojciech[at]sciesinski[dot]net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +--- + +- name: check adding of repository defaults - check mode + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location }}" + state: present + check_mode: True + register: adding_repository_check + +- name: get result of adding repository defaults - check mode + ansible.windows.win_shell: (Get-PSRepository -Name {{ repository_name | quote }} -ErrorAction SilentlyContinue | Measure-Object).Count + changed_when: false + register: result_adding_repository_check + +- name: test adding repository defaults - check mode + assert: + that: + - adding_repository_check is changed + - result_adding_repository_check.stdout_lines[0] == '0' + +- name: check adding repository defaults + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location }}" + state: present + register: adding_repository + +- name: get result of adding repository defaults + ansible.windows.win_shell: | + $repo = Get-PSRepository -Name {{ repository_name | quote }} + ($repo | Measure-Object).Count + $repo.SourceLocation + $repo.InstallationPolicy + register: result_adding_repository + +- name: test adding repository defaults + assert: + that: + - adding_repository is changed + - result_adding_repository.stdout_lines[0] == '1' + - result_adding_repository.stdout_lines[1] == repository_source_location + - result_adding_repository.stdout_lines[2] == 'Trusted' + +- name: check adding repository defaults - idempotent + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location }}" + state: present + register: adding_repository_again + +- name: test check adding repository defaults - idempotent + assert: + that: + - adding_repository_again is not changed + +- name: change InstallationPolicy - check mode + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location }}" + installation_policy: untrusted + check_mode: True + register: change_installation_policy_check + +- name: get result of change InstallationPolicy - check mode + ansible.windows.win_shell: '(Get-PSRepository -Name {{ repository_name | quote }}).InstallationPolicy' + changed_when: false + register: result_change_installation_policy_check + +- name: test change InstallationPolicy - check mode + assert: + that: + - change_installation_policy_check is changed + - result_change_installation_policy_check.stdout | trim == 'Trusted' + +- name: change InstallationPolicy + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location }}" + installation_policy: untrusted + register: change_installation_policy + +- name: get result of change InstallationPolicy + ansible.windows.win_shell: '(Get-PSRepository -Name {{ repository_name | quote }}).InstallationPolicy' + changed_when: false + register: result_change_installation_policy + +- name: test change InstallationPolicy + assert: + that: + - change_installation_policy is changed + - result_change_installation_policy.stdout | trim == 'Untrusted' + +- name: change InstallationPolicy - idempotent + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location }}" + installation_policy: untrusted + register: change_installation_policy_again + +- name: test change InstallationPolicy - idempotent + assert: + that: + - change_installation_policy_again is not changed + +- name: change source - check mode + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location2 }}" + state: present + check_mode: True + register: change_source_check + +- name: get result of change source - check mode + ansible.windows.win_shell: | + $repo = Get-PSRepository -Name {{ repository_name | quote }} + $repo.SourceLocation + $repo.InstallationPolicy + changed_when: False + register: result_change_source_check + +- name: test change source - check mode + assert: + that: + - change_source_check is changed + - result_change_source_check.stdout_lines[0] == repository_source_location + - result_change_source_check.stdout_lines[1] == 'Untrusted' + +- name: change source + win_psrepository: + name: "{{ repository_name }}" + source: "{{ repository_source_location2 }}" + state: present + register: change_source + +- name: get result of change source + ansible.windows.win_shell: | + $repo = Get-PSRepository -Name {{ repository_name | quote }} + $repo.SourceLocation + $repo.InstallationPolicy + changed_when: False + register: result_change_source + +- name: test change source + assert: + that: + - change_source is changed + - result_change_source.stdout_lines[0] == repository_source_location2 + - result_change_source.stdout_lines[1] == 'Untrusted' + +- name: remove repository - check mode + win_psrepository: + name: "{{ repository_name }}" + state: absent + check_mode: True + register: removing_repository_check + +- name: get result of remove repository - check mode + ansible.windows.win_shell: '(Get-PSRepository -Name {{ repository_name | quote }} -ErrorAction SilentlyContinue | Measure-Object).Count' + changed_when: false + register: result_removing_repository_check + +- name: test remove repository - check mode + assert: + that: + - removing_repository_check is changed + - result_removing_repository_check.stdout | trim == '1' + +- name: remove repository + win_psrepository: + name: "{{ repository_name }}" + state: absent + register: removing_repository + +- name: get result of remove repository + ansible.windows.win_shell: '(Get-PSRepository -Name {{ repository_name | quote }} -ErrorAction SilentlyContinue | Measure-Object).Count' + changed_when: false + register: result_removing_repository + +- name: test remove repository + assert: + that: + - removing_repository is changed + - result_removing_repository.stdout | trim == '0' + +- name: remove repository - idempotent + win_psrepository: + name: "{{ repository_name }}" + state: absent + register: remove_repository_again + +- name: test remove repository - idempotent + assert: + that: + - remove_repository_again is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/update_and_force.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/update_and_force.yml new file mode 100644 index 000000000..ca78770c7 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository/tasks/update_and_force.yml @@ -0,0 +1,368 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Tests for creating a repository with all locations set + block: + - name: Add a repository with all locations set (check mode) + win_psrepository: + name: "{{ repository_name }}" + source_location: "{{ repository_source_location }}" + publish_location: "{{ repository_publish_location }}" + script_source_location: "{{ repository_script_source_location }}" + script_publish_location: "{{ repository_script_publish_location }}" + register: all_register_check + check_mode: True + + - name: Assert that task is changed (check mode) + assert: + that: + - all_register_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that the repository was not created (check mode) + assert: + that: + - not repo_result.exists + + - name: Add a repository with all locations set + win_psrepository: + name: "{{ repository_name }}" + source_location: "{{ repository_source_location }}" + publish_location: "{{ repository_publish_location }}" + script_source_location: "{{ repository_script_source_location }}" + script_publish_location: "{{ repository_script_publish_location }}" + register: all_register_check + + - name: Assert that task is changed + assert: + that: + - all_register_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that the repository was created with expected locations + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location + - repo_result.repo.PublishLocation == repository_publish_location + - repo_result.repo.ScriptSourceLocation == repository_script_source_location + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location + + - name: Add a repository with all locations set again (check mode) + win_psrepository: + name: "{{ repository_name }}" + source_location: "{{ repository_source_location }}" + publish_location: "{{ repository_publish_location }}" + script_source_location: "{{ repository_script_source_location }}" + script_publish_location: "{{ repository_script_publish_location }}" + register: all_register_check + check_mode: True + + - name: Assert that task is not changed + assert: + that: + - all_register_check is not changed + +- name: Tests for upating individual locations without changing existing ones + block: + - name: Update source location + block: + - name: Update source_location only (check mode) + win_psrepository: + name: "{{ repository_name }}" + source_location: "{{ repository_source_location_alt }}" + register: single_update_check + check_mode: True + + - name: Assert that task is changed (check mode) + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that source location was not updated (check mode) + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation != repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location + - repo_result.repo.ScriptSourceLocation == repository_script_source_location + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location + + - name: Update source_location only + win_psrepository: + name: "{{ repository_name }}" + source_location: "{{ repository_source_location_alt }}" + register: single_update_check + + - name: Assert that task is changed + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that source location was updated + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location + - repo_result.repo.ScriptSourceLocation == repository_script_source_location + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location + + - name: Update publish location + block: + - name: Update publish_location only (check mode) + win_psrepository: + name: "{{ repository_name }}" + publish_location: "{{ repository_publish_location_alt }}" + register: single_update_check + check_mode: True + + - name: Assert that task is changed (check mode) + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that publish location was not updated (check mode) + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation != repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location + + - name: Update publish_location only + win_psrepository: + name: "{{ repository_name }}" + publish_location: "{{ repository_publish_location_alt }}" + register: single_update_check + + - name: Assert that task is changed + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that publish location was updated + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location + + - name: Update script source location + block: + - name: Update script_source_location only (check mode) + win_psrepository: + name: "{{ repository_name }}" + script_source_location: "{{ repository_script_source_location_alt }}" + register: single_update_check + check_mode: True + + - name: Assert that task is changed (check mode) + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that script source location was not updated (check mode) + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation != repository_script_source_location_alt + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location + + - name: Update script_source_location only + win_psrepository: + name: "{{ repository_name }}" + script_source_location: "{{ repository_script_source_location_alt }}" + register: single_update_check + + - name: Assert that task is changed + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that script source location was updated + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location_alt + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location + + - name: Update script publish location + block: + - name: Update script_publish_location only (check mode) + win_psrepository: + name: "{{ repository_name }}" + script_publish_location: "{{ repository_script_publish_location_alt }}" + register: single_update_check + check_mode: True + + - name: Assert that task is changed (check mode) + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that script publish location was not updated (check mode) + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location_alt + - repo_result.repo.ScriptPublishLocation != repository_script_publish_location_alt + + - name: Update script_publish_location only + win_psrepository: + name: "{{ repository_name }}" + script_publish_location: "{{ repository_script_publish_location_alt }}" + register: single_update_check + + - name: Assert that task is changed + assert: + that: + - single_update_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that script publish location was updated + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location_alt + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location_alt + +- name: Tests for using force + block: + - name: Ensure repository is not unnecessarily re-registered (check mode) + win_psrepository: + force: True + name: "{{ repository_name }}" + source_location: "{{ repository_source_location_alt }}" + publish_location: "{{ repository_publish_location_alt }}" + script_source_location: "{{ repository_script_source_location_alt }}" + script_publish_location: "{{ repository_script_publish_location_alt }}" + register: force_check + check_mode: True + + - name: Assert that task is not changed (check mode) + assert: + that: + - force_check is not changed + + - import_tasks: get_repo_info.yml + + - name: Assert that repository settings remain identical (check mode) + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location_alt + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location_alt + + - name: Ensure repository is not unnecessarily re-registered + win_psrepository: + force: True + name: "{{ repository_name }}" + source_location: "{{ repository_source_location_alt }}" + publish_location: "{{ repository_publish_location_alt }}" + script_source_location: "{{ repository_script_source_location_alt }}" + script_publish_location: "{{ repository_script_publish_location_alt }}" + register: force_check + + - name: Assert that task is not changed + assert: + that: + - force_check is not changed + + - import_tasks: get_repo_info.yml + + - name: Assert that repository settings remain identical + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location_alt + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location_alt + + - name: Set only two locations (check mode) + win_psrepository: + force: True + name: "{{ repository_name }}" + source_location: "{{ repository_source_location }}" + script_source_location: "{{ repository_script_source_location }}" + register: force_check + check_mode: True + + - name: Assert that task is changed (check mode) + assert: + that: + - force_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that repository settings remain identical (check mode) + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location_alt + - repo_result.repo.PublishLocation == repository_publish_location_alt + - repo_result.repo.ScriptSourceLocation == repository_script_source_location_alt + - repo_result.repo.ScriptPublishLocation == repository_script_publish_location_alt + + - name: Set only two locations + win_psrepository: + force: True + name: "{{ repository_name }}" + source_location: "{{ repository_source_location }}" + script_source_location: "{{ repository_script_source_location }}" + register: force_check + + - name: Assert that task is changed + assert: + that: + - force_check is changed + + - import_tasks: get_repo_info.yml + + - name: Assert that repository settings were updated + assert: + that: + - repo_result.exists + - repo_result.repo.SourceLocation == repository_source_location + - repo_result.repo.ScriptSourceLocation == repository_script_source_location + # these won't be blank because PowerShellGet tries to discover publish locations based on sources + - repo_result.repo.PublishLocation != repository_publish_location_alt + - repo_result.repo.ScriptPublishLocation != repository_script_publish_location_alt + +- name: remove repository (update and force tests) + win_psrepository: + name: "{{ repository_name }}" + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/defaults/main.yml new file mode 100644 index 000000000..3cfe90d68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/defaults/main.yml @@ -0,0 +1,12 @@ +--- +user_password: 'gEYB#f74MslYcz&*1!*2WDM65!i&4*H' +test_users: + - name: repo_copy1 + - name: repo_copy2 + profile: '{{ remote_tmp_dir }}\odd_dir' + - name: copy_repo3 + +test_repos: + - PSGallery + - FakeGet + - FakeFileServer diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/files/SampleRepositories.xml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/files/SampleRepositories.xml new file mode 100644 index 000000000..162c3ea8f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/files/SampleRepositories.xml @@ -0,0 +1,94 @@ +<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"> + <Obj RefId="0"> + <TN RefId="0"> + <T>System.Collections.Hashtable</T> + <T>System.Object</T> + </TN> + <DCT> + <En> + <S N="Key">FakeFileServer</S> + <Obj N="Value" RefId="1"> + <TN RefId="1"> + <T>Microsoft.PowerShell.Commands.PSRepository</T> + <T>System.Management.Automation.PSCustomObject</T> + <T>System.Object</T> + </TN> + <MS> + <S N="Name">FakeFileServer</S> + <S N="SourceLocation">\\domain\share\repo\</S> + <S N="PublishLocation">\\domain\share\repo\</S> + <S N="ScriptSourceLocation">\\domain\share\repo\</S> + <S N="ScriptPublishLocation">\\domain\share\repo\</S> + <B N="Trusted">false</B> + <B N="Registered">true</B> + <S N="InstallationPolicy">Untrusted</S> + <S N="PackageManagementProvider">NuGet</S> + <Obj N="ProviderOptions" RefId="2"> + <TNRef RefId="0" /> + <DCT /> + </Obj> + </MS> + </Obj> + </En> + <En> + <S N="Key">PSGallery</S> + <Obj N="Value" RefId="3"> + <TN RefId="2"> + <T>Deserialized.Microsoft.PowerShell.Commands.PSRepository</T> + <T>Deserialized.System.Management.Automation.PSCustomObject</T> + <T>Deserialized.System.Object</T> + </TN> + <MS> + <S N="Name">PSGallery</S> + <S N="SourceLocation">https://www.powershellgallery.com/api/v2</S> + <S N="PublishLocation">https://www.powershellgallery.com/api/v2/package/</S> + <S N="ScriptSourceLocation">https://www.powershellgallery.com/api/v2/items/psscript</S> + <S N="ScriptPublishLocation">https://www.powershellgallery.com/api/v2/package/</S> + <Obj N="Trusted" RefId="4"> + <TN RefId="3"> + <T>System.Management.Automation.SwitchParameter</T> + <T>System.ValueType</T> + <T>System.Object</T> + </TN> + <ToString>False</ToString> + <Props> + <B N="IsPresent">false</B> + </Props> + </Obj> + <B N="Registered">true</B> + <S N="InstallationPolicy">Untrusted</S> + <S N="PackageManagementProvider">NuGet</S> + <Obj N="ProviderOptions" RefId="5"> + <TN RefId="4"> + <T>Deserialized.System.Collections.Hashtable</T> + <T>Deserialized.System.Object</T> + </TN> + <DCT /> + </Obj> + </MS> + </Obj> + </En> + <En> + <S N="Key">FakeGet</S> + <Obj N="Value" RefId="6"> + <TNRef RefId="2" /> + <MS> + <S N="Name">FakeGet</S> + <S N="SourceLocation">https://www.example.org/api/nuget/v2</S> + <S N="PublishLocation">https://www.example.org/api/nuget/v2/package/</S> + <S N="ScriptSourceLocation">https://www.exampe.org/api/nuget/v2/items/psscript</S> + <S N="ScriptPublishLocation">https://www.example.org/api/nuget/v2/package/</S> + <B N="Trusted">true</B> + <B N="Registered">true</B> + <S N="InstallationPolicy">Trusted</S> + <S N="PackageManagementProvider">NuGet</S> + <Obj N="ProviderOptions" RefId="7"> + <TNRef RefId="4" /> + <DCT /> + </Obj> + </MS> + </Obj> + </En> + </DCT> + </Obj> +</Objs> diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/meta/main.yml new file mode 100644 index 000000000..acc7fbcae --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - setup_remote_tmp_dir + - setup_win_psget diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/main.yml new file mode 100644 index 000000000..80d6039ea --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/main.yml @@ -0,0 +1,95 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Reset + import_tasks: reset.yml + +- name: Main block + module_defaults: + community.windows.win_psrepository_copy: + source: '{{ remote_tmp_dir }}\SampleRepositories.xml' + block: + # avoiding the use of win_psrepository / Register-PSRepository due to its strict target checking. + # in the end, this module always looks at the XML file, so we'll just put a pre-baked one in place. + - name: Put our source file in place + ansible.windows.win_copy: + src: SampleRepositories.xml + dest: "{{ remote_tmp_dir }}" + force: yes + + - name: Copy repos with module defaults - check + community.windows.win_psrepository_copy: + register: status + check_mode: yes + + - assert: + that: status is changed + + - name: Copy repos with module defaults + community.windows.win_psrepository_copy: + register: status + + - assert: + that: status is changed + + - name: Copy repos with module defaults - again + community.windows.win_psrepository_copy: + register: status + + - assert: + that: status is not changed + + # these users should inherit the repositories via the Default profile + - name: Create test users + ansible.windows.win_user: + name: "{{ item.name }}" + profile: "{{ item.profile | default(omit) }}" + password: "{{ user_password }}" + groups: + - Administrators + loop: "{{ test_users }}" + + ##################################### + ## Begin inherited tests + + - name: Test inherited repos via Default profile + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: "{{ test_repos }}" + loop: "{{ test_users | map(attribute='name') | list }}" + + ## End inherited tests + ##################################### + + - import_tasks: test_system_users.yml + + - import_tasks: test_exclude_profile.yml + + - import_tasks: test_include_profile.yml + + - import_tasks: test_exclude_repo.yml + + - import_tasks: test_include_repo.yml + + always: + - name: Reset + import_tasks: reset.yml + + - name: Remove test users + ansible.windows.win_user: + name: "{{ item.name }}" + state: absent + loop: "{{ test_users }}" + + - name: Remove test profiles + import_tasks: remove_test_profiles.yml + + - name: Remove sample file + ansible.windows.win_file: + path: '{{ remote_tmp_dir }}\SampleRepositories.xml' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/remove_test_profiles.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/remove_test_profiles.yml new file mode 100644 index 000000000..ab15d40fb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/remove_test_profiles.yml @@ -0,0 +1,42 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +# removing a user doesn't remove their profile (which includes the filesystem directory and registry key). +# without cleaning up, every CI run will create a new path and key and they will build up over time. +# this finds all profiles on the system, via the registry, whose path basename starts with one of our test users +# and then deletes the directory; if that was succesful, it deletes the registry key too. +# +# As a result this should also clean up after any old runs that left behind profiles, so an interrupted run +# that didn't run the rescue block will still get cleaned up by the next when this runs again. + +- name: Remove all the test user profiles from the system + become: yes + become_user: SYSTEM + become_method: runas + ansible.windows.win_shell: | + $names = @' + {{ test_users | map(attribute='name') | list | to_json }} + '@ | ConvertFrom-Json + $regPL = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' + $profiles = Get-ChildItem -LiteralPath $regPL + + $profiles | ForEach-Object -Process { + $path = ($_ | Get-ItemProperty | Select-Object -ExpandProperty ProfileImagePath) -as [System.IO.DirectoryInfo] + :search foreach ($target in $names) { + if ($path.Name -match "^$target") { + $delReg=$true + if ($path.Exists) { + & cmd.exe /c rd /s /q $path.FullName + if (-not ($delReg=$?)) { + Write-Warning -Message "Couldn't remove '$path'" + } + } + if ($delReg) { + $_ | Remove-Item -Force -ErrorAction SilentlyContinue + } + break search + } + } + } diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/reset.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/reset.yml new file mode 100644 index 000000000..d985c2950 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/reset.yml @@ -0,0 +1,22 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Remove the psrepositories for all profiles on the system + become: yes + become_user: SYSTEM + become_method: runas + ansible.windows.win_shell: | + $regPL = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' + $default = Get-ItemProperty -LiteralPath $regPL | Select-Object -ExpandProperty Default + $profiles = ( + @($default) + + (Get-ChildItem -LiteralPath $regPL | Get-ItemProperty | Select-Object -ExpandProperty ProfileImagePath) + ) -as [System.IO.DirectoryInfo[]] + $profiles | + Where-Object -Property Exists -EQ $true | + ForEach-Object -Process { + $p = [System.IO.Path]::Combine($_.FullName, 'AppData\Local\Microsoft\Windows\PowerShell\PowerShellGet\PSRepositories.xml') + Remove-Item -LiteralPath $p -Force -ErrorAction SilentlyContinue + } diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_by_user.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_by_user.yml new file mode 100644 index 000000000..f5f76d20b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_by_user.yml @@ -0,0 +1,32 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Get repository info + become: yes + become_user: "{{ user }}" + become_method: runas + community.windows.win_psrepository_info: + register: repos + +- name: Ensure expected repositories are present + assert: + that: repo in (repos.repositories | map(attribute='name') | list) + success_msg: "expected repository '{{ repo }}' was found for user '{{ user }}'" + fail_msg: "expected repository '{{ repo }}' was not found for user '{{ user }}'" + loop: "{{ expected_repos }}" + loop_control: + loop_var: repo + +- name: Ensure present repositories are expected + assert: + that: repo in expected_repos or repo == 'PSGallery' + # although not completely consistent depending on OS and/or PowerShellGet versions + # often calling Get-PSRepository will register PSGallery if it's missing, so we + # must be prepared for it to show up here "unexpectedly" by way of us querying :( + success_msg: "found expected repository '{{ repo }}' for user '{{ user }}'" + fail_msg: "found unexpected repository '{{ repo }}' for user '{{ user }}'" + loop: "{{ repos.repositories | map(attribute='name') | list }}" + loop_control: + loop_var: repo diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_exclude_profile.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_exclude_profile.yml new file mode 100644 index 000000000..19da14e1d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_exclude_profile.yml @@ -0,0 +1,58 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +##################################### +## Begin filter profile (exclusive) tests +- name: Reset + import_tasks: reset.yml + +- name: Copy repos with excluded profiles - check + community.windows.win_psrepository_copy: + exclude_profiles: 'repo*' + register: status + check_mode: yes + +- assert: + that: status is changed + +- name: Copy repos with excluded profiles + community.windows.win_psrepository_copy: + exclude_profiles: 'repo*' + register: status + +- assert: + that: status is changed + +- name: Copy repos with excluded profiles - again + community.windows.win_psrepository_copy: + exclude_profiles: 'repo*' + register: status + +- assert: + that: status is not changed + +- name: Test filtered profiles (excluded) + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: [] + loop: + - repo_copy1 + - repo_copy2 + +- name: Test filtered profiles (not excluded) + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: "{{ test_repos }}" + loop: + - copy_repo3 + +## End filter profile (exclusive) tests +##################################### diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_exclude_repo.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_exclude_repo.yml new file mode 100644 index 000000000..c85190243 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_exclude_repo.yml @@ -0,0 +1,49 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +##################################### +## Begin filter repo (exclusive) tests + +- name: Reset + import_tasks: reset.yml + +- name: Copy filtered repos by exclusion - check + community.windows.win_psrepository_copy: + exclude: '*Server' + register: status + check_mode: yes + +- assert: + that: status is changed + +- name: Copy filtered repos by exclusion + community.windows.win_psrepository_copy: + exclude: '*Server' + register: status + +- assert: + that: status is changed + +- name: Copy filtered repos by exclusion - again + community.windows.win_psrepository_copy: + exclude: '*Server' + register: status + +- assert: + that: status is not changed + +- name: Test filtered repos (included) + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: + - PSGallery + - FakeGet + loop: "{{ test_users | map(attribute='name') | list }}" + +## End filter repo (exclusive) tests +##################################### diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_include_profile.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_include_profile.yml new file mode 100644 index 000000000..6e3d47b97 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_include_profile.yml @@ -0,0 +1,59 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +##################################### +## Begin filter profile (inclusive) tests + +- name: Reset + import_tasks: reset.yml + +- name: Copy repos with filtered profiles - check + community.windows.win_psrepository_copy: + profiles: 'repo*' + register: status + check_mode: yes + +- assert: + that: status is changed + +- name: Copy repos with filtered profiles + community.windows.win_psrepository_copy: + profiles: 'repo*' + register: status + +- assert: + that: status is changed + +- name: Copy repos with filtered profiles - again + community.windows.win_psrepository_copy: + profiles: 'repo*' + register: status + +- assert: + that: status is not changed + +- name: Test filtered profiles (included) + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: "{{ test_repos }}" + loop: + - repo_copy1 + - repo_copy2 + +- name: Test filtered profiles (not included) + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: [] + loop: + - copy_repo3 + +## End filter profile (inclusive) tests +##################################### diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_include_repo.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_include_repo.yml new file mode 100644 index 000000000..3bc392931 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_include_repo.yml @@ -0,0 +1,49 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +##################################### +## Begin filter repo (inclusive) tests + +- name: Reset + import_tasks: reset.yml + +- name: Copy filtered repos - check + community.windows.win_psrepository_copy: + name: '*G*' + register: status + check_mode: yes + +- assert: + that: status is changed + +- name: Copy filtered repos + community.windows.win_psrepository_copy: + name: '*G*' + register: status + +- assert: + that: status is changed + +- name: Copy filtered repos - again + community.windows.win_psrepository_copy: + name: '*G*' + register: status + +- assert: + that: status is not changed + +- name: Test filtered repos (included) + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: + - PSGallery + - FakeGet + loop: "{{ test_users | map(attribute='name') | list }}" + +## End filter repo (inclusive) tests +##################################### diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_system_users.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_system_users.yml new file mode 100644 index 000000000..446ad4d1c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_copy/tasks/test_system_users.yml @@ -0,0 +1,68 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +##################################### +## Begin system user tests + +- name: Reset + import_tasks: reset.yml + +- name: Copy repos only to special system users - check + community.windows.win_psrepository_copy: + profiles: + - '*Service' + - 'systemprofile' + exclude_profiles: [] + register: status + check_mode: yes + +- assert: + that: status is changed + +- name: Copy repos only to special system users + community.windows.win_psrepository_copy: + profiles: + - '*Service' + - 'systemprofile' + exclude_profiles: [] + register: status + +- assert: + that: status is changed + +- name: Copy repos only to special system users - again + community.windows.win_psrepository_copy: + profiles: + - '*Service' + - 'systemprofile' + exclude_profiles: [] + register: status + +- assert: + that: status is not changed + +- name: Test system users + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ 'SYSTEM' if item == 'systemprofile' else item }}" + expected_repos: "{{ test_repos }}" + loop: + - systemprofile + - LocalService + - NetworkService + +- name: Test other users + include_tasks: + file: test_by_user.yml + apply: + vars: + user: "{{ item }}" + expected_repos: [] + loop: "{{ test_users | map(attribute='name') | list }}" + +## End system user tests +##################################### diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/aliases new file mode 100644 index 000000000..748bc3ed5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/aliases @@ -0,0 +1,3 @@ +shippable/windows/group3 +needs/httptester +unstable diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/defaults/main.yml new file mode 100644 index 000000000..13dac5dee --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/defaults/main.yml @@ -0,0 +1,10 @@ +--- +run_check_mode: False +suffix: "{{ '(check mode)' if run_check_mode else '' }}" +default_repository_name: PSGallery + +second_repository_name: PowerShellGetDemo +second_repository_source_location: https://www.nuget.org/api/v2 + +third_repository_name: OtherRepo +third_repository_source_location: http://{{ httpbin_host }}/get diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/meta/main.yml new file mode 100644 index 000000000..f050654c7 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - setup_http_tests + - setup_win_psget diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/contains_all_fields.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/contains_all_fields.yml new file mode 100644 index 000000000..6f3e6f500 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/contains_all_fields.yml @@ -0,0 +1,21 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Check for key ('{{ key }}') in result + assert: + that: key in dict_to_check + loop_control: + loop_var: key + loop: + - name + - installation_policy + - package_management_provider + - provider_options + - publish_location + - source_location + - script_source_location + - script_publish_location + - registered + - trusted diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/empty.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/empty.yml new file mode 100644 index 000000000..9bc6d4c64 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/empty.yml @@ -0,0 +1,19 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Tests against the empty set of repositories + block: + - name: Get repository info {{ suffix }} + win_psrepository_info: + register: repo_info + + - name: Assert that the correct structure is returned {{ suffix }} + assert: + that: + - repo_info.repositories is defined + - repo_info.repositories is sequence() + - repo_info.repositories | length == 0 + # block + check_mode: "{{ run_check_mode }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/main.yml new file mode 100644 index 000000000..72427e211 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/main.yml @@ -0,0 +1,51 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Unregister all repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + +- block: + - name: Run Empty Tests + import_tasks: empty.yml + + - name: Run Empty Tests (check mode) + import_tasks: empty.yml + vars: + run_check_mode: True + + - name: Add the default repository + ansible.windows.win_shell: | + Register-PSRepository -Default + + - name: Single Repository Tests + import_tasks: single.yml + + - name: Single Repository Tests (check mode) + import_tasks: single.yml + vars: + run_check_mode: True + + - name: Add two more repositories + ansible.windows.win_shell: | + Register-PSRepository -Name '{{ second_repository_name }}' -SourceLocation '{{ second_repository_source_location }}' + Register-PSRepository -Name '{{ third_repository_name }}' -SourceLocation '{{ third_repository_source_location }}' + + - name: Multi Repository Tests + import_tasks: multiple.yml + + - name: Multi Repository Tests (check mode) + import_tasks: multiple.yml + vars: + run_check_mode: True + + always: + - name: Unregister all repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + + - name: Ensure only the default repository remains + ansible.windows.win_shell: | + Register-PSRepository -Default diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/multiple.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/multiple.yml new file mode 100644 index 000000000..8c5b986e7 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/multiple.yml @@ -0,0 +1,37 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Tests against mutiple repositories + block: + - name: Get all repository info {{ suffix }} + win_psrepository_info: + register: repo_info + + - name: Assert that the correct structure is returned {{ suffix }} + assert: + that: + - repo_info.repositories is defined + - repo_info.repositories is sequence() + - repo_info.repositories | length == 3 + + - include_tasks: contains_all_fields.yml + vars: + dict_to_check: "{{ item }}" + loop: "{{ repo_info.repositories }}" + + - name: Get two repositories with a filter {{ suffix }} + win_psrepository_info: + name: P* + register: repo_info + + - name: Assert that the correct two repositories were returned {{ suffix }} + assert: + that: + - repo_info.repositories | length == 2 + - default_repository_name in (repo_info.repositories | map(attribute='name') | list) + - second_repository_name in (repo_info.repositories | map(attribute='name') | list) + + # block + check_mode: "{{ run_check_mode }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/single.yml b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/single.yml new file mode 100644 index 000000000..00071ec55 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psrepository_info/tasks/single.yml @@ -0,0 +1,26 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Tests against a single repository ({{ default_repository_name }}) + block: + - name: Get repository info {{ suffix }} + win_psrepository_info: + name: "{{ default_repository_name }}" + register: repo_info + + - name: Assert that the correct structure is returned {{ suffix }} + assert: + that: + - repo_info.repositories is defined + - repo_info.repositories is sequence() + - repo_info.repositories | length == 1 + - repo_info.repositories[0].name == default_repository_name + + - include_tasks: contains_all_fields.yml + vars: + dict_to_check: "{{ repo_info.repositories[0] }}" + + # block + check_mode: "{{ run_check_mode }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psscript/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript/defaults/main.yml new file mode 100644 index 000000000..010348236 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/defaults/main.yml @@ -0,0 +1,26 @@ +--- +repos: + - name: Repo1 + path: '{{ remote_tmp_dir }}\Repo1' + policy: trusted + scripts: + - Test-One + - Test-Multi + + - name: Repo2 + path: '{{ remote_tmp_dir }}\Repo2' + policy: untrusted + scripts: + - Test-Two + - Test-Multi + +versions: + - 1.0.0 + - 1.0.9 + - 1.0.10 + - 10.0.0 + - 10.0.9 + - 10.0.10 + +earliest_version: 1.0.0 +latest_version: 10.0.10 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/handlers/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript/handlers/main.yml new file mode 100644 index 000000000..83c4a490c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/handlers/main.yml @@ -0,0 +1,22 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Clear Repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + +- name: Add PSGallery + ansible.windows.win_shell: | + Register-PSRepository -Default + +- name: Clear Installed Scripts + ansible.windows.win_shell: | + Get-InstalledScript | Uninstall-Script -Force + +- name: Remove Directories + ansible.windows.win_file: + path: '{{ item.path }}' + state: absent + loop: "{{ repos }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript/meta/main.yml new file mode 100644 index 000000000..acc7fbcae --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - setup_remote_tmp_dir + - setup_win_psget diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/main.yml new file mode 100644 index 000000000..97779ae0f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/main.yml @@ -0,0 +1,13 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Setup local repositories + import_tasks: setup_repos.yml + tags: + - always + - setup + +- name: Run Tests + import_tasks: tests.yml
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/script_info.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/script_info.yml new file mode 100644 index 000000000..d16993460 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/script_info.yml @@ -0,0 +1,81 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Get installed scripts + ansible.windows.win_shell: | + $pMachine = "$env:ProgramFiles\WindowsPowerShell\Scripts" + # 2012/2012R2 test environments seem to have blank values for some environment vars + $userStub = 'Documents\WindowsPowerShell\Scripts' + $userBase = if ($Home) { + $Home + } + elseif ($env:UserProfile) { + $env:UserProfile + } + elseif ($env:HomeDrive -and $env:HomePath) { + "${env:HomeDrive}${env:HomePath}\$userStub" + } + elseif ($env:UserName) { + $root = if ($env:SystemDrive) { + $env:SystemDrive + } + elseif ($emv:HomeDrive) { + $env:HomeDrive + } + else { + 'C:' + } + "${root}\Users\${env:UserName}" + } + $pUser = "${userBase}\${userStub}" + + $scripts = Get-InstalledScript | + Select-Object Name,Version,InstalledLocation,Repository,@{ + Name = 'current_user' + Expression = { $_.InstalledLocation -eq $pUser } + }, + @{ + Name = 'all_users' + Expression = { $_.InstalledLocation -eq $pMachine } + }, + @{ + # this is for troubleshooting tests + # 2012/2012R2 test environments seem to have blank values for some environment vars + Name = 'paths_to_compare' + Expression = { + @{ + pUser = $pUser + pMachine = $pMachine + home = $Home + homedrive = $env:HOMEDRIVE + homepath = $env:HOMEPATH + providerhome = (Get-PSProvider -PSProvider FileSystem).Home + userprofile = $env:UserProfile + username = $env:UserName + } + } + } + + if (-not $scripts) { + $scripts = @() + } + + ConvertTo-Json -Depth 5 -InputObject ([object[]]$scripts) + register: _raw_info + +- name: Set script info var + set_fact: + "{{ script_info_var | default('scripts') }}": "{{ + dict( + _raw_info.stdout + | from_json + | map(attribute='Name') + | list + | zip( + _raw_info.stdout + | from_json + ) + ) + }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/setup_repos.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/setup_repos.yml new file mode 100644 index 000000000..94dea1871 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/setup_repos.yml @@ -0,0 +1,58 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Unregister all repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + notify: Add PSGallery + +- name: Create repo folders + ansible.windows.win_file: + path: '{{ item.path }}' + state: directory + loop: "{{ repos }}" + notify: Remove Directories + +- name: Register repos + win_psrepository: + name: '{{ item.name }}' + source: '{{ item.path }}' + installation_policy: '{{ item.policy }}' + notify: Clear Repositories + loop: "{{ repos }}" + +- name: Create and Publish Scripts + ansible.windows.win_shell: | + $tmp = '{{ remote_tmp_dir }}' + + # it looks like Tls12 is not added during the nuget bootstrapping process + # hitting connection closed errors during some tests. May be related to this: + # https://devblogs.microsoft.com/nuget/deprecating-tls-1-0-and-1-1-on-nuget-org/ + [System.Net.ServicePointManager]::SecurityProtocol = ` + [System.Net.ServicePointManager]::SecurityProtocol -bor + [System.Net.SecurityProtocolType]::SystemDefault -bor + [System.Net.SecurityProtocolType]::Tls11 -bor + [System.Net.SecurityProtocolType]::Tls12 + + {% for version in versions %} + {% for repo in repos %} + {% for script in repo.scripts %} + + $script = '{{ script }}' + $repo = '{{ repo.name }}' + $ver = '{{ version }}' + $file = "${script}.ps1" + $out = $tmp | Join-Path -ChildPath $file + + try { + New-ScriptFileInfo -Description $script -Version $ver -Path $out -Force + Publish-Script -Path $out -Repository $repo -Force + } finally { + Remove-Item -LiteralPath $out -Force -ErrorAction SilentlyContinue + } + + {% endfor %} + {% endfor %} + {% endfor %} diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/tests.yml new file mode 100644 index 000000000..7376b3fa5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript/tasks/tests.yml @@ -0,0 +1,555 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Basic Tests Script 1 + tags: + - basic1 + vars: + script: Test-One + scope: all_users + expected: + repo: Repo1 + version: "{{ latest_version }}" + block: + - name: Install a script (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure nothing was installed (check mode) + assert: + that: + - scripts | length == 0 + - script not in scripts + + - name: Install a script + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + register: result + notify: Clear Installed Scripts + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was installed + assert: + that: + - scripts | length == 1 + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + - scripts[script][scope] + + - name: Install script again (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + +- name: Basic Tests Script 2 + tags: + - basic2 + vars: + script: Test-Two + scope: current_user + expected: + repo: Repo2 + version: "{{ latest_version }}" + block: + - name: Install a script (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure nothing was installed (check mode) + assert: + that: + - scripts | length == 1 + - script not in scripts + + - name: Install a script + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + register: result + notify: Clear Installed Scripts + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was installed + assert: + that: + - scripts | length == 2 + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + - scripts[script][scope] + + - name: Install script again (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + +- name: Removal Tests Script 1 + tags: + - remove1 + vars: + script: Test-One + state: absent + block: + - name: Remove Script (check mode) + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was not removed (check mode) + assert: + that: + - scripts | length == 2 + - script in scripts + + - name: Remove Script + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was removed + assert: + that: + - scripts | length == 1 + - script not in scripts + + - name: Remove Script Again (check mode) + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + +- name: Removal Tests Script 2 + tags: + - remove2 + vars: + script: Test-Two + state: absent + block: + - name: Remove Script (check mode) + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was not removed (check mode) + assert: + that: + - scripts | length == 1 + - script in scripts + + - name: Remove Script + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was removed + assert: + that: + - scripts | length == 0 + - script not in scripts + + - name: Remove Script Again (check mode) + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + +- name: Tests for Script in Multiple Repos + tags: + - multi + vars: + script: Test-Multi + scope: all_users + expected: + repo: Repo2 + version: "{{ latest_version }}" + block: + - name: Install a script from multiple repos + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + register: result + failed_when: result.msg is not search('Multiple scripts found') + + - name: Install a script from multiple repos by chosing one (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + repository: "{{ expected.repo }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure nothing was installed (check mode) + assert: + that: + - scripts | length == 0 + - script not in scripts + + - name: Install a script from multiple repos by chosing one + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + repository: "{{ expected.repo }}" + register: result + notify: Clear Installed Scripts + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was installed + assert: + that: + - scripts | length == 1 + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + - scripts[script][scope] + + - name: Install script again (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + repository: "{{ expected.repo }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + +- name: Exact Version Tests + tags: + - exact + vars: + script: Test-Two + scope: current_user + expected: + repo: Repo2 + version: "{{ earliest_version }}" + block: + - name: Install a script (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + required_version: "{{ expected.version }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure nothing was installed (check mode) + assert: + that: + - script not in scripts + + - name: Install a script + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + required_version: "{{ expected.version }}" + register: result + notify: Clear Installed Scripts + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was installed + assert: + that: + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + - scripts[script][scope] + + - name: Install script again (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + required_version: "{{ expected.version }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + +- name: Minimum / Maximum Version Tests + tags: + - minmax + - present + vars: + script: Test-Two + scope: current_user + minver: 1.0.1 + maxver: 2.0.0 + expected: + repo: Repo2 + version: "1.0.10" + block: + - name: Install a script (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + minimum_version: "{{ minver }}" + maximum_version: "{{ maxver }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure nothing was installed (check mode) + assert: + that: + - script not in scripts or scripts[script].Version is version(expected.version, '<') + + - name: Install a script + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + minimum_version: "{{ minver }}" + maximum_version: "{{ maxver }}" + register: result + notify: Clear Installed Scripts + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was installed + assert: + that: + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + - scripts[script][scope] + + - name: Install script again (check mode) + win_psscript: + name: "{{ script }}" + scope: "{{ scope }}" + minimum_version: "{{ minver }}" + maximum_version: "{{ maxver }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + +- name: State Present Tests + tags: + - present + vars: + script: Test-Two + state: present + scope: all_users # implied + curver: 1.0.10 + expected: + repo: Repo2 + version: "{{ curver }}" + block: + - name: Install present script (check mode) + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed + + - import_tasks: script_info.yml + + - name: Ensure nothing was installed (check mode) + assert: + that: + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + + - name: Install present script + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + notify: Clear Installed Scripts + + - name: Assert task is not changed + assert: + that: result is not changed + + - import_tasks: script_info.yml + + - name: Ensure script was not installed + assert: + that: + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + +- name: State Latest Tests + tags: + - latest + vars: + script: Test-Two + state: latest + scope: all_users # implied + expected: + repo: Repo2 + version: "10.0.10" + block: + - name: Install latest script (check mode) + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + check_mode: True + + - name: Assert task is changed (check mode) + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure nothing was installed (check mode) + assert: + that: + - script not in scripts or scripts[script].Version is version(expected.version, '<') + + - name: Install latest script + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + notify: Clear Installed Scripts + + - name: Assert task is changed + assert: + that: result is changed + + - import_tasks: script_info.yml + + - name: Ensure script was installed + assert: + that: + - script in scripts + - scripts[script].Version is version(expected.version, '==') + - scripts[script].Repository == expected.repo + - scripts[script][scope] + + - name: Install latest script again (check mode) + win_psscript: + name: "{{ script }}" + state: "{{ state }}" + register: result + check_mode: True + + - name: Assert task is not changed (check mode) + assert: + that: result is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/aliases b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/defaults/main.yml new file mode 100644 index 000000000..786fb5477 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/defaults/main.yml @@ -0,0 +1,34 @@ +--- +run_check_mode: False +suffix: "{{ '(check mode)' if run_check_mode else '' }}" +repository_name: Repo +repo_path: "{{ remote_tmp_dir }}\\repo\\" + +sample_scripts: + - Test-RPC + - Upgrade-PowerShell + - Install-WMF3Hotfix + - Install-Git + +expected_fields: + - name + - version + - installed_location + - author + - copyright + - company_name + - description + - dependencies + - icon_uri + - license_uri + - project_uri + - repository_source_location + - repository + - release_notes + - installed_date + - published_date + - updated_date + - package_management_provider + - tags + - power_shell_get_format_version + - additional_metadata diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/install-git.1.0.5.nupkg b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/install-git.1.0.5.nupkg Binary files differnew file mode 100644 index 000000000..0dc10a004 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/install-git.1.0.5.nupkg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/install-wmf3hotfix.1.0.0.nupkg b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/install-wmf3hotfix.1.0.0.nupkg Binary files differnew file mode 100644 index 000000000..2728fb4d5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/install-wmf3hotfix.1.0.0.nupkg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/test-rpc.1.0.0.nupkg b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/test-rpc.1.0.0.nupkg Binary files differnew file mode 100644 index 000000000..caf11314f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/test-rpc.1.0.0.nupkg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/upgrade-powershell.1.0.0.nupkg b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/upgrade-powershell.1.0.0.nupkg Binary files differnew file mode 100644 index 000000000..d260004a2 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/files/upgrade-powershell.1.0.0.nupkg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/meta/main.yml new file mode 100644 index 000000000..acc7fbcae --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - setup_remote_tmp_dir + - setup_win_psget diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/common.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/common.yml new file mode 100644 index 000000000..b4cdda063 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/common.yml @@ -0,0 +1,37 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Assert that the correct structure is returned {{ suffix }} + assert: + that: + - script_info.scripts is defined + - script_info.scripts is sequence() + quiet: yes + +- name: Assert that the correct number of scripts are returned {{ suffix }} + assert: + that: script_info.scripts | length >= expected_scripts | length + fail_msg: >- + Expected {{ expected_scripts | length }} scripts, got {{ script_info.scripts | map(attribute='name') | join(',') }} ({{ script_info.scripts | length}}) + quiet: yes + +- name: Assert that all expected scripts are present {{ suffix }} + assert: + that: item in (script_info.scripts | map(attribute='name')) + fail_msg: "Expected script '{{ item }}' not found in results." + quiet: yes + loop: "{{ expected_scripts }}" + loop_control: + label: "Assert '{{ item }}' in result." + +- include_tasks: contains_all_fields.yml + vars: + dict_to_check: "{{ item }}" + loop: "{{ + only_check_first + | default(True) + | bool + | ternary([ script_info.scripts[0] ], script_info.scripts) + }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/contains_all_fields.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/contains_all_fields.yml new file mode 100644 index 000000000..20750e003 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/contains_all_fields.yml @@ -0,0 +1,13 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Check for key ('{{ key }}') in result + assert: + that: key in dict_to_check + quiet: yes + fail_msg: "'{{ key }}' not found in dict." + loop_control: + loop_var: key + loop: "{{ expected_fields }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/main.yml new file mode 100644 index 000000000..b3f1394e5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/main.yml @@ -0,0 +1,49 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Reset repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + Register-PSRepository -Default + +- name: Set up directory repository + ansible.windows.win_copy: + src: "{{ role_path }}/files/" + dest: "{{ repo_path }}" + force: no + +- name: Register repository + community.windows.win_psrepository: + name: "{{ repository_name }}" + source_location: "{{ repo_path }}" + installation_policy: trusted + +- block: + - name: Install Scripts + community.windows.win_psscript: + name: "{{ item }}" + state: latest + repository: "{{ repository_name }}" + loop: "{{ sample_scripts }}" + + - name: Run Tests + import_tasks: tests.yml + + - name: Run Tests (check mode) + import_tasks: tests.yml + vars: + run_check_mode: True + + always: + - name: Remove Scripts + community.windows.win_psscript: + name: "{{ item }}" + state: absent + loop: "{{ sample_scripts }}" + + - name: Reset repositories + ansible.windows.win_shell: | + Get-PSRepository | Unregister-PSRepository + Register-PSRepository -Default diff --git a/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/tests.yml new file mode 100644 index 000000000..45931c567 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_psscript_info/tasks/tests.yml @@ -0,0 +1,92 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Get info on a named script + vars: + expected_scripts: + - "{{ sample_scripts[0] }}" + block: + - name: Get single script {{ suffix }} + community.windows.win_psscript_info: + name: "{{ expected_scripts[0] }}" + register: script_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on scripts by wildcard match + vars: + wildcard: "Install-*" + expected_scripts: "{{ + sample_scripts + | select('match', '^Install-') + | list + }}" + block: + - name: Get multiple scripts by wildcard {{ suffix }} + community.windows.win_psscript_info: + name: "{{ wildcard }}" + register: script_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on scripts by repository + vars: + repository: "{{ repository_name }}" + expected_scripts: "{{ sample_scripts }}" + block: + - name: Get multiple scripts by repository {{ suffix }} + community.windows.win_psscript_info: + repository: "{{ repository }}" + register: script_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on scripts by repository and name pattern + vars: + wildcard: "*RPC" + repository: "{{ repository_name }}" + expected_scripts: "{{ + sample_scripts + | select('match', 'RPC$') + | list + }}" + block: + - name: Get scripts by wildcard and repository {{ suffix }} + community.windows.win_psscript_info: + name: "{{ wildcard }}" + repository: "{{ repository }}" + register: script_info + + - include_tasks: common.yml + + # block + check_mode: "{{ run_check_mode }}" + +- name: Get info on all scripts + vars: + expected_scripts: "{{ sample_scripts }}" + block: + - name: Get all scripts {{ suffix }} + community.windows.win_psscript_info: + register: script_info + + - include_tasks: common.yml + + # one last time checking all the fields + - include_tasks: common.yml + vars: + only_check_first: False + + # block + check_mode: "{{ run_check_mode }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/aliases b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/defaults/main.yml new file mode 100644 index 000000000..3a6642d34 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/defaults/main.yml @@ -0,0 +1,3 @@ +--- +config_name: Test +config_description: A config for testing diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/meta/main.yml new file mode 100644 index 000000000..5648cebe8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + # - setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/tasks/main.yml new file mode 100644 index 000000000..a56930b5d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/tasks/main.yml @@ -0,0 +1,21 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- block: + - ansible.windows.setup: + gather_subset: + - '!all' + - '!min' + - powershell_version + + - import_tasks: tests.yml + + always: + - name: Remove + ansible.windows.win_shell: | + Unregister-PSSessionConfiguration -Name '{{ config_name }}' -Force -ErrorAction SilentlyContinue + ignore_errors: yes + # sometimes this fails because the connection was interrupted + # it doesn't matter because it's still successful diff --git a/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/tasks/tests.yml new file mode 100644 index 000000000..23e62164f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_pssession_configuration/tasks/tests.yml @@ -0,0 +1,448 @@ +# This file is part of Ansible + +# Copyright: (c) 2020, Brian Scholer <@briantist> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: Create a simple configuration (check) + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + register: status + check_mode: yes + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +- name: Create a simple configuration + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + register: status + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +- name: Create a simple configuration again (check) + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + register: status + check_mode: yes + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + +- name: Create a simple configuration again + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + register: status + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + +####### + +- name: Try to disable polling (failure expected) + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}~~~~" + modules_to_import: Microsoft.PowerShell.Utility + async_timeout: 500 + async_poll: 0 + register: status + ignore_errors: yes + +- name: Ensure it failed + assert: + that: + - status is failed + - status.msg is search('async_poll') + quiet: yes + +- name: Try it with the keywords (failure expected) + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + async: 500 + poll: 2 + register: status + ignore_errors: yes + +- name: Check for expected failire + assert: + that: status is failed + quiet: yes + +####### + +- name: Try to set a parameter that's only available in PowerShell 5+ (check) + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + mount_user_drive: True + register: status + check_mode: yes + ignore_errors: yes + +- name: Check for version-based status + assert: + that: (status is failed) == (ansible_powershell_version < 5) + quiet: yes + +####### +# Incomplete tests for future enhancement of waiting for connections before making changes + +# - name: Start a 45 second WinRM connection +# ansible.windows.win_shell: | +# Invoke-Command -ComputerName . -ScriptBlock { Start-Sleep -Seconds 45 } -AsJob + +# - name: Make a change with a 10 second fail on timeout (failure expected) +# win_pssession_configuration: +# name: "{{ config_name }}" +# description: "{{ config_description }}" +# mount_user_drive: False +# existing_connection_timeout_seconds: 10 +# existing_connection_timeout_action: fail +# register: status +# ignore_errors: yes + +# - name: Check for failed status +# assert: +# that: status is failed +# quiet: yes + +# - name: Start a 25 second WinRM connection +# ansible.windows.win_shell: | +# Invoke-Command -ComputerName . -ScriptBlock { Start-Sleep -Seconds 25 } -AsJob + +# - name: Make a change with a 45 second fail on timeout (success expected) +# win_pssession_configuration: +# name: "{{ config_name }}" +# description: "{{ config_description }}" +# mount_user_drive: False +# existing_connection_timeout_seconds: 10 +# existing_connection_timeout_action: fail +# register: status + +# - name: Check for changed status +# assert: +# that: status is changed +# quiet: yes + +####### +- name: Omit a field that doesn't matter (check) + win_pssession_configuration: + name: "{{ config_name }}" + # description: "{{ config_description }}" # intentionally omitted + modules_to_import: Microsoft.PowerShell.Utility + register: status + check_mode: yes + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + +- name: Omit a field that doesn't matter + win_pssession_configuration: + name: "{{ config_name }}" + # description: "{{ config_description }}" # intentionally omitted + modules_to_import: Microsoft.PowerShell.Utility + register: status + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + +####### + +- name: Omit a field that doesn't matter by default but does explicitly (check) + win_pssession_configuration: + name: "{{ config_name }}" + # description: "{{ config_description }}" # intentionally omitted + modules_to_import: Microsoft.PowerShell.Utility + lenient_config_fields: + - guid + - author + register: status + check_mode: yes + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +- name: Omit a field that doesn't matter by default but does explicitly + win_pssession_configuration: + name: "{{ config_name }}" + # description: "{{ config_description }}" # intentionally omitted + modules_to_import: Microsoft.PowerShell.Utility + lenient_config_fields: + - guid + - author + register: status + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +######## + +- name: Change something not in the config file (check) + win_pssession_configuration: + name: "{{ config_name }}" + thread_options: reuse_thread + use_shared_process: True + register: status + check_mode: yes + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +- name: Change something not in the config file + win_pssession_configuration: + name: "{{ config_name }}" + thread_options: reuse_thread + use_shared_process: True + register: status + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +- name: Change something not in the config file (again) (check) + win_pssession_configuration: + name: "{{ config_name }}" + thread_options: reuse_thread + use_shared_process: True + register: status + check_mode: yes + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + +- name: Change something not in the config file (again) + win_pssession_configuration: + name: "{{ config_name }}" + thread_options: reuse_thread + use_shared_process: True + register: status + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + +###### + +- name: Check custom type parameters for PowerShell <= 4 (check) + win_pssession_configuration: + name: "{{ config_name }}" + guid: "{{ 'test' | to_uuid }}" + powershell_version: '3.0' + maximum_received_data_size_per_command_mb: 5.0E+11 + maximum_received_object_size_mb: 6.6666666666666666666666666 + register: status + when: ansible_powershell_version <= 4 + +- name: Check custom type parameters for PowerShell >= 5 (check) + win_pssession_configuration: + name: "{{ config_name }}" + guid: "{{ 'test' | to_uuid }}" + powershell_version: '3.0' + user_drive_maximum_size: 5000000000 + maximum_received_data_size_per_command_mb: 5.0E+11 + maximum_received_object_size_mb: 6.6666666666666666666666666 + register: status + when: ansible_powershell_version >= 5 + +###### + +- name: Reset back to a steady state + win_pssession_configuration: + name: "{{ config_name }}" + register: status + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + ignore_errors: yes + +- name: Reset back to a steady state (again) + win_pssession_configuration: + name: "{{ config_name }}" + register: status + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + ignore_errors: yes + +####### + +- name: "RunAs Tests" + vars: + runas_user: "{{ ansible_user }}" + runas_pass: "{{ ansible_password }}" + block: + - name: Change runas credential (check) + win_pssession_configuration: + name: "{{ config_name }}" + run_as_credential_username: "{{ runas_user }}" + run_as_credential_password: "{{ runas_pass }}" + register: status + check_mode: yes + + - name: Check for changed status + assert: + that: status is changed + quiet: yes + + - name: Change runas credential + win_pssession_configuration: + name: "{{ config_name }}" + run_as_credential_username: "{{ runas_user }}" + run_as_credential_password: "{{ runas_pass }}" + register: status + + - name: Check for changed status + assert: + that: status is changed + quiet: yes + + - name: Try an invalid runas credential (failure expected) + win_pssession_configuration: + name: "{{ config_name }}" + run_as_credential_username: Fake + run_as_credential_password: Fake + register: status + ignore_errors: yes + + - name: Check for failed status + assert: + that: status is failed + quiet: yes + + - name: Ensure the sessions configuration still exists with the working user + ansible.windows.win_shell: | + $sc = Get-PSSessionConfiguration -Name '{{ config_name }}' + if ($sc.RunAsUser -ne '{{ runas_user }}') { + throw + } + register: status + +####### + +- name: complex configuration for pwsh 5.1+ + when: ansible_powershell_version > 4 + block: + - name: Create a complex configuration + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + visible_aliases: [] + visible_cmdlets: [] + visible_external_commands: [] + visible_functions: [] + security_descriptor_sddl: "O:NSG:BAD:P(A;;GA;;;IU)(A;;GA;;;BA)(A;;GA;;;RM)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" + register: status + + - name: Check for changed status + assert: + that: status is changed + quiet: yes + + - name: Create a complex configuration again (check) + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + visible_aliases: [] + visible_cmdlets: [] + visible_external_commands: [] + visible_functions: [] + security_descriptor_sddl: "O:NSG:BAD:P(A;;GA;;;IU)(A;;GA;;;BA)(A;;GA;;;RM)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" + register: status + check_mode: yes + + - name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + + - name: Create a complex configuration again + win_pssession_configuration: + name: "{{ config_name }}" + description: "{{ config_description }}" + modules_to_import: Microsoft.PowerShell.Utility + visible_aliases: [] + visible_cmdlets: [] + visible_external_commands: [] + visible_functions: [] + security_descriptor_sddl: "O:NSG:BAD:P(A;;GA;;;IU)(A;;GA;;;BA)(A;;GA;;;RM)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" + register: status + + - name: Check for unchanged status + assert: + that: status is not changed + quiet: yes + +####### + +- name: Test session configuration removal (check) + win_pssession_configuration: + name: "{{ config_name }}" + state: absent + register: status + check_mode: yes + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +- name: Test session configuration removal + win_pssession_configuration: + name: "{{ config_name }}" + state: absent + register: status + +- name: Check for changed status + assert: + that: status is changed + quiet: yes + +- name: Test session configuration removal (again) + win_pssession_configuration: + name: "{{ config_name }}" + state: absent + register: status + +- name: Check for unchanged status + assert: + that: status is not changed + quiet: yes diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/aliases b/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/aliases new file mode 100644 index 000000000..aefcbeb1b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/aliases @@ -0,0 +1,2 @@ +shippable/windows/group3 +disabled diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/tasks/main.yml new file mode 100644 index 000000000..ce60dd109 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/tasks/main.yml @@ -0,0 +1,7 @@ +# Setup action creates ansible_distribution_version variable +- ansible.windows.setup: + +- include_tasks: tasks/tests.yml + # Works on windows >= Windows 7/Windows Server 2008 R2 + # See https://github.com/ansible/ansible/pull/28118#issuecomment-323684042 for additional info. + when: ansible_distribution_version is version('6.1', '>=') diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/tasks/tests.yml new file mode 100644 index 000000000..53ee0efdf --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rabbitmq_plugin/tasks/tests.yml @@ -0,0 +1,134 @@ +- name: Ensure RabbitMQ installed + chocolatey.chocolatey.win_chocolatey: + name: rabbitmq + state: present + +- name: Ensure that rabbitmq_management plugin disabled + win_rabbitmq_plugin: + names: rabbitmq_management + state: disabled + +- name: Enable rabbitmq_management plugin in check mode + win_rabbitmq_plugin: + names: rabbitmq_management + state: enabled + check_mode: yes + register: enable_plugin_in_check_mode + +- name: Check that enabling plugin in check mode succeeds with a change + assert: + that: + - enable_plugin_in_check_mode.changed == true + +- name: Enable rabbitmq_management plugin in check mode again + win_rabbitmq_plugin: + names: rabbitmq_management + state: enabled + check_mode: yes + register: enable_plugin_in_check_mode_again + +- name: Check that enabling plugin in check mode does not make changes + assert: + that: + - enable_plugin_in_check_mode_again.changed == true + +- name: Enable rabbitmq_management plugin + win_rabbitmq_plugin: + names: rabbitmq_management + state: enabled + register: enable_plugin + +- name: Check that enabling plugin succeeds with a change + assert: + that: + - enable_plugin.changed == true + - enable_plugin.enabled == ['rabbitmq_management'] + +- name: Enable enabled rabbitmq_management plugin + win_rabbitmq_plugin: + names: rabbitmq_management + state: enabled + register: enable_plugin_again + +- name: Check that enabling enabled plugin succeeds without a change + assert: + that: + - enable_plugin_again.changed == false + - enable_plugin_again.enabled == [] + +- name: Enable new plugin when 'new_only' option is 'no' (by default) and there are installed plugins + win_rabbitmq_plugin: + names: rabbitmq_mqtt + state: enabled + check_mode: yes + register: enable_plugin_without_new_only + +- name: Check that 'new_only == no' option enables new plugin and disables the old one + assert: + that: + - enable_plugin_without_new_only.changed == true + - enable_plugin_without_new_only.enabled == ['rabbitmq_mqtt'] + - enable_plugin_without_new_only.disabled == ['rabbitmq_management'] + +- name: Enable new plugin when 'new_only' option is 'yes' and there are installed plugins + win_rabbitmq_plugin: + names: rabbitmq_mqtt + state: enabled + new_only: yes + check_mode: yes + register: enable_plugin_with_new_only + +- name: Check that 'new_only == yes' option just enables new plugin + assert: + that: + - enable_plugin_with_new_only.changed == true + - enable_plugin_with_new_only.enabled == ['rabbitmq_mqtt'] + - enable_plugin_with_new_only.disabled == [] + +- name: Disable rabbitmq_management plugin in check mode + win_rabbitmq_plugin: + names: rabbitmq_management + state: disabled + check_mode: yes + register: disable_plugin_in_check_mode + +- name: Check that disabling plugin in check mode succeeds with a change + assert: + that: + - disable_plugin_in_check_mode.changed == true + +- name: Disable rabbitmq_management plugin in check mode again + win_rabbitmq_plugin: + names: rabbitmq_management + state: disabled + check_mode: yes + register: disable_plugin_in_check_mode_again + +- name: Check that disabling plugin in check mode does not make changes + assert: + that: + - disable_plugin_in_check_mode_again.changed == true + +- name: Disable rabbitmq_management plugin + win_rabbitmq_plugin: + names: rabbitmq_management + state: disabled + register: disable_plugin + +- name: Check that disabling plugin succeeds with a change + assert: + that: + - disable_plugin.changed == true + - disable_plugin.disabled == ['rabbitmq_management'] + +- name: Disable disabled rabbitmq_management plugin + win_rabbitmq_plugin: + names: rabbitmq_management + state: disabled + register: disable_plugin_again + +- name: Check that disabling disabled plugin succeeds without a change + assert: + that: + - disable_plugin_again.changed == false + - disable_plugin_again.disabled == [] diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/aliases b/ansible_collections/community/windows/tests/integration/targets/win_rds/aliases new file mode 100644 index 000000000..8d0a829ba --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/aliases @@ -0,0 +1,5 @@ +shippable/windows/group3 +destructive +win_rds_cap +win_rds_rap +win_rds_settings diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/defaults/main.yml new file mode 100644 index 000000000..a332469ea --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/defaults/main.yml @@ -0,0 +1,9 @@ +# win_rds_cap +test_win_rds_cap_name: Ansible Test CAP + +# win_rds_rap +test_win_rds_rap_name: Ansible Test RAP + +# win_rds_settings +test_win_rds_settings_path: '{{ remote_tmp_dir }}\win_rds_settings' +rds_cert_suject: rdg.test.com diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/meta/main.yml new file mode 100644 index 000000000..45806c8dc --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/main.yml new file mode 100644 index 000000000..25b32a553 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/main.yml @@ -0,0 +1,73 @@ +--- +# Cannot use win_feature to install RDS on Server 2008 +- name: check if feature is availble + ansible.windows.win_shell: if (Get-Command -Name Add-WindowsFeature -ErrorAction SilentlyContinue) { $true } else { $false } + changed_when: False + register: module_available + +- name: install Remote Desktop Gateway features + when: module_available.stdout | trim | bool + block: + - name: ensure Remote Desktop Gateway services are installed + ansible.windows.win_feature: + name: + - RDS-Gateway + - RDS-Licensing + - RDS-RD-Server + state: present + register: rds_install + + - name: reboot server if needed + ansible.windows.win_reboot: + when: rds_install.reboot_required + + # After a reboot Windows is still configuring the feature, this is a hack to wait until that is finished + - name: wait for component servicing to be finished + ansible.windows.win_shell: | + $start = Get-Date + $path = "HKLM:\SYSTEM\CurrentControlSet\Control\Winlogon\Notifications\Components\TrustedInstaller" + $tries = 0 + while ((Get-ItemProperty -Path $path -Name Events).Events.Contains("CreateSession")) { + $tries += 1 + Start-Sleep -Seconds 5 + if (((Get-Date) - $start).TotalSeconds -gt 180) { + break + } + } + $tries + changed_when: False + + - name: run win_rds_cap integration tests + include_tasks: win_rds_cap.yml + + - name: run win_rds_rap integration tests + include_tasks: win_rds_rap.yml + + - name: run win_rds_settings integration tests + include_tasks: win_rds_settings.yml + + always: + # Server 2008 R2 requires us to remove this first before the other features + - name: remove the RDS-Gateway feature + ansible.windows.win_feature: + name: RDS-Gateway + state: absent + register: rds_uninstall + + - name: reboot after removing RDS-Gateway feature + ansible.windows.win_reboot: + when: rds_uninstall.reboot_required + + # Now remove the remaining features + - name: remove installed RDS feature + ansible.windows.win_feature: + name: + - RDS-Licensing + - RDS-RD-Server + - Web-Server # not part of the initial feature install but RDS-Gateway requires this and it breaks httptester + state: absent + register: rds_uninstall2 + + - name: reboot after feature removal + ansible.windows.win_reboot: + when: rds_uninstall2.reboot_required diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_cap.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_cap.yml new file mode 100644 index 000000000..24adb25b9 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_cap.yml @@ -0,0 +1,9 @@ +--- +- name: run tests with cleanup + block: + - name: run tests + include_tasks: win_rds_cap_tests.yml + + always: + - name: delete all CAPs + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Remove-Item -Path RDS:\GatewayServer\CAP\* -Recurse diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_cap_tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_cap_tests.yml new file mode 100644 index 000000000..a7dc059a2 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_cap_tests.yml @@ -0,0 +1,264 @@ +--- +- name: test create a new CAP (check mode) + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + user_groups: + - administrators + - users@builtin + state: present + register: new_cap_check + check_mode: yes + +- name: get result of create a new CAP (check mode) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}") + register: new_cap_actual_check + +- name: assert results of create a new CAP (check mode) + assert: + that: + - new_cap_check.changed == true + - new_cap_actual_check.stdout_lines[0] == "False" + +- name: test create a new CAP + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + user_groups: + - administrators + - users@builtin + state: present + register: new_cap + +- name: get result of create a new CAP + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}") + register: new_cap_actual + +- name: assert results of create a new CAP + assert: + that: + - new_cap.changed == true + - new_cap_actual.stdout_lines[0] == "True" + +- name: test create a new CAP (idempotent) + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + user_groups: + - administrators + - users@builtin + state: present + register: new_cap_again + +- name: get result of create a new CAP (idempotent) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}") + register: new_cap_actual_again + +- name: assert results of create a new CAP (idempotent) + assert: + that: + - new_cap_again.changed == false + - new_cap_actual_again.stdout_lines[0] == "True" + +- name: test edit a CAP + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + user_groups: + # Test with different group name formats + - users@builtin + - .\guests + computer_groups: + - administrators + auth_method: both + session_timeout: 20 + session_timeout_action: reauth + allow_only_sdrts_servers: true + idle_timeout: 10 + redirect_clipboard: false + redirect_drives: false + redirect_printers: false + redirect_serial: false + redirect_pnp: false + state: disabled + register: edit_cap + +- name: get result of edit a CAP + ansible.windows.win_shell: | + Import-Module RemoteDesktopServices; + $cap_path = "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}" + $cap = @{} + Get-ChildItem -Path "$cap_path" | foreach { $cap.Add($_.Name,$_.CurrentValue) } + $cap.DeviceRedirection = @{} + Get-ChildItem -Path "$cap_path\DeviceRedirection" | foreach { $cap.DeviceRedirection.Add($_.Name, ($_.CurrentValue -eq 1)) } + $cap.UserGroups = @(Get-ChildItem -Path "$cap_path\UserGroups" | Select -ExpandProperty Name) + $cap.ComputerGroups = @(Get-ChildItem -Path "$cap_path\ComputerGroups" | Select -ExpandProperty Name) + $cap | ConvertTo-Json + register: edit_cap_actual_json + +- name: parse result of edit a CAP. + set_fact: + edit_cap_actual: '{{ edit_cap_actual_json.stdout | from_json }}' + +- name: assert results of edit a CAP + assert: + that: + - edit_cap.changed == true + - edit_cap_actual.Status == "0" + - edit_cap_actual.EvaluationOrder == "1" + - edit_cap_actual.AllowOnlySDRTSServers == "1" + - edit_cap_actual.AuthMethod == "3" + - edit_cap_actual.IdleTimeout == "10" + - edit_cap_actual.SessionTimeoutAction == "1" + - edit_cap_actual.SessionTimeout == "20" + - edit_cap_actual.DeviceRedirection.Clipboard == false + - edit_cap_actual.DeviceRedirection.DiskDrives == false + - edit_cap_actual.DeviceRedirection.PlugAndPlayDevices == false + - edit_cap_actual.DeviceRedirection.Printers == false + - edit_cap_actual.DeviceRedirection.SerialPorts == false + - edit_cap_actual.UserGroups | length == 2 + - edit_cap_actual.UserGroups[0] == "Users@BUILTIN" + - edit_cap_actual.UserGroups[1] == "Guests@BUILTIN" + - edit_cap_actual.ComputerGroups | length == 1 + - edit_cap_actual.ComputerGroups[0] == "Administrators@BUILTIN" + +- name: test remove all computer groups of CAP + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + computer_groups: [] + register: remove_computer_groups_cap + +- name: get result of remove all computer groups of CAP + ansible.windows.win_shell: | + Import-Module RemoteDesktopServices; + $cap_path = "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}" + Write-Host @(Get-ChildItem -Path "$cap_path\ComputerGroups" | Select -ExpandProperty Name).Count + register: remove_computer_groups_cap_actual + +- name: assert results of remove all computer groups of CAP + assert: + that: + - remove_computer_groups_cap.changed == true + - remove_computer_groups_cap_actual.stdout_lines[0] == "0" + +- name: test create a CAP in second position + win_rds_cap: + name: '{{ test_win_rds_cap_name }} Second' + user_groups: + - users@builtin + order: 2 + state: present + register: second_cap + +- name: get result of create a CAP in second position + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Get-Item "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }} Second\EvaluationOrder").CurrentValue + register: second_cap_actual + +- name: assert results of create a CAP in second position + assert: + that: + - second_cap.changed == true + - second_cap.warnings is not defined + - second_cap_actual.stdout_lines[0] == "2" + +- name: test create a CAP with order greater than existing CAP count + win_rds_cap: + name: '{{ test_win_rds_cap_name }} Last' + user_groups: + - users@builtin + order: 50 + state: present + register: cap_big_order + +- name: get result of create a CAP with order greater than existing CAP count + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Get-Item "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }} Last\EvaluationOrder").CurrentValue + register: cap_big_order_actual + +- name: assert results of create a CAP with order greater than existing CAP count + assert: + that: + - cap_big_order.changed == true + - cap_big_order.warnings | length == 1 + - cap_big_order_actual.stdout_lines[0] == "3" + +- name: test remove CAP (check mode) + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + state: absent + register: remove_cap_check + check_mode: yes + +- name: get result of remove CAP (check mode) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}") + register: remove_cap_actual_check + +- name: assert results of remove CAP (check mode) + assert: + that: + - remove_cap_check.changed == true + - remove_cap_actual_check.stdout_lines[0] == "True" + +- name: test remove CAP + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + state: absent + register: remove_cap_check + +- name: get result of remove CAP + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}") + register: remove_cap_actual_check + +- name: assert results of remove CAP + assert: + that: + - remove_cap_check.changed == true + - remove_cap_actual_check.stdout_lines[0] == "False" + +- name: test remove CAP (idempotent) + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + state: absent + register: remove_cap_check + +- name: get result of remove CAP (idempotent) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\CAP\{{ test_win_rds_cap_name }}") + register: remove_cap_actual_check + +- name: assert results of remove CAP (idempotent) + assert: + that: + - remove_cap_check.changed == false + - remove_cap_actual_check.stdout_lines[0] == "False" + +- name: fail when create a new CAP without user group + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + state: present + register: new_cap_without_group + check_mode: yes + failed_when: "new_cap_without_group.msg != 'User groups must be defined to create a new CAP.'" + +- name: fail when create a new CAP with an empty user group list + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + user_groups: [] + state: present + register: new_cap_empty_group_list + check_mode: yes + failed_when: "new_cap_empty_group_list.msg is not search('cannot be an empty list')" + +- name: fail when create a new CAP with an invalid user group + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + user_groups: + - fake_group + state: present + register: new_cap_invalid_user_group + check_mode: yes + failed_when: new_cap_invalid_user_group.changed != false or new_cap_invalid_user_group.msg is not search('is not a valid account') + +- name: fail when create a new CAP with an invalid computer group + win_rds_cap: + name: '{{ test_win_rds_cap_name }}' + computer_groups: + - fake_group + state: present + register: new_cap_invalid_computer_group + check_mode: yes + failed_when: new_cap_invalid_computer_group.changed != false or new_cap_invalid_computer_group.msg is not search('is not a valid account') diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_rap.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_rap.yml new file mode 100644 index 000000000..774076269 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_rap.yml @@ -0,0 +1,9 @@ +--- +- name: run tests with cleanup + block: + - name: run tests + include_tasks: win_rds_rap_tests.yml + + always: + - name: delete all RAPs + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Remove-Item -Path RDS:\GatewayServer\RAP\* -Recurse diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_rap_tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_rap_tests.yml new file mode 100644 index 000000000..1e0fdbe03 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_rap_tests.yml @@ -0,0 +1,254 @@ +--- +- name: test create a new RAP (check mode) + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: + - administrators + - users@builtin + state: present + register: new_rap_check + check_mode: yes + +- name: get result of create a new RAP (check mode) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}") + register: new_rap_actual_check + +- name: assert results of create a new RAP (check mode) + assert: + that: + - new_rap_check.changed == true + - new_rap_actual_check.stdout_lines[0] == "False" + +- name: test create a new RAP + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: + - administrators + - users@builtin + state: present + register: new_rap + +- name: get result of create a new RAP + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}") + register: new_rap_actual + +- name: assert results of create a new RAP + assert: + that: + - new_rap.changed == true + - new_rap_actual.stdout_lines[0] == "True" + +- name: test create a new RAP (idempotent) + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: + - administrators + - users@builtin + state: present + register: new_rap_again + +- name: get result of create a new RAP (idempotent) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}") + register: new_rap_actual_again + +- name: assert results of create a new RAP (idempotent) + assert: + that: + - new_rap_again.changed == false + - new_rap_actual_again.stdout_lines[0] == "True" + +- name: test edit a RAP + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + description: 'Description of {{ test_win_rds_rap_name }}' + user_groups: + # Test with different group name formats + - users@builtin + - .\guests + computer_group_type: ad_network_resource_group + computer_group: administrators + allowed_ports: + - 3389 + - 3390 + - 3391 + state: disabled + register: edit_rap + +- name: get result of edit a RAP + ansible.windows.win_shell: | + Import-Module RemoteDesktopServices; + $rap_path = "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}" + $rap = @{} + Get-ChildItem -Path "$rap_path" | foreach { $rap.Add($_.Name,$_.CurrentValue) } + $rap.UserGroups = @(Get-ChildItem -Path "$rap_path\UserGroups" | Select -ExpandProperty Name) + $rap | ConvertTo-Json + register: edit_rap_actual_json + +- name: parse result of edit a RAP. + set_fact: + edit_rap_actual: '{{ edit_rap_actual_json.stdout | from_json }}' + +- name: assert results of edit a RAP + assert: + that: + - edit_rap.changed == true + - edit_rap_actual.Status == "0" + - edit_rap_actual.Description == "Description of {{ test_win_rds_rap_name }}" + - edit_rap_actual.PortNumbers == "3389,3390,3391" + - edit_rap_actual.UserGroups | length == 2 + - edit_rap_actual.UserGroups[0] == "Users@BUILTIN" + - edit_rap_actual.UserGroups[1] == "Guests@BUILTIN" + - edit_rap_actual.ComputerGroupType == "1" + - edit_rap_actual.ComputerGroup == "Administrators@BUILTIN" + +- name: test edit a RAP (indempotent) + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + description: 'Description of {{ test_win_rds_rap_name }}' + user_groups: + - users@builtin + - guests@builtin + computer_group_type: ad_network_resource_group + computer_group: Administrators@BUILTIN + allowed_ports: + - 3389 + - 3390 + - 3391 + state: disabled + register: edit_rap_again + +- name: assert results of edit a RAP (indempotent) + assert: + that: + - edit_rap_again.changed == false + +- name: test allow all ports + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + allowed_ports: any + register: edit_rap_allow_all_ports + +- name: get result of allow all ports + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Get-Item "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}\PortNumbers").CurrentValue + register: edit_rap_allow_all_ports_actual + +- name: assert results of allow all ports + assert: + that: + - edit_rap_allow_all_ports.changed == true + - edit_rap_allow_all_ports_actual.stdout_lines[0] == "*" + +- name: test remove RAP (check mode) + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + state: absent + register: remove_rap_check + check_mode: yes + +- name: get result of remove RAP (check mode) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}") + register: remove_rap_actual_check + +- name: assert results of remove RAP (check mode) + assert: + that: + - remove_rap_check.changed == true + - remove_rap_actual_check.stdout_lines[0] == "True" + +- name: test remove RAP + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + state: absent + register: remove_rap + +- name: get result of remove RAP + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}") + register: remove_rap_actual + +- name: assert results of remove RAP + assert: + that: + - remove_rap.changed == true + - remove_rap_actual.stdout_lines[0] == "False" + +- name: test remove RAP (idempotent) + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + state: absent + register: remove_rap_again + +- name: get result of remove RAP (idempotent) + ansible.windows.win_shell: Import-Module RemoteDesktopServices; Write-Host (Test-Path "RDS:\GatewayServer\RAP\{{ test_win_rds_rap_name }}") + register: remove_rap_actual_again + +- name: assert results of remove RAP (idempotent) + assert: + that: + - remove_rap_again.changed == false + - remove_rap_actual_again.stdout_lines[0] == "False" + +- name: fail when create a new RAP without user group + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + state: present + register: new_rap_without_group + check_mode: yes + failed_when: "new_rap_without_group.msg != 'User groups must be defined to create a new RAP.'" + +- name: fail when create a new RAP with an empty user group list + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: [] + state: present + register: new_rap_empty_group_list + check_mode: yes + failed_when: "new_rap_empty_group_list.msg is not search('cannot be an empty list')" + +- name: fail when create a new RAP with an invalid user group + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: + - fake_group + state: present + register: new_rap_invalid_group + check_mode: yes + failed_when: new_rap_invalid_group.changed != false or new_rap_invalid_group.msg is not search('is not a valid account') + +- name: fail when create a new RAP with an invalid AD computer group + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: + - administrators + computer_group_type: ad_network_resource_group + computer_group: fake_ad_group + state: present + register: new_rap_invalid_ad_computer_group + check_mode: yes + failed_when: new_rap_invalid_ad_computer_group.changed != false or new_rap_invalid_ad_computer_group.msg is not search('is not a valid account') + +- name: fail when create a new RAP with an invalid gateway managed computer group + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: + - administrators + computer_group_type: rdg_group + computer_group: fake_rdg_group + state: present + register: new_rap_invalid_rdg_computer_group + check_mode: yes + failed_when: new_rap_invalid_rdg_computer_group.changed != false or new_rap_invalid_rdg_computer_group.msg is not search('is not a valid gateway managed computer group') + +- name: fail when create a new RAP with invalid port numbers + win_rds_rap: + name: '{{ test_win_rds_rap_name }}' + user_groups: + - administrators + allowed_ports: + - '{{ item }}' + state: present + loop: + - invalid_port_number + - 65536 + register: new_rap_invalid_port + check_mode: yes + failed_when: new_rap_invalid_port.changed != false or new_rap_invalid_port.msg is not search('is not a valid port number') diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_settings.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_settings.yml new file mode 100644 index 000000000..dff44963c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_settings.yml @@ -0,0 +1,66 @@ +- name: run tests with cleanup + block: + - name: gather facts + ansible.windows.setup: + filter: ansible_hostname + + - name: ensure testing folders exists + ansible.windows.win_file: + path: '{{test_win_rds_settings_path}}' + state: directory + + - name: deploy test artifacts + ansible.windows.win_template: + src: '{{item}}.j2' + dest: '{{test_win_rds_settings_path}}\{{item | basename}}' + with_items: + - rds_base_cfg.xml + + - name: import RDS test configuration + ansible.windows.win_shell: | + $ts = Get-WmiObject Win32_TSGatewayServer -namespace root\cimv2\TerminalServices + $import_xml = Get-Content {{test_win_rds_settings_path}}\rds_base_cfg.xml + $import_result = $ts.Import(45, $import_xml) + exit $import_result.ReturnValue + + - name: write certreq file + ansible.windows.win_copy: + content: |- + [NewRequest] + Subject = "CN={{ rds_cert_suject }}" + KeyLength = 2048 + KeyAlgorithm = RSA + MachineKeySet = true + RequestType = Cert + KeyUsage = 0xA0 ; Digital Signature, Key Encipherment + [EnhancedKeyUsageExtension] + OID=1.3.6.1.5.5.7.3.1 ; Server Authentication + dest: '{{test_win_rds_settings_path}}\certreq.txt' + + - name: create self signed cert from certreq + ansible.windows.win_command: certreq -new -machine {{test_win_rds_settings_path}}\certreq.txt {{test_win_rds_settings_path}}\certreqresp.txt + + - name: register certificate thumbprint + raw: '(gci Cert:\LocalMachine\my | ? {$_.subject -eq "CN={{ rds_cert_suject }}"})[0].Thumbprint' + register: rds_cert_thumbprint + + - name: run tests + include_tasks: win_rds_settings_tests.yml + + always: + - name: restore RDS base configuration + ansible.windows.win_shell: | + $ts = Get-WmiObject Win32_TSGatewayServer -namespace root\cimv2\TerminalServices + $import_xml = Get-Content {{test_win_rds_settings_path}}\rds_base_cfg.xml + $import_result = $ts.Import(45, $import_xml) + exit $import_result.ReturnValue + + - name: remove certificate + raw: 'remove-item cert:\localmachine\my\{{ item }} -force -ea silentlycontinue' + with_items: + - "{{ rds_cert_thumbprint.stdout_lines[0] }}" + + - name: cleanup test artifacts + ansible.windows.win_file: + path: '{{test_win_rds_settings_path}}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_settings_tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_settings_tests.yml new file mode 100644 index 000000000..0b611eb95 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/tasks/win_rds_settings_tests.yml @@ -0,0 +1,89 @@ +--- +- name: test change RDS settings (check mode) + win_rds_settings: + max_connections: 50 + certificate_hash: '{{rds_cert_thumbprint.stdout_lines[0]}}' + ssl_bridging: https_https + enable_only_messaging_capable_clients: yes + register: configure_rds_check + check_mode: yes + +- name: get result of change RDS settings (check mode) + ansible.windows.win_shell: | + Import-Module RemoteDesktopServices + (Get-Item RDS:\GatewayServer\MaxConnections).CurrentValue + (Get-Item RDS:\GatewayServer\SSLCertificate\Thumbprint).CurrentValue + (Get-Item RDS:\GatewayServer\SSLBridging).CurrentValue + (Get-Item RDS:\GatewayServer\EnableOnlyMessagingCapableClients).CurrentValue + register: configure_rds_actual_check + +- name: assert results of change RDS settings (check mode) + assert: + that: + - configure_rds_check.changed == true + - configure_rds_actual_check.stdout_lines[0] != "50" + - configure_rds_actual_check.stdout_lines[1] != rds_cert_thumbprint.stdout_lines[0] + - configure_rds_actual_check.stdout_lines[2] == "0" + - configure_rds_actual_check.stdout_lines[3] == "0" + +- name: test change RDS settings + win_rds_settings: + max_connections: 50 + certificate_hash: '{{rds_cert_thumbprint.stdout_lines[0]}}' + ssl_bridging: https_https + enable_only_messaging_capable_clients: yes + register: configure_rds + +- name: get result of change RDS settings + ansible.windows.win_shell: | + Import-Module RemoteDesktopServices + (Get-Item RDS:\GatewayServer\MaxConnections).CurrentValue + (Get-Item RDS:\GatewayServer\SSLCertificate\Thumbprint).CurrentValue + (Get-Item RDS:\GatewayServer\SSLBridging).CurrentValue + (Get-Item RDS:\GatewayServer\EnableOnlyMessagingCapableClients).CurrentValue + register: configure_rds_actual + +- name: assert results of change RDS settings + assert: + that: + - configure_rds.changed == true + - configure_rds_actual.stdout_lines[0] == "50" + - configure_rds_actual.stdout_lines[1] == rds_cert_thumbprint.stdout_lines[0] + - configure_rds_actual.stdout_lines[2] == "2" + - configure_rds_actual.stdout_lines[3] == "1" + +- name: test change RDS settings (idempotent) + win_rds_settings: + max_connections: 50 + certificate_hash: '{{rds_cert_thumbprint.stdout_lines[0]}}' + ssl_bridging: https_https + enable_only_messaging_capable_clients: yes + register: configure_rds_again + +- name: assert results of change RDS settings (idempotent) + assert: + that: + - configure_rds_again.changed == false + +- name: test disable connection limit + win_rds_settings: + max_connections: -1 + register: disable_limit + +- name: get result of disable connection limit + ansible.windows.win_shell: | + Import-Module RemoteDesktopServices + (Get-Item RDS:\GatewayServer\MaxConnections).CurrentValue -eq (Get-Item RDS:\GatewayServer\MaxConnectionsAllowed).CurrentValue + register: disable_limit_actual + +- name: assert results of disable connection limit + assert: + that: + - disable_limit.changed == true + - disable_limit_actual.stdout_lines[0] == "True" + +- name: fail with invalid certificate thumbprint + win_rds_settings: + certificate_hash: 72E8BD0216FA14100192A3E8B7B150C65B4B0817 + register: fail_invalid_cert + failed_when: fail_invalid_cert.msg is not search('Unable to locate certificate')
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_rds/templates/rds_base_cfg.xml.j2 b/ansible_collections/community/windows/tests/integration/targets/win_rds/templates/rds_base_cfg.xml.j2 new file mode 100644 index 000000000..5aa48ed84 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_rds/templates/rds_base_cfg.xml.j2 @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-16"?> +<?TSGateway version="1.0"?> +<TsgServer> + <ServerName>{{ ansible_hostname }}</ServerName> + <ServerSettings> + <MaxConnections>4294967295</MaxConnections> + <UnlimitedConnections>1</UnlimitedConnections> + <CentralCapEnabled>0</CentralCapEnabled> + <RequestSOH>0</RequestSOH> + <OnlyConsentCapableClients>0</OnlyConsentCapableClients> + <LogEvents> + <LogEvent> + <Name>LogChannelDisconnect</Name> + <Enabled>1</Enabled> + </LogEvent> + <LogEvent> + <Name>LogFailureChannelConnect</Name> + <Enabled>1</Enabled> + </LogEvent> + <LogEvent> + <Name>LogFailureConnectionAuthorizationCheck</Name> + <Enabled>1</Enabled> + </LogEvent> + <LogEvent> + <Name>LogFailureResourceAuthorizationCheck</Name> + <Enabled>1</Enabled> + </LogEvent> + <LogEvent> + <Name>LogSuccessfulChannelConnect</Name> + <Enabled>1</Enabled> + </LogEvent> + <LogEvent> + <Name>LogSuccessfulConnectionAuthorizationCheck</Name> + <Enabled>1</Enabled> + </LogEvent> + <LogEvent> + <Name>LogSuccessfulResourceAuthorizationCheck</Name> + <Enabled>1</Enabled> + </LogEvent> + </LogEvents> + <AuthenticationPlugin>native</AuthenticationPlugin> + <AuthorizationPlugin>native</AuthorizationPlugin> + <ConsentMessageText/> + <AdminMessageText/> + <AdminMsgStartDate/> + <AdminMsgEndDate/> + <SslBridging>0</SslBridging> + <HttpIPAddress>*</HttpIPAddress> + <UdpIPAddress>*</UdpIPAddress> + <HttpPort>443</HttpPort> + <UdpPort>3391</UdpPort> + <IsUdpEnabled>1</IsUdpEnabled> + <EnforceChannelBinding>1</EnforceChannelBinding> + </ServerSettings> + <Caps/> + <Raps/> + <ResourceGroups/> +</TsgServer>
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_region/aliases b/ansible_collections/community/windows/tests/integration/targets/win_region/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_region/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_region/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_region/tasks/main.yml new file mode 100644 index 000000000..be2b8be21 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_region/tasks/main.yml @@ -0,0 +1,252 @@ +# test code for the win_region module +# 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: expect failure when only setting copy_settings + win_region: + copy_settings: False + register: actual + failed_when: actual.msg != "An argument for 'format', 'location' or 'unicode_language' needs to be supplied" + +- name: expect failure when using invalid geo id for the location + win_region: + location: 111111 + register: actual + failed_when: actual.msg != "The argument location '111111' does not contain a valid Geo ID" + +- name: expect failure when using invalid culture for format + win_region: + format: ab-CD + register: actual + failed_when: actual.msg != "The argument format 'ab-CD' does not contain a valid Culture Name" + +- name: expect failure when using invalid culture for unicode_language + win_region: + unicode_language: ab-CD + register: actual + failed_when: actual.msg != "The argument unicode_language 'ab-CD' does not contain a valid Culture Name" + +- name: set settings all to English Australia before tests for a baseline + win_region: + location: 12 + format: en-AU + unicode_language: en-AU + +- name: reboot server to set properties + ansible.windows.win_reboot: + +- name: check that changing location in check mode works + win_region: + location: 244 + register: check_location + check_mode: yes + +- name: get current location value + ansible.windows.win_command: powershell (Get-ItemProperty -Path 'HKCU:\Control Panel\International\Geo').Nation + register: actual_location + +- name: check assertion about location change in check mode + assert: + that: + - "actual_location.stdout_lines[0] == '12'" # Corresponds to en-AU + - "check_location is changed" + - "check_location.restart_required == False" + +- name: set location to United States + win_region: + location: 244 + register: location + +- name: get current location value + ansible.windows.win_command: powershell (Get-ItemProperty -Path 'HKCU:\Control Panel\International\Geo').Nation + register: actual_location + +- name: check assertion about location change + assert: + that: + - "actual_location.stdout_lines[0] == '244'" # Corresponds to en-US + - "location is changed" + - "location.restart_required == False" + +- name: set location to United States again + win_region: + location: 244 + register: location_again + +- name: check that the result did not change + assert: + that: + - "location_again is not changed" + - "location_again.restart_required == False" + +- name: set format to English United States in check mode + win_region: + format: en-US + register: check_format + check_mode: yes + +- name: get actual format value from check mode + ansible.windows.win_command: powershell (Get-Culture).Name + register: actual_format + +- name: check assertion about location change in check mode + assert: + that: + - "actual_format.stdout_lines[0] == 'en-AU'" + - "check_format is changed" + - "check_format.restart_required == False" + +- name: set format to English United States + win_region: + format: en-US + register: format + +- name: get actual format value + ansible.windows.win_command: powershell (Get-Culture).Name + register: actual_format + +- name: check assertion about format change + assert: + that: + - "actual_format.stdout_lines[0] == 'en-US'" + - "format is changed" + - "format.restart_required == False" + +- name: set format to English United States again + win_region: + format: en-US + register: format_again + +- name: check that the result did not change + assert: + that: + - "format_again is not changed" + - "format_again.restart_required == False" + +- name: set unicode_language to English United States in check mode + win_region: + unicode_language: en-US + register: check_unicode + check_mode: yes + +- name: get actual unicode values + ansible.windows.win_command: powershell (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Nls\Language').Default + register: actual_unicode + +- name: check assertion about unicode language change in check mode + assert: + that: + - "actual_unicode.stdout_lines[0] == '0c09'" + - "check_unicode is changed" + - "check_unicode.restart_required == True" + +- name: set unicode_language to English United States + win_region: + unicode_language: en-US + register: unicode + +- name: reboot the server after changing unicode language + ansible.windows.win_reboot: + when: unicode.restart_required + +- name: get actual unicode value + ansible.windows.win_command: powershell (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Nls\Language').Default + register: actual_unicode + +- name: check assertion about unicode language change + assert: + that: + - "actual_unicode.stdout_lines[0] == '0409'" # corresponds to en-US + - "unicode is changed" + - "unicode.restart_required == True" + +- name: set unicode_language to English United States again + win_region: + unicode_language: en-US + register: unicode_again + +- name: check that the result did not change + assert: + that: + - "unicode_again is not changed" + - "unicode_again.restart_required == False" + +- name: copy settings when setting to the same format check mode + win_region: + format: en-US + copy_settings: True + register: check_copy_same + check_mode: yes + +- name: check that the result did not change in check mode + assert: + that: + - "check_copy_same is not changed" + - "check_copy_same.restart_required == False" + +- name: copy settings when setting to the same format + win_region: + format: en-US + copy_settings: True + register: copy_same + +- name: check that the result did not change + assert: + that: + - "copy_same is not changed" + - "copy_same.restart_required == False" + +- name: copy setting when setting to a different format + win_region: + format: en-GB + copy_settings: True + register: copy + +- name: get actual format value after copy_settings + ansible.windows.win_command: powershell (Get-Culture).Name + register: actual_copy + +- name: get locale name for local service registry hive + ansible.windows.win_command: powershell "New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null; (Get-ItemProperty 'HKU:\S-1-5-19\Control Panel\International').LocaleName" + register: actual_local + +- name: get locale name for network service registry hive + ansible.windows.win_command: powershell "New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null; (Get-ItemProperty 'HKU:\S-1-5-20\Control Panel\International').LocaleName" + register: actual_network + +- name: load temp hive + ansible.windows.win_command: reg load HKU\TEMP C:\Users\Default\NTUSER.DAT + +- name: get locale name for default registry hive + ansible.windows.win_command: powershell "New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null; (Get-ItemProperty 'HKU:\TEMP\Control Panel\International').LocaleName" + register: actual_temp + +- name: unload temp hive + ansible.windows.win_command: reg unload HKU\TEMP + +- name: get locale name for default registry hive + ansible.windows.win_command: powershell "New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null; (Get-ItemProperty 'HKU:\.DEFAULT\Control Panel\International').LocaleName" + register: actual_default + +- name: check assertions about copy setting when setting to a different format + assert: + that: + - "actual_copy.stdout_lines[0] == 'en-GB'" + - "actual_local.stdout_lines[0] == 'en-GB'" + - "actual_network.stdout_lines[0] == 'en-GB'" + - "actual_temp.stdout_lines[0] == 'en-GB'" + - "actual_default.stdout_lines[0] == 'en-GB'" + - "copy is changed" + - "copy.restart_required == False" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/aliases b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings1.reg b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings1.reg Binary files differnew file mode 100644 index 000000000..baec75b2a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings1.reg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings2.reg b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings2.reg Binary files differnew file mode 100644 index 000000000..fc2612cb8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings2.reg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings3.reg b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings3.reg Binary files differnew file mode 100644 index 000000000..fbe7411c9 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/files/settings3.reg diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/tasks/main.yml new file mode 100644 index 000000000..af640eb93 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/tasks/main.yml @@ -0,0 +1,133 @@ +# test code for the win_regmerge module +# (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/>. + +# clear the area of the registry we are using for tests +- name: remove setting + ansible.windows.win_regedit: + key: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp' + state: absent + +# copy over some registry files to work with +- name: copy over some registry files to work with + ansible.windows.win_copy: src={{item}} dest={{ remote_tmp_dir }}\\{{item}} + with_items: + - settings1.reg + - settings2.reg + - settings3.reg + +# test 1 - basic test of changed behaviour +# merge in REG_SZ +- name: test 1 merge in a setting + win_regmerge: + path: "{{ remote_tmp_dir }}\\settings1.reg" + register: merge11_result + +- assert: + that: + - "merge11_result.changed == true" + +# re run the merge +- name: test 1 merge in the setting again + win_regmerge: + path: "{{ remote_tmp_dir }}\\settings1.reg" + register: merge12_result + +# without a compare to key, should always report changed +- assert: + that: + - "merge12_result.changed == true" +# assert changed false + +# prune reg key +- name: test 1 remove setting + ansible.windows.win_regedit: + key: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp' + state: absent + +# +# test 2, observe behaviour when compare_to param is set +# +- name: test 2 merge in a setting + win_regmerge: + path: "{{ remote_tmp_dir }}\\settings1.reg" + compare_to: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp\Moosic\ILikeToMooveIt' + register: merge21_result + +- assert: + that: + - "merge21_result.changed == true" + +# re run the merge +- name: test 2 merge in the setting again but with compare_key + win_regmerge: + path: "{{ remote_tmp_dir }}\\settings1.reg" + compare_to: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp\Moosic\ILikeToMooveIt' + register: merge22_result + +# with a compare to key, should now report not changed +- assert: + that: + - "merge22_result.changed == false" +# assert changed false + +# prune the contents of the registry from the parent of the compare key downwards +- name: test 2 clean up remove setting + ansible.windows.win_regedit: + key: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp' + state: absent + +# test 3 merge in more complex settings +- name: test 3 merge in a setting + win_regmerge: + path: "{{ remote_tmp_dir }}\\settings3.reg" + compare_to: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp\Moo Monitor' + register: merge31_result + +- assert: + that: + - "merge31_result.changed == true" + +# re run the merge +- name: test 3 merge in the setting again but with compare_key check + win_regmerge: + path: "{{ remote_tmp_dir }}\\settings3.reg" + compare_to: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp\Moo Monitor' + register: merge32_result + +# with a compare to key, should now report not changed +- assert: + that: + - "merge32_result.changed == false" +# assert changed false + +# prune the contents of the registry from the compare key downwards +- name: test 3 clean up remove setting + ansible.windows.win_regedit: + key: 'HKLM:\SOFTWARE\Wow6432Node\Cow Corp' + state: absent + +# clean up registry files + +- name: clean up registry files + ansible.windows.win_file: path={{ remote_tmp_dir }}\\{{item}} state=absent + with_items: + - settings1.reg + - settings2.reg + - settings3.reg + +# END OF win_regmerge tests diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/templates/win_line_ending.j2 b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/templates/win_line_ending.j2 new file mode 100644 index 000000000..d0cefd76f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/templates/win_line_ending.j2 @@ -0,0 +1,4 @@ +#jinja2: newline_sequence:'\r\n' +{{ templated_var }}
+{{ templated_var }}
+{{ templated_var }}
diff --git a/ansible_collections/community/windows/tests/integration/targets/win_regmerge/vars/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/vars/main.yml new file mode 100644 index 000000000..1e8f64ccf --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_regmerge/vars/main.yml @@ -0,0 +1 @@ +templated_var: templated_var_loaded diff --git a/ansible_collections/community/windows/tests/integration/targets/win_route/aliases b/ansible_collections/community/windows/tests/integration/targets/win_route/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_route/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_route/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_route/defaults/main.yml new file mode 100644 index 000000000..a77ebc16c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_route/defaults/main.yml @@ -0,0 +1,3 @@ +--- +default_gateway: 192.168.1.1 +destination_ip_address: 192.168.2.10 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_route/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_route/tasks/main.yml new file mode 100644 index 000000000..4bbdee5b5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_route/tasks/main.yml @@ -0,0 +1,29 @@ +--- +# test code for the win_psmodule module when using winrm connection +# (c) 2017, Daniele Lazzari <lazzari@mailup.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: get os info + ansible.windows.win_shell: '[Environment]::OSVersion.Version -ge [Version]"6.3"' + register: os + +- name: Perform with os Windows 2012R2 or newer + when: os.stdout_lines[0] == "True" + block: + - name: run all tasks + include: tests.yml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_route/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_route/tasks/tests.yml new file mode 100644 index 000000000..dc456068a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_route/tasks/tests.yml @@ -0,0 +1,79 @@ +--- +- name: add a static route + win_route: + destination: "{{ destination_ip_address }}/32" + gateway: "{{ default_gateway }}" + metric: 1 + state: present + register: route + +- name: check if route successfully added + ansible.windows.win_shell: (Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '{{ destination_ip_address }}'").Caption + register: route_added + +- name: check route default gateway + ansible.windows.win_shell: (Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '{{ destination_ip_address }}'").NextHop + register: route_gateway + +- name: test if route successfully added + assert: + that: + - route is changed + - route_added.stdout_lines[0] == "{{ destination_ip_address }}" + - route_gateway.stdout_lines[0] == "{{ default_gateway }}" + +- name: add a static route to test idempotency + win_route: + destination: "{{ destination_ip_address }}/32" + gateway: "{{ default_gateway }}" + metric: 1 + state: present + register: idempotent_route + +- name: test idempotency + assert: + that: + - idempotent_route is not changed + - idempotent_route.output == "Static route already exists" + +- name: remove route + win_route: + destination: "{{ destination_ip_address }}/32" + state: absent + register: route_removed + +- name: check route is removed + ansible.windows.win_shell: Get-CimInstance win32_ip4PersistedrouteTable -Filter "Destination = '{{ destination_ip_address }}'" + register: check_route_removed + +- name: test route is removed + assert: + that: + - route_removed is changed + - check_route_removed.stdout == '' + +- name: remove static route to test idempotency + win_route: + destination: "{{ destination_ip_address }}/32" + state: absent + register: idempotent_route_removed + +- name: test idempotency + assert: + that: + - idempotent_route_removed is not changed + - idempotent_route_removed.output == "No route to remove" + +- name: add route to wrong ip address + win_route: + destination: "715.18.0.0/32" + gateway: "{{ default_gateway }}" + metric: 1 + state: present + ignore_errors: yes + register: wrong_ip + +- name: test route to wrong ip address + assert: + that: + - wrong_ip is failed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_say/aliases b/ansible_collections/community/windows/tests/integration/targets/win_say/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_say/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_say/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_say/tasks/main.yml new file mode 100644 index 000000000..5b37a19ba --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_say/tasks/main.yml @@ -0,0 +1,44 @@ +# CI hosts don't have a valid Speech package so we rely on check mode for basic +# sanity tests +--- +- name: Warn of impending deployment + win_say: + msg: Warning, deployment commencing in 5 minutes, please log out. + check_mode: "{{ win_say_check_mode |default('yes') }}" + +- name: Using a specified voice and a start sound + win_say: + msg: Warning, deployment commencing in 5 minutes, please log out. + start_sound_path: C:\Windows\Media\ding.wav + voice: Microsoft Hazel Desktop + check_mode: "{{ win_say_check_mode |default('yes') }}" + +- name: Example with start and end sound + win_say: + msg: New software installed + start_sound_path: C:\Windows\Media\Windows Balloon.wav + end_sound_path: C:\Windows\Media\chimes.wav + check_mode: "{{ win_say_check_mode |default('yes') }}" + +- name: Create message file + ansible.windows.win_copy: + content: Stay calm and carry on + dest: C:\Windows\Temp\win_say_message.txt + +- name: Text from file example + win_say: + msg_file: C:\Windows\Temp\win_say_message.txt + start_sound_path: C:\Windows\Media\Windows Balloon.wav + end_sound_path: C:\Windows\Media\chimes.wav + check_mode: "{{ win_say_check_mode |default('yes') }}" + +- name: Remove message file + ansible.windows.win_file: + path: C:\Windows\Temp\win_say_message.txt + state: absent + +- name: Different speech speed + win_say: + speech_speed: 5 + msg: Stay calm and proceed to the closest fire exit. + check_mode: "{{ win_say_check_mode |default('yes') }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/aliases b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/defaults/main.yml new file mode 100644 index 000000000..f6cd77da3 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/defaults/main.yml @@ -0,0 +1,15 @@ +--- +test_scheduled_task_name: Ansible Test +test_scheduled_task_path: \Ansible Test Folder +test_scheduled_task_user: MooCow +test_scheduled_task_pass: Password01 + +test_scheduled_task_invalid_chars: + - '\' + - '/' + - ':' + - '*' + - '"' + - '<' + - '>' + - '|' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/clean.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/clean.yml new file mode 100644 index 000000000..df7bbd06e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/clean.yml @@ -0,0 +1,16 @@ +# cleans up each test to ensure a blank slate +--- +- win_scheduled_task: + name: '{{item.name}}' + path: '{{item.path|default(omit)}}' + state: absent + with_items: + - name: Task # old tests + path: \Path + - name: '{{test_scheduled_task_name}}' + - name: '{{test_scheduled_task_name}}' + path: '{{test_scheduled_task_path}}' + +- ansible.windows.win_user: + name: '{{test_scheduled_task_user}}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/failures.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/failures.yml new file mode 100644 index 000000000..57c4e4895 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/failures.yml @@ -0,0 +1,161 @@ +# test out the known failure cases to ensure we have decent error messages +--- +# ensure we have good error messages for invalid task chars +- name: fail to create tasks with invalid characters + win_scheduled_task: + name: "Bad {{ item }} {{ test_scheduled_task_name }}" + state: present + actions: + - path: cmd.exe + arguments: /c echo hi + loop: "{{ test_scheduled_task_invalid_chars }}" + register: fail_invalid_chars + failed_when: fail_invalid_chars is not failed or fail_invalid_chars.msg is not search('The following characters are not valid') + +- name: fail create task without an action + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + register: fail_create_without_action + failed_when: fail_create_without_action.msg != 'cannot create a task with no actions, set at least one action with a path to an executable' + +- name: fail both username and group are set + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{ansible_user}}' + group: '{{ansible_user}}' + register: fail_username_and_group + failed_when: fail_username_and_group.msg != 'username and group can not be set at the same time' + +- name: fail logon type s4u but no password set + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + logon_type: s4u + register: fail_lt_s4u_not_set + failed_when: fail_lt_s4u_not_set.msg != 'password must be set when logon_type=s4u' + +- name: fail logon type group but no group set + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + logon_type: group + register: fail_lt_group_not_set + failed_when: fail_lt_group_not_set.msg != 'group must be set when logon_type=group' + +- name: fail logon type service but non service user set + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + logon_type: service_account + username: '{{ansible_user}}' + register: fail_lt_service_invalid_user + failed_when: fail_lt_service_invalid_user.msg != 'username must be SYSTEM, LOCAL SERVICE or NETWORK SERVICE when logon_type=service_account' + +- name: fail trigger with no type + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - delay: test + register: fail_trigger_no_type + failed_when: fail_trigger_no_type.msg != "a trigger entry must contain a key 'type' with a value of 'event', 'time', 'daily', 'weekly', 'monthly', 'monthlydow', 'idle', 'registration', 'boot', 'logon', 'session_state_change'" + +- name: fail trigger with datetime in incorrect format + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: time + start_boundary: fake + register: fail_trigger_invalid_datetime + failed_when: fail_trigger_invalid_datetime.msg != "trigger option 'start_boundary' must be in the format 'YYYY-MM-DDThh:mm:ss' format but was 'fake'" + +- name: fail trigger with duration in incorrect format + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: boot + execution_time_limit: fake + register: fail_trigger_invalid_duration + failed_when: fail_trigger_invalid_duration.msg != "trigger option 'execution_time_limit' must be in the XML duration format but was 'fake'" + +- name: fail trigger option invalid day of the week + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: weekly + start_boundary: '2000-01-01T00:00:01' + days_of_week: fakeday + register: fail_trigger_invalid_day_of_week + failed_when: fail_trigger_invalid_day_of_week.msg != "invalid day of week 'fakeday', check the spelling matches the full day name" + +- name: fail trigger option invalid day of the month + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: monthly + start_boundary: '2000-01-01T00:00:01' + days_of_month: 35 + register: fail_trigger_invalid_day_of_month + failed_when: fail_trigger_invalid_day_of_month.msg != "invalid day of month '35', please specify numbers from 1-31" + +- name: fail trigger option invalid week of the month + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: monthlydow + start_boundary: '2000-01-01T00:00:01' + weeks_of_month: 5 + register: fail_trigger_invalid_week_of_month + failed_when: fail_trigger_invalid_week_of_month.msg != "invalid week of month '5', please specify weeks from 1-4" + +- name: fail trigger option invalid month of the year + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: monthlydow + start_boundary: '2000-01-01T00:00:01' + months_of_year: fakemonth + register: fail_trigger_invalid_month_of_year + failed_when: fail_trigger_invalid_month_of_year.msg != "invalid month name 'fakemonth', please specify full month name" + +- name: fail trigger repetition with duration in incorrect format + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: boot + repetition: + - duration: fake + register: fail_trigger_repetition_invalid_duration + failed_when: fail_trigger_repetition_invalid_duration.msg != "trigger option 'duration' must be in the XML duration format but was 'fake'" + +- name: fail trigger repetition with interval in incorrect format + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: boot + repetition: + - interval: fake + register: fail_trigger_repetition_invalid_interval + failed_when: fail_trigger_repetition_invalid_interval.msg != "trigger option 'interval' must be in the XML duration format but was 'fake'" + +- name: fail trigger repetition option interval greater than duration + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + triggers: + - type: boot + repetition: + - interval: PT5M + duration: PT1M + register: fail_trigger_repetition_interval_greater_than_duration + failed_when: fail_trigger_repetition_interval_greater_than_duration.msg != "trigger repetition option 'interval' value 'PT5M' must be less than or equal to 'duration' value 'PT1M'" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/main.yml new file mode 100644 index 000000000..5142ac9ab --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/main.yml @@ -0,0 +1,24 @@ +--- +- name: remove test tasks before test + include_tasks: clean.yml + +- block: + - name: Test failure scenarios + include_tasks: failures.yml + + - name: Test normal scenarios + include_tasks: tests.yml + + - include_tasks: clean.yml + + - name: Test principals + include_tasks: principals.yml + + - include_tasks: clean.yml + + - name: Test triggers + include_tasks: triggers.yml + + always: + - name: remove test tasks after test + include_tasks: clean.yml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/principals.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/principals.yml new file mode 100644 index 000000000..9961855f8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/principals.yml @@ -0,0 +1,436 @@ +--- +- name: create test user + ansible.windows.win_user: + name: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + state: present + groups: + - Administrators + +- name: task with password principal (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + logon_type: password + update_password: no + actions: + - path: cmd.exe + register: task_with_password_check + check_mode: yes + +- name: get result of task with password principal (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_password_result_check + +- name: assert results of task with password principal (check mode) + assert: + that: + - task_with_password_check is changed + - task_with_password_result_check.task_exists == False + +- name: task with password principal + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + logon_type: password + update_password: no + actions: + - path: cmd.exe + register: task_with_password + +- name: get result of task with password principal + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_password_result + +- name: assert results of task with password principal + assert: + that: + - task_with_password is changed + - task_with_password_result.task_exists == True + - task_with_password_result.principal.group_id == None + - task_with_password_result.principal.logon_type == "TASK_LOGON_PASSWORD" + - task_with_password_result.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_password_result.principal.user_id.endswith(test_scheduled_task_user) + +- name: task with password principal (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + logon_type: password + update_password: no + actions: + - path: cmd.exe + register: task_with_password_again + +- name: assert results of task with password principal (idempotent) + assert: + that: + - task_with_password_again is not changed + +- name: task with password principal force pass change + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + logon_type: password + update_password: yes + actions: + - path: cmd.exe + register: task_with_password_force_update + +- name: assert results of task with password principal force pass change + assert: + that: + - task_with_password_force_update is changed + +- name: task with s4u principal (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + logon_type: s4u + update_password: no + actions: + - path: cmd.exe + register: task_with_s4u_check + check_mode: yes + +- name: get result of task with s4u principal (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_s4u_result_check + +- name: assert results of task with s4u principal (check mode) + assert: + that: + - task_with_s4u_check is changed + - task_with_s4u_result_check.task_exists == True + - task_with_s4u_result_check.principal.group_id == None + - task_with_s4u_result_check.principal.logon_type == "TASK_LOGON_PASSWORD" + - task_with_s4u_result_check.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_s4u_result_check.principal.user_id.endswith(test_scheduled_task_user) + +- name: task with s4u principal + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + logon_type: s4u + update_password: no + actions: + - path: cmd.exe + register: task_with_s4u + +- name: get result of task with s4u principal + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_s4u_result + +- name: assert results of task with s4u principal + assert: + that: + - task_with_s4u is changed + - task_with_s4u_result.task_exists == True + - task_with_s4u_result.principal.group_id == None + - task_with_s4u_result.principal.logon_type == "TASK_LOGON_S4U" + - task_with_s4u_result.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_s4u_result.principal.user_id.endswith(test_scheduled_task_user) + +- name: task with s4u principal (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + password: '{{test_scheduled_task_pass}}' + logon_type: s4u + update_password: no + actions: + - path: cmd.exe + register: task_with_s4u_again + +- name: assert results of task with s4u principal (idempotent) + assert: + that: + - task_with_s4u_again is not changed + +- name: task with interactive principal (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + logon_type: interactive_token + actions: + - path: cmd.exe + register: task_with_interactive_check + check_mode: yes + +- name: get result of task with interactive principal (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_interactive_result_check + +- name: assert results of task with interactive principal (check mode) + assert: + that: + - task_with_interactive_check is changed + - task_with_interactive_result_check.task_exists == True + - task_with_interactive_result_check.principal.group_id == None + - task_with_interactive_result_check.principal.logon_type == "TASK_LOGON_S4U" + - task_with_interactive_result_check.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_interactive_result_check.principal.user_id.endswith(test_scheduled_task_user) + +- name: task with interactive principal + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + logon_type: interactive_token + actions: + - path: cmd.exe + register: task_with_interactive + +- name: get result of task with interactive principal + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_interactive_result + +- name: assert results of task with interactive principal + assert: + that: + - task_with_interactive is changed + - task_with_interactive_result.task_exists == True + - task_with_interactive_result.principal.group_id == None + - task_with_interactive_result.principal.logon_type == "TASK_LOGON_INTERACTIVE_TOKEN" + - task_with_interactive_result.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_interactive_result.principal.user_id.endswith(test_scheduled_task_user) + +- name: task with interactive principal (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: '{{test_scheduled_task_user}}' + logon_type: interactive_token + actions: + - path: cmd.exe + register: task_with_interactive_again + +- name: assert results of task with interactive principal (idempotent) + assert: + that: + - task_with_interactive_again is not changed + +- name: task with group principal (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + group: Administrators + logon_type: group + actions: + - path: cmd.exe + register: task_with_group_check + check_mode: yes + +- name: get result of task with group principal (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_group_result_check + +- name: assert results of task with group principal (check mode) + assert: + that: + - task_with_group_check is changed + - task_with_group_result_check.task_exists == True + - task_with_group_result_check.principal.group_id == None + - task_with_group_result_check.principal.logon_type == "TASK_LOGON_INTERACTIVE_TOKEN" + - task_with_group_result_check.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_group_result_check.principal.user_id.endswith(test_scheduled_task_user) + +- name: task with group principal + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + group: Administrators + logon_type: group + actions: + - path: cmd.exe + register: task_with_group + +- name: get result of task with group principal + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_group_result + +- name: assert results of task with group principal + assert: + that: + - task_with_group is changed + - task_with_group_result.task_exists == True + - task_with_group_result.principal.group_id == "BUILTIN\\Administrators" + - task_with_group_result.principal.logon_type == "TASK_LOGON_GROUP" + - task_with_group_result.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_group_result.principal.user_id == None + +- name: task with group principal (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + group: Administrators + logon_type: group + actions: + - path: cmd.exe + register: task_with_group_again + +- name: assert results of task with group principal (idempotent) + assert: + that: + - task_with_group_again is not changed + +- name: task with service account principal (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: System + logon_type: service_account + action: + - path: cmd.exe + register: task_with_service_check + check_mode: yes + +- name: get result of task with service account principal (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_service_result_check + +- name: assert results of task with service account principal (check mode) + assert: + that: + - task_with_service_check is changed + - task_with_service_result_check.task_exists == True + - task_with_service_result_check.principal.group_id == "BUILTIN\\Administrators" + - task_with_service_result_check.principal.logon_type == "TASK_LOGON_GROUP" + - task_with_service_result_check.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_service_result_check.principal.user_id == None + +- name: task with service account principal + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: System + logon_type: service_account + action: + - path: cmd.exe + register: task_with_service + +- name: get result of task with service account principal + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_service_result + +- name: assert results of task with service account principal + assert: + that: + - task_with_service is changed + - task_with_service_result.task_exists == True + - task_with_service_result.principal.group_id == None + - task_with_service_result.principal.logon_type == "TASK_LOGON_SERVICE_ACCOUNT" + - task_with_service_result.principal.run_level == "TASK_RUNLEVEL_LUA" + - task_with_service_result.principal.user_id == "NT AUTHORITY\\SYSTEM" + +- name: task with service account principal (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + username: System + logon_type: service_account + action: + - path: cmd.exe + register: task_with_service_again + +- name: assert results of task with service account principal (idempotent) + assert: + that: + - task_with_service_again is not changed + +- name: task with highest privilege (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + run_level: highest + username: System + logon_type: service_account + action: + - path: cmd.exe + register: task_with_highest_privilege_check + check_mode: yes + +- name: get result of task with highest privilege (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_highest_privilege_result_check + +- name: assert results of task with highest privilege (check mode) + assert: + that: + - task_with_highest_privilege_check is changed + - task_with_highest_privilege_result_check.principal.run_level == "TASK_RUNLEVEL_LUA" + +- name: task with highest privilege + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + run_level: highest + username: System + logon_type: service_account + action: + - path: cmd.exe + register: task_with_highest_privilege + +- name: get result of task with highest privilege + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: task_with_highest_privilege_result + +- name: assert results of task with highest privilege + assert: + that: + - task_with_highest_privilege is changed + - task_with_highest_privilege_result.principal.run_level == "TASK_RUNLEVEL_HIGHEST" + +- name: task with highest privilege (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + run_level: highest + username: System + logon_type: service_account + action: + - path: cmd.exe + register: task_with_highest_privilege_again + +- name: assert results of task with highest privilege (idempotent) + assert: + that: + - task_with_highest_privilege_again is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/tests.yml new file mode 100644 index 000000000..b4ed29e49 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/tests.yml @@ -0,0 +1,440 @@ +--- +- name: create task (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + arguments: /c echo hi + description: Original Description + register: create_task_check + check_mode: yes + +- name: get result of create task (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: create_task_result_check + +- name: assert results of create task (check mode) + assert: + that: + - create_task_check is changed + - create_task_result_check.task_exists == False + +- name: create task + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + arguments: /c echo hi + description: Original Description + register: create_task + +- name: get result of create task + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: create_task_result + +- name: assert results of create task + assert: + that: + - create_task is changed + - create_task_result.task_exists == True + - create_task_result.actions|count == 1 + - create_task_result.actions[0].path == "cmd.exe" + - create_task_result.actions[0].arguments == "/c echo hi" + - create_task_result.actions[0].working_directory == None + - create_task_result.registration_info.description == "Original Description" + - create_task_result.triggers|count == 0 + +- name: create task (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + arguments: /c echo hi + description: Original Description + register: create_task_again + +- name: assert results of create task (idempotent) + assert: + that: + - create_task_again is not changed + +- name: change task (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + author: Cow Inc. + description: Test for Ansible + allow_demand_start: no + restart_count: 5 + restart_interval: PT2H5M + register: change_task_check + check_mode: yes + +- name: get result of change task (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: change_task_result_check + +- name: assert results of change task (check mode) + assert: + that: + - change_task_check is changed + - change_task_result_check.actions|count == 1 + - change_task_result_check.registration_info.author == None + - change_task_result_check.registration_info.description == "Original Description" + - change_task_result_check.settings.allow_demand_start == true + - change_task_result_check.settings.restart_count == 0 + - change_task_result_check.settings.restart_interval == None + +- name: change task + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + author: Cow Inc. + description: Test for Ansible + allow_demand_start: no + restart_count: 5 + restart_interval: PT1M + register: change_task + +- name: get result of change task + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: change_task_result + +- name: assert results of change task + assert: + that: + - change_task is changed + - change_task_result.actions|count == 1 + - change_task_result.registration_info.author == "Cow Inc." + - change_task_result.registration_info.description == "Test for Ansible" + - change_task_result.settings.allow_demand_start == false + - change_task_result.settings.restart_count == 5 + - change_task_result.settings.restart_interval == "PT1M" + +- name: change task (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + author: Cow Inc. + description: Test for Ansible + allow_demand_start: no + restart_count: 5 + restart_interval: PT1M + register: change_task_again + +- name: assert results of change task (idempotent) + assert: + that: + - change_task_again is not changed + +- name: add task action (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + arguments: /c echo hi + - path: powershell.exe + arguments: -File C:\ansible\script.ps1 + working_directory: C:\ansible + register: add_task_action_check + check_mode: yes + +- name: get result of add task action (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: add_task_action_result_check + +- name: assert results of add task action (check mode) + assert: + that: + - add_task_action_check is changed + - add_task_action_result_check.actions|count == 1 + - add_task_action_result_check.actions[0].path == "cmd.exe" + - add_task_action_result_check.actions[0].arguments == "/c echo hi" + - add_task_action_result_check.actions[0].working_directory == None + +- name: add task action + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + arguments: /c echo hi + - path: powershell.exe + arguments: -File C:\ansible\script.ps1 + working_directory: C:\ansible + register: add_task_action + +- name: get result of add task action + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: add_task_action_result + +- name: assert results of add task action + assert: + that: + - add_task_action is changed + - add_task_action_result.actions|count == 2 + - add_task_action_result.actions[0].path == "cmd.exe" + - add_task_action_result.actions[0].arguments == "/c echo hi" + - add_task_action_result.actions[0].working_directory == None + - add_task_action_result.actions[1].path == "powershell.exe" + - add_task_action_result.actions[1].arguments == "-File C:\\ansible\\script.ps1" + - add_task_action_result.actions[1].working_directory == "C:\\ansible" + +- name: add task action (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + arguments: /c echo hi + - path: powershell.exe + arguments: -File C:\ansible\script.ps1 + working_directory: C:\ansible + register: add_task_action_again + +- name: assert results of add task action (idempotent) + assert: + that: + - add_task_action_again is not changed + +- name: remove task action (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: powershell.exe + arguments: -File C:\ansible\script.ps1 + working_directory: C:\ansible + register: remove_task_action_check + check_mode: yes + +- name: get result of remove task action (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_task_action_result_check + +- name: assert results of remove task action (check mode) + assert: + that: + - remove_task_action_check is changed + - remove_task_action_result_check.actions|count == 2 + - remove_task_action_result_check.actions[0].path == "cmd.exe" + - remove_task_action_result_check.actions[0].arguments == "/c echo hi" + - remove_task_action_result_check.actions[0].working_directory == None + - remove_task_action_result_check.actions[1].path == "powershell.exe" + - remove_task_action_result_check.actions[1].arguments == "-File C:\\ansible\\script.ps1" + - remove_task_action_result_check.actions[1].working_directory == "C:\\ansible" + +- name: remove task action + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: powershell.exe + arguments: -File C:\ansible\script.ps1 + working_directory: C:\ansible + register: remove_task_action + +- name: get result of remove task action + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_task_action_result + +- name: assert results of remove task action + assert: + that: + - remove_task_action is changed + - remove_task_action_result.actions|count == 1 + - remove_task_action_result.actions[0].path == "powershell.exe" + - remove_task_action_result.actions[0].arguments == "-File C:\\ansible\\script.ps1" + - remove_task_action_result.actions[0].working_directory == "C:\\ansible" + +- name: remove task action (idempontent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: powershell.exe + arguments: -File C:\ansible\script.ps1 + working_directory: C:\ansible + register: remove_task_action_again + +- name: assert results of remove task action (idempotent) + assert: + that: + - remove_task_action_again is not changed + +- name: remove task (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: absent + register: remove_task_check + check_mode: yes + +- name: get result of remove task (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_task_result_check + +- name: assert results of remove task (check mode) + assert: + that: + - remove_task_check is changed + - remove_task_result_check.task_exists == True + +- name: remove task + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: absent + register: remove_task + +- name: get result of remove task + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_task_result + +- name: assert results of remove task + assert: + that: + - remove_task is changed + - remove_task_result.task_exists == False + +- name: remove task (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: absent + register: remove_task_again + +- name: assert results of remove task (idempotent) + assert: + that: + - remove_task_again is not changed + +- name: create sole task in folder (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + path: '{{test_scheduled_task_path}}' + actions: + - path: cmd.exe + register: create_sole_task_check + check_mode: yes + +- name: get result of create sole task in folder (check mode) + win_scheduled_task_stat: + path: '{{test_scheduled_task_path}}' + name: '{{test_scheduled_task_name}}' + register: create_sole_task_result_check + +- name: assert results of create sole task in folder (check mode) + assert: + that: + - create_sole_task_check is changed + - create_sole_task_result_check.folder_exists == False + - create_sole_task_result_check.task_exists == False + +- name: create sole task in folder + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + path: '{{test_scheduled_task_path}}' + actions: + - path: cmd.exe + register: create_sole_task + +- name: get result of create sole task in folder + win_scheduled_task_stat: + path: '{{test_scheduled_task_path}}' + name: '{{test_scheduled_task_name}}' + register: create_sole_task_result + +- name: assert results of create sole task in folder + assert: + that: + - create_sole_task is changed + - create_sole_task_result.folder_exists == True + - create_sole_task_result.task_exists == True + +- name: create sole task in folder (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + path: '{{test_scheduled_task_path}}' + actions: + - path: cmd.exe + register: create_sole_task_again + +- name: assert results of create sole task in folder (idempotent) + assert: + that: + - create_sole_task_again is not changed + +- name: remove sole task in folder (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + path: '{{test_scheduled_task_path}}' + state: absent + register: remove_sole_task_check + check_mode: yes + +- name: get result of remove sole task in folder (check mode) + win_scheduled_task_stat: + path: '{{test_scheduled_task_path}}' + name: '{{test_scheduled_task_name}}' + register: remove_sole_task_result_check + +- name: assert results of remove sole task in folder (check mode) + assert: + that: + - remove_sole_task_check is changed + - remove_sole_task_result_check.folder_exists == True + - remove_sole_task_result_check.task_exists == True + +- name: remove sole task in folder + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + path: '{{test_scheduled_task_path}}' + state: absent + register: remove_sole_task + +- name: get result of remove sole task in folder + win_scheduled_task_stat: + path: '{{test_scheduled_task_path}}' + name: '{{test_scheduled_task_name}}' + register: remove_sole_task_result + +- name: assert results of remove sole task in folder + assert: + that: + - remove_sole_task is changed + - remove_sole_task_result.folder_exists == False + - remove_sole_task_result.task_exists == False + +- name: remove sole task in folder (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + path: '{{test_scheduled_task_path}}' + state: absent + register: remove_sole_task_again + +- name: assert results of remove sole task in folder (idempotent) + assert: + that: + - remove_sole_task_again is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/triggers.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/triggers.yml new file mode 100644 index 000000000..eae42c98a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task/tasks/triggers.yml @@ -0,0 +1,851 @@ +--- +- name: create boot trigger (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: boot + register: trigger_boot_check + check_mode: yes + +- name: get result of create boot trigger (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_boot_result_check + +- name: assert results of create boot trigger (check mode) + assert: + that: + - trigger_boot_check is changed + - trigger_boot_result_check.task_exists == False + +- name: create boot trigger + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: boot + register: trigger_boot + +- name: get result of create boot trigger + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_boot_result + +- name: assert results of create boot trigger + assert: + that: + - trigger_boot is changed + - trigger_boot_result.task_exists == True + - trigger_boot_result.triggers|count == 1 + - trigger_boot_result.triggers[0].type == "TASK_TRIGGER_BOOT" + - trigger_boot_result.triggers[0].enabled == True + - trigger_boot_result.triggers[0].start_boundary == None + - trigger_boot_result.triggers[0].end_boundary == None + +- name: create boot trigger (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: boot + register: trigger_boot_again + +- name: assert results of create boot trigger (idempotent) + assert: + that: + - trigger_boot_again is not changed + +- name: create daily trigger (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: daily + start_boundary: '2000-01-01T00:00:01' + register: trigger_daily_check + check_mode: yes + +- name: get result of create daily trigger (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_daily_result_check + +- name: assert results of create daily trigger (check mode) + assert: + that: + - trigger_daily_check is changed + - trigger_daily_result_check.task_exists == True + - trigger_daily_result_check.triggers|count == 1 + - trigger_daily_result_check.triggers[0].type == "TASK_TRIGGER_BOOT" + - trigger_daily_result_check.triggers[0].enabled == True + - trigger_daily_result_check.triggers[0].start_boundary == None + - trigger_daily_result_check.triggers[0].end_boundary == None + +- name: create daily trigger + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: daily + start_boundary: '2000-01-01T00:00:01' + register: trigger_daily + +- name: get result of create daily trigger + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_daily_result + +- name: assert results of create daily trigger + assert: + that: + - trigger_daily is changed + - trigger_daily_result.task_exists == True + - trigger_daily_result.triggers|count == 1 + - trigger_daily_result.triggers[0].type == "TASK_TRIGGER_DAILY" + - trigger_daily_result.triggers[0].enabled == True + - trigger_daily_result.triggers[0].start_boundary == "2000-01-01T00:00:01" + - trigger_daily_result.triggers[0].end_boundary == None + +- name: create daily trigger (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: daily + start_boundary: '2000-01-01T00:00:01' + register: trigger_daily_again + +- name: assert results of create daily trigger (idempotent) + assert: + that: + - trigger_daily_again is not changed + +- name: create logon trigger (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: logon + register: trigger_logon_check + check_mode: yes + +- name: get result of create logon trigger (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_logon_result_check + +- name: assert results of create logon trigger + assert: + that: + - trigger_logon_check is changed + - trigger_logon_result_check.task_exists == True + - trigger_logon_result_check.triggers|count == 1 + - trigger_logon_result_check.triggers[0].type == "TASK_TRIGGER_DAILY" + - trigger_logon_result_check.triggers[0].enabled == True + - trigger_logon_result_check.triggers[0].start_boundary == "2000-01-01T00:00:01" + - trigger_logon_result_check.triggers[0].end_boundary == None + +- name: create logon trigger + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: logon + register: trigger_logon + +- name: get result of create logon trigger + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_logon_result + +- name: assert results of create logon trigger + assert: + that: + - trigger_logon is changed + - trigger_logon_result.task_exists == True + - trigger_logon_result.triggers|count == 1 + - trigger_logon_result.triggers[0].type == "TASK_TRIGGER_LOGON" + - trigger_logon_result.triggers[0].enabled == True + - trigger_logon_result.triggers[0].start_boundary == None + - trigger_logon_result.triggers[0].end_boundary == None + +- name: create logon trigger (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: logon + register: trigger_logon_again + +- name: assert results of create logon trigger (idempotent) + assert: + that: + - trigger_logon_again is not changed + +- name: create monthly dow trigger (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: monthlydow + start_boundary: '2000-01-01T00:00:01' + weeks_of_month: 1,2 + run_on_last_week_of_month: true + days_of_week: [ "monday", "wednesday" ] + register: trigger_monthlydow_check + check_mode: yes + +- name: get result of create monthly dow trigger (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_monthlydow_result_check + +- name: assert results of create monthly dow trigger (check mode) + assert: + that: + - trigger_monthlydow_check is changed + - trigger_monthlydow_result_check.task_exists == True + - trigger_monthlydow_result_check.triggers|count == 1 + - trigger_monthlydow_result_check.triggers[0].type == "TASK_TRIGGER_LOGON" + - trigger_monthlydow_result_check.triggers[0].enabled == True + - trigger_monthlydow_result_check.triggers[0].start_boundary == None + - trigger_monthlydow_result_check.triggers[0].end_boundary == None + +- name: create monthly dow trigger + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: monthlydow + start_boundary: '2000-01-01T00:00:01+03:00' + weeks_of_month: 1,2 + run_on_last_week_of_month: true + days_of_week: [ "monday", "wednesday" ] + register: trigger_monthlydow + +- name: get result of create monthly dow trigger + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: trigger_monthlydow_result + +- name: get expected date based on host timezone + ansible.windows.win_shell: (Get-Date '1999-12-31T21:00:01+00:00').ToString('yyyy-MM-ddTHH:mm:sszzz') + register: trigger_monthlydow_date + +- name: assert results of create monthly dow trigger + assert: + that: + - trigger_monthlydow is changed + - trigger_monthlydow_result.task_exists == True + - trigger_monthlydow_result.triggers|count == 1 + - trigger_monthlydow_result.triggers[0].type == "TASK_TRIGGER_MONTHLYDOW" + - trigger_monthlydow_result.triggers[0].enabled == True + - trigger_monthlydow_result.triggers[0].start_boundary == trigger_monthlydow_date.stdout|trim + - trigger_monthlydow_result.triggers[0].end_boundary == None + - trigger_monthlydow_result.triggers[0].weeks_of_month == "1,2" + - trigger_monthlydow_result.triggers[0].run_on_last_week_of_month == True + - trigger_monthlydow_result.triggers[0].days_of_week == "monday,wednesday" + +- name: create monthly dow trigger (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: monthlydow + start_boundary: '2000-01-01T00:00:01+03:00' + weeks_of_month: 1,2 + run_on_last_week_of_month: true + days_of_week: [ "monday", "wednesday" ] + register: trigger_monthlydow_again + +- name: assert results of create monthly dow trigger (idempotent) + assert: + that: + - trigger_monthlydow_again is not changed + +- name: create trigger repetition (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + repetition: + # TODO: change to dict in 2.12 as a list format is deprecated + - interval: PT1M + duration: PT5M + stop_at_duration_end: yes + register: create_trigger_repetition_check + check_mode: yes + +- name: get result of create trigger repetition (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: create_trigger_repetition_result_check + +- name: assert results of create trigger repetition (check mode) + assert: + that: + - create_trigger_repetition_check is changed + - create_trigger_repetition_check.deprecations|count == 1 + - create_trigger_repetition_check.deprecations[0].date == "2021-07-01" + - create_trigger_repetition_check.deprecations[0].msg == "repetition is a list, should be defined as a dict" + - create_trigger_repetition_result_check.task_exists == True + - create_trigger_repetition_result_check.triggers|count == 1 + - create_trigger_repetition_result_check.triggers[0].type == "TASK_TRIGGER_MONTHLYDOW" + - create_trigger_repetition_result_check.triggers[0].enabled == True + - create_trigger_repetition_result_check.triggers[0].start_boundary == trigger_monthlydow_date.stdout|trim + - create_trigger_repetition_result_check.triggers[0].end_boundary == None + - create_trigger_repetition_result_check.triggers[0].weeks_of_month == "1,2" + - create_trigger_repetition_result_check.triggers[0].run_on_last_week_of_month == True + - create_trigger_repetition_result_check.triggers[0].days_of_week == "monday,wednesday" + - create_trigger_repetition_result_check.triggers[0].repetition.interval == None + - create_trigger_repetition_result_check.triggers[0].repetition.duration == None + - create_trigger_repetition_result_check.triggers[0].repetition.stop_at_duration_end == False + +- name: create trigger repetition + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + repetition: + interval: PT1M + duration: PT5M + stop_at_duration_end: yes + register: create_trigger_repetition + +- name: get result of create trigger repetition + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: create_trigger_repetition_result + +- name: assert results of create trigger repetition + assert: + that: + - create_trigger_repetition is changed + - create_trigger_repetition_result.task_exists == True + - create_trigger_repetition_result.triggers|count == 1 + - create_trigger_repetition_result.triggers[0].type == "TASK_TRIGGER_REGISTRATION" + - create_trigger_repetition_result.triggers[0].enabled == True + - create_trigger_repetition_result.triggers[0].start_boundary == None + - create_trigger_repetition_result.triggers[0].end_boundary == None + - create_trigger_repetition_result.triggers[0].repetition.interval == "PT1M" + - create_trigger_repetition_result.triggers[0].repetition.duration == "PT5M" + - create_trigger_repetition_result.triggers[0].repetition.stop_at_duration_end == True + +- name: create trigger repetition (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + repetition: + interval: PT1M + duration: PT5M + stop_at_duration_end: yes + register: create_trigger_repetition_again + +- name: assert results of create trigger repetition (idempotent) + assert: + that: + - create_trigger_repetition_again is not changed + +- name: change trigger repetition (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + repetition: + interval: PT10M + duration: PT20M + stop_at_duration_end: no + register: change_trigger_repetition_check + check_mode: yes + +- name: get result of change trigger repetition (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: change_trigger_repetition_result_check + +- name: assert results of change trigger repetition (check mode) + assert: + that: + - change_trigger_repetition_check is changed + - change_trigger_repetition_result_check.task_exists == True + - change_trigger_repetition_result_check.triggers|count == 1 + - change_trigger_repetition_result_check.triggers[0].type == "TASK_TRIGGER_REGISTRATION" + - change_trigger_repetition_result_check.triggers[0].enabled == True + - change_trigger_repetition_result_check.triggers[0].start_boundary == None + - change_trigger_repetition_result_check.triggers[0].end_boundary == None + - change_trigger_repetition_result_check.triggers[0].repetition.interval == "PT1M" + - change_trigger_repetition_result_check.triggers[0].repetition.duration == "PT5M" + - change_trigger_repetition_result_check.triggers[0].repetition.stop_at_duration_end == True + +- name: change trigger repetition + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + repetition: + interval: PT10M + duration: PT20M + stop_at_duration_end: no + register: change_trigger_repetition + +- name: get result of change trigger repetition + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: change_trigger_repetition_result + +- name: assert results of change trigger repetition + assert: + that: + - change_trigger_repetition is changed + - change_trigger_repetition_result.task_exists == True + - change_trigger_repetition_result.triggers|count == 1 + - change_trigger_repetition_result.triggers[0].type == "TASK_TRIGGER_REGISTRATION" + - change_trigger_repetition_result.triggers[0].enabled == True + - change_trigger_repetition_result.triggers[0].start_boundary == None + - change_trigger_repetition_result.triggers[0].end_boundary == None + - change_trigger_repetition_result.triggers[0].repetition.interval == "PT10M" + - change_trigger_repetition_result.triggers[0].repetition.duration == "PT20M" + - change_trigger_repetition_result.triggers[0].repetition.stop_at_duration_end == False + +- name: change trigger repetition (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + repetition: + interval: PT10M + duration: PT20M + stop_at_duration_end: no + register: change_trigger_repetition_again + +- name: assert results of change trigger repetition (idempotent) + assert: + that: + - change_trigger_repetition_again is not changed + +- name: create trigger with state_change + win_scheduled_task: + name: '{{ test_scheduled_task_name }}' + state: present + actions: + - path: cmd.exe + triggers: + - type: session_state_change + state_change: session_lock + register: trigger_state_change + +- name: get result of create trigger with state change + win_scheduled_task_stat: + path: \ + name: '{{ test_scheduled_task_name }}' + register: trigger_state_change_result + +- name: assert results of create trigger with state_change + assert: + that: + - trigger_state_change is changed + - trigger_state_change_result.triggers|count == 1 + - trigger_state_change_result.triggers[0].type == "TASK_TRIGGER_SESSION_STATE_CHANGE" + - trigger_state_change_result.triggers[0].state_change == 7 + - trigger_state_change_result.triggers[0].state_change_str == "TASK_SESSION_LOCK" + - trigger_state_change_result.triggers[0].user_id == None + +- name: create task with multiple triggers (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: monthly + days_of_month: 1,5,10,15,20,25,30 + run_on_last_day_of_month: true + start_boundary: '2000-01-01T00:00:01' + months_of_year: + - march + - may + - july + - type: time + start_boundary: '2000-01-01T00:00:01' + random_delay: PT10M5S + register: create_multiple_triggers_check + check_mode: yes + +- name: get result of create task with multiple triggers (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: create_multiple_triggers_result_check + +- name: assert results of create task with multiple triggers (check mode) + assert: + that: + - create_multiple_triggers_check is changed + - create_multiple_triggers_result_check.task_exists == True + - create_multiple_triggers_result_check.triggers|count == 1 + - create_multiple_triggers_result_check.triggers[0].type == "TASK_TRIGGER_SESSION_STATE_CHANGE" + - create_multiple_triggers_result_check.triggers[0].state_change == 7 + - create_multiple_triggers_result_check.triggers[0].state_change_str == "TASK_SESSION_LOCK" + - create_multiple_triggers_result_check.triggers[0].user_id == None + +- name: create task with multiple triggers + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: monthly + days_of_month: 1,5,10,15,20,25,30 + run_on_last_day_of_month: true + start_boundary: '2000-01-01T00:00:01' + months_of_year: + - march + - may + - july + - type: time + start_boundary: '2000-01-01T00:00:01' + random_delay: PT10M5S + register: create_multiple_triggers + +- name: get result of create task with multiple triggers + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: create_multiple_triggers_result + +- name: assert results of create task with multiple triggers + assert: + that: + - create_multiple_triggers is changed + - create_multiple_triggers_result.task_exists == True + - create_multiple_triggers_result.triggers|count == 2 + - create_multiple_triggers_result.triggers[0].type == "TASK_TRIGGER_MONTHLY" + - create_multiple_triggers_result.triggers[0].enabled == True + - create_multiple_triggers_result.triggers[0].start_boundary == "2000-01-01T00:00:01" + - create_multiple_triggers_result.triggers[0].end_boundary == None + - create_multiple_triggers_result.triggers[0].days_of_month == "1,5,10,15,20,25,30" + - create_multiple_triggers_result.triggers[0].months_of_year == "march,may,july" + - create_multiple_triggers_result.triggers[0].run_on_last_day_of_month == True + - create_multiple_triggers_result.triggers[1].type == "TASK_TRIGGER_TIME" + - create_multiple_triggers_result.triggers[1].enabled == True + - create_multiple_triggers_result.triggers[1].start_boundary == "2000-01-01T00:00:01" + - create_multiple_triggers_result.triggers[1].end_boundary == None + - create_multiple_triggers_result.triggers[1].random_delay == "PT10M5S" + +- name: create task with multiple triggers (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: monthly + days_of_month: 1,5,10,15,20,25,30 + run_on_last_day_of_month: true + start_boundary: '2000-01-01T00:00:01' + months_of_year: + - march + - may + - july + - type: time + start_boundary: '2000-01-01T00:00:01' + random_delay: PT10M5S + register: create_multiple_triggers_again + +- name: assert results of create task with multiple triggers (idempotent) + assert: + that: + - create_multiple_triggers_again is not changed + +- name: change task with multiple triggers (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: weekly + days_of_week: tuesday,friday + start_boundary: '2000-01-01T00:00:01' + - type: registration + enabled: no + register: change_multiple_triggers_check + check_mode: yes + +- name: get result of change task with multiple triggers (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: change_multiple_triggers_result_check + +- name: assert results of change task with multiple triggers (check mode) + assert: + that: + - change_multiple_triggers_check is changed + - change_multiple_triggers_result_check.task_exists == True + - change_multiple_triggers_result_check.triggers|count == 2 + - change_multiple_triggers_result_check.triggers[0].type == "TASK_TRIGGER_MONTHLY" + - change_multiple_triggers_result_check.triggers[0].enabled == True + - change_multiple_triggers_result_check.triggers[0].start_boundary == "2000-01-01T00:00:01" + - change_multiple_triggers_result_check.triggers[0].end_boundary == None + - change_multiple_triggers_result_check.triggers[0].days_of_month == "1,5,10,15,20,25,30" + - change_multiple_triggers_result_check.triggers[0].months_of_year == "march,may,july" + - change_multiple_triggers_result_check.triggers[0].run_on_last_day_of_month == True + - change_multiple_triggers_result_check.triggers[1].type == "TASK_TRIGGER_TIME" + - change_multiple_triggers_result_check.triggers[1].enabled == True + - change_multiple_triggers_result_check.triggers[1].start_boundary == "2000-01-01T00:00:01" + - change_multiple_triggers_result_check.triggers[1].end_boundary == None + - change_multiple_triggers_result_check.triggers[1].random_delay == "PT10M5S" + +- name: change task with multiple triggers + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: weekly + days_of_week: tuesday,friday + start_boundary: '2000-01-01T00:00:01' + - type: registration + enabled: no + register: change_multiple_triggers + +- name: get result of change task with multiple triggers + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: change_multiple_triggers_result + +- name: assert results of change task with multiple triggers + assert: + that: + - change_multiple_triggers is changed + - change_multiple_triggers_result.task_exists == True + - change_multiple_triggers_result.triggers|count == 2 + - change_multiple_triggers_result.triggers[0].type == "TASK_TRIGGER_WEEKLY" + - change_multiple_triggers_result.triggers[0].enabled == True + - change_multiple_triggers_result.triggers[0].start_boundary == "2000-01-01T00:00:01" + - change_multiple_triggers_result.triggers[0].end_boundary == None + - change_multiple_triggers_result.triggers[0].days_of_week == "tuesday,friday" + - change_multiple_triggers_result.triggers[1].type == "TASK_TRIGGER_REGISTRATION" + - change_multiple_triggers_result.triggers[1].enabled == False + - change_multiple_triggers_result.triggers[1].start_boundary == None + - change_multiple_triggers_result.triggers[1].end_boundary == None + +- name: change task with multiple triggers (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: weekly + days_of_week: tuesday,friday + start_boundary: '2000-01-01T00:00:01' + - type: registration + enabled: no + register: change_multiple_triggers_again + +- name: assert results of change task with multiple triggers (idempotent) + assert: + that: + - change_multiple_triggers_again is not changed + +- name: remove trigger from multiple triggers (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + enabled: no + register: remove_single_trigger_check + check_mode: yes + +- name: get result of remove trigger from multiple triggers (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_single_trigger_result_check + +- name: assert results of remove trigger from multiple triggers (check mode) + assert: + that: + - remove_single_trigger_check is changed + - remove_single_trigger_result_check.task_exists == True + - remove_single_trigger_result_check.triggers|count == 2 + - remove_single_trigger_result_check.triggers[0].type == "TASK_TRIGGER_WEEKLY" + - remove_single_trigger_result_check.triggers[0].enabled == True + - remove_single_trigger_result_check.triggers[0].start_boundary == "2000-01-01T00:00:01" + - remove_single_trigger_result_check.triggers[0].end_boundary == None + - remove_single_trigger_result_check.triggers[0].days_of_week == "tuesday,friday" + - remove_single_trigger_result_check.triggers[1].type == "TASK_TRIGGER_REGISTRATION" + - remove_single_trigger_result_check.triggers[1].enabled == False + - remove_single_trigger_result_check.triggers[1].start_boundary == None + - remove_single_trigger_result_check.triggers[1].end_boundary == None + +- name: remove trigger from multiple triggers + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + enabled: no + register: remove_single_trigger + +- name: get result of remove trigger from multiple triggers + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_single_trigger_result + +- name: assert results of remove trigger from multiple triggers + assert: + that: + - remove_single_trigger is changed + - remove_single_trigger_result.task_exists == True + - remove_single_trigger_result.triggers|count == 1 + - remove_single_trigger_result.triggers[0].type == "TASK_TRIGGER_REGISTRATION" + - remove_single_trigger_result.triggers[0].enabled == False + - remove_single_trigger_result.triggers[0].start_boundary == None + - remove_single_trigger_result.triggers[0].end_boundary == None + +- name: remove trigger from multiple triggers (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: + - type: registration + enabled: no + register: remove_single_trigger_again + +- name: assert results of remove trigger from multiple triggers (idempotent) + assert: + that: + - remove_single_trigger_again is not changed + +- name: remove all triggers (check mode) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: [] + register: remove_triggers_check + check_mode: yes + +- name: get result of remove all triggers (check mode) + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_triggers_result_check + +- name: assert results of remove all triggers (check mode) + assert: + that: + - remove_triggers_check is changed + - remove_triggers_result_check.task_exists == True + - remove_triggers_result_check.triggers|count == 1 + - remove_triggers_result_check.triggers[0].type == "TASK_TRIGGER_REGISTRATION" + - remove_triggers_result_check.triggers[0].enabled == False + - remove_triggers_result_check.triggers[0].start_boundary == None + - remove_triggers_result_check.triggers[0].end_boundary == None + +- name: remove all triggers + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: [] + register: remove_triggers + +- name: get result of remove all triggers + win_scheduled_task_stat: + path: \ + name: '{{test_scheduled_task_name}}' + register: remove_triggers_result + +- name: assert results of remove all triggers + assert: + that: + - remove_triggers is changed + - remove_triggers_result.task_exists == True + - remove_triggers_result.triggers|count == 0 + +- name: remove all triggers (idempotent) + win_scheduled_task: + name: '{{test_scheduled_task_name}}' + state: present + actions: + - path: cmd.exe + triggers: [] + register: remove_triggers_again + +- name: assert results of remove all triggers (idempotent) + assert: + that: + - remove_triggers_again is not changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/aliases b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/aliases new file mode 100644 index 000000000..423ce3910 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/aliases @@ -0,0 +1 @@ +shippable/windows/group2 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/defaults/main.yml new file mode 100644 index 000000000..55a2b87b3 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/defaults/main.yml @@ -0,0 +1,5 @@ +--- +test_scheduled_task_stat_name: Test Task +test_scheduled_task_stat_path: \test path +test_scheduled_task_stat_username: testtaskuser +test_scheduled_task_stat_password: Password123!
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/tasks/main.yml new file mode 100644 index 000000000..736899487 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/tasks/main.yml @@ -0,0 +1,47 @@ +--- +- name: ensure task is deleted before test + win_scheduled_task: + name: '{{test_scheduled_task_stat_name}}' + path: '{{test_scheduled_task_stat_path}}' + state: absent + +- name: create test user for service execution + ansible.windows.win_user: + name: '{{test_scheduled_task_stat_username}}' + password: '{{test_scheduled_task_stat_password}}' + state: present + groups_action: add + groups: + - Users + register: user_info + +# Run actual tests +- block: + - name: normalise test account name + ansible.windows.win_powershell: + parameters: + SID: '{{ user_info.sid }}' + script: | + [CmdletBinding()] + param ([String]$SID) + + $Ansible.Changed = $false + ([System.Security.Principal.SecurityIdentifier]$SID).Translate([System.Security.Principal.NTAccount]).Value + register: test_normalised_username + + - set_fact: + test_normalised_username: '{{ test_normalised_username.output[0] }}' + + - include_tasks: tests.yml + + always: + - name: remove test user + ansible.windows.win_user: + name: '{{test_scheduled_task_stat_username}}' + state: absent + + - name: delete task after test + win_scheduled_task: + name: '{{test_scheduled_task_stat_name}}' + path: '{{test_scheduled_task_stat_path}}' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/tasks/tests.yml new file mode 100644 index 000000000..b0c8454d1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scheduled_task_stat/tasks/tests.yml @@ -0,0 +1,175 @@ +--- +# folder stat tests + +# check_mode operation test +- name: check_mode - get stat of a folder that is missing + win_scheduled_task_stat: + path: '{{test_scheduled_task_stat_path}}' + register: stat_folder_missing + check_mode: True + +- name: assert that check_mode works + assert: + that: stat_folder_missing is not skipped + +- name: get stat of a folder that is missing + win_scheduled_task_stat: + path: '{{test_scheduled_task_stat_path}}' + register: stat_folder_missing + +- name: assert get stat of a folder that is missing + assert: + that: + - stat_folder_missing.folder_exists == False + +- name: get stat of existing folder + win_scheduled_task_stat: + path: \ + register: stat_folder_present + +- name: assert get stat of existing folder + assert: + that: + - stat_folder_present.folder_exists == True + - stat_folder_present.folder_task_count is defined + - stat_folder_present.folder_task_names is defined + +- name: create scheduled task in folder + win_scheduled_task: + path: '{{test_scheduled_task_stat_path}}' + name: '{{test_scheduled_task_stat_name}}' + state: present + logon_type: interactive_token + username: '{{ test_normalised_username }}' + author: Ansible Author + description: Fake description + execution_time_limit: PT23H + disallow_start_if_on_batteries: false + restart_count: 3 + restart_interval: PT15M + actions: + - path: cmd.exe + - path: C:\temp\some.exe + arguments: --help + working_directory: C:\temp + triggers: + - type: boot + delay: PT15M + - type: monthly + days_of_month: 5,15,30 + months_of_year: june,december + run_on_last_day_of_month: true + start_boundary: '2017-09-20T03:44:38' + - type: session_state_change + state_change: console_disconnect + user_id: '{{ test_normalised_username }}' + +- name: get stat of existing folder with task + win_scheduled_task_stat: + path: '{{test_scheduled_task_stat_path}}' + register: stat_folder_with_task + +- name: assert get stat of existing folder with task + assert: + that: + - stat_folder_with_task.folder_exists == True + - stat_folder_with_task.folder_task_count == 1 + - stat_folder_with_task.folder_task_names[0] == "Test Task" + - stat_folder_with_task.task_exists is not defined + +# task stat tests +- name: get stat of missing task with invalid folder + win_scheduled_task_stat: + path: fake path + name: fake task + register: stat_task_missing_folder + +- name: assert get stat of missing task with invalid folder + assert: + that: + - stat_task_missing_folder.folder_exists == False + - stat_task_missing_folder.task_exists == False + +- name: get stat of missing task + win_scheduled_task_stat: + path: '{{test_scheduled_task_stat_path}}' + name: fake task + register: stat_task_missing + +- name: assert get stat of missing task + assert: + that: + - stat_task_missing.task_exists == False + +- name: get stat of existing task + win_scheduled_task_stat: + path: '{{test_scheduled_task_stat_path}}' + name: '{{test_scheduled_task_stat_name}}' + register: stat_task_present + +- name: assert get stat of existing task + assert: + that: + - stat_task_present.task_exists == True + - stat_task_present.actions|count == 2 + - stat_task_present.actions[0].path == "cmd.exe" + - stat_task_present.actions[0].type == "TASK_ACTION_EXEC" + - stat_task_present.actions[0].working_directory == None + - stat_task_present.actions[1].arguments == "--help" + - stat_task_present.actions[1].path == "C:\\temp\some.exe" + - stat_task_present.actions[1].type == "TASK_ACTION_EXEC" + - stat_task_present.actions[1].working_directory == "C:\\temp" + - stat_task_present.principal.display_name == None + - stat_task_present.principal.group_id == None + - stat_task_present.principal.logon_type == "TASK_LOGON_INTERACTIVE_TOKEN" + - stat_task_present.principal.run_level == "TASK_RUNLEVEL_LUA" + - stat_task_present.principal.user_id == test_normalised_username + - stat_task_present.registration_info.author == "Ansible Author" + - stat_task_present.registration_info.date is defined + - stat_task_present.registration_info.description == "Fake description" + - stat_task_present.settings.disallow_start_if_on_batteries == False + - stat_task_present.settings.execution_time_limit == "PT23H" + - stat_task_present.settings.restart_count == 3 + - stat_task_present.settings.restart_interval == "PT15M" + - stat_task_present.state.status == "TASK_STATE_READY" + - stat_task_present.triggers|count == 3 + - stat_task_present.triggers[0].delay == "PT15M" + - stat_task_present.triggers[0].type == "TASK_TRIGGER_BOOT" + - stat_task_present.triggers[0].repetition.stop_at_duration_end == False + - stat_task_present.triggers[0].repetition.duration == None + - stat_task_present.triggers[0].repetition.interval == None + - stat_task_present.triggers[1].days_of_month == "5,15,30" + - stat_task_present.triggers[1].months_of_year == "june,december" + - stat_task_present.triggers[1].run_on_last_day_of_month == True + - stat_task_present.triggers[1].start_boundary == "2017-09-20T03:44:38" + - stat_task_present.triggers[1].type == "TASK_TRIGGER_MONTHLY" + - stat_task_present.triggers[1].repetition.stop_at_duration_end == False + - stat_task_present.triggers[1].repetition.duration == None + - stat_task_present.triggers[1].repetition.interval == None + - stat_task_present.triggers[2].type == "TASK_TRIGGER_SESSION_STATE_CHANGE" + - stat_task_present.triggers[2].user_id == test_normalised_username + - stat_task_present.triggers[2].state_change == 2 + - stat_task_present.triggers[2].state_change_str == "TASK_CONSOLE_DISCONNECT" + +- name: change principal to system account so it will run in the next step + win_scheduled_task: + name: '{{test_scheduled_task_stat_name}}' + path: '{{test_scheduled_task_stat_path}}' + username: SYSTEM + +- name: start the scheduled task + ansible.windows.win_command: schtasks.exe /Run /TN "{{test_scheduled_task_stat_path}}\{{test_scheduled_task_stat_name}}" + +- name: get stat of running task + win_scheduled_task_stat: + path: '{{test_scheduled_task_stat_path}}' + name: '{{test_scheduled_task_stat_name}}' + register: stat_task_running + +- name: assert stat of running task + assert: + that: + - stat_task_running.state.status == "TASK_STATE_RUNNING" + +- name: stop the scheduled task + ansible.windows.win_command: schtasks.exe /End /TN "{{test_scheduled_task_stat_path}}\{{test_scheduled_task_stat_name}}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop/aliases b/ansible_collections/community/windows/tests/integration/targets/win_scoop/aliases new file mode 100644 index 000000000..e2eacc2b6 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop/aliases @@ -0,0 +1,3 @@ +shippable/windows/group3 +skip/windows/2012 # Need pwsh 5+ +skip/windows/2012-R2 # Need pwsh 5+ diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scoop/defaults/main.yml new file mode 100644 index 000000000..10bf6204b --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop/defaults/main.yml @@ -0,0 +1,6 @@ +--- +test_scoop_package1: grep +test_scoop_package2: sed +test_scoop_packages: + - '{{ test_scoop_package1 }}' + - '{{ test_scoop_package2 }}' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scoop/tasks/main.yml new file mode 100644 index 000000000..895cdffe2 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop/tasks/main.yml @@ -0,0 +1,8 @@ +--- +- name: ensure test packages are uninstalled + win_scoop: + name: '{{ test_scoop_packages }}' + state: absent + +- name: run tests + include_tasks: tests.yml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_scoop/tasks/tests.yml new file mode 100644 index 000000000..b773e61cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop/tasks/tests.yml @@ -0,0 +1,159 @@ +--- +- name: install package check mode + win_scoop: + name: '{{ test_scoop_package1 }}' + check_mode: yes + register: install_check + +- name: assert install package check mode + assert: + that: + - install_check is changed + - install_check.stdout is not defined + +- name: install package locally + win_scoop: + name: '{{ test_scoop_package1 }}' + register: install_locally + +- name: assert install package locally + assert: + that: + - install_locally is changed + +- name: install package locally (idempotent) + win_scoop: + name: '{{ test_scoop_package1 }}' + register: install_idempotent + +- name: assert install package locally (idempotent) + assert: + that: + - not install_idempotent is changed + +- name: install package globally + win_scoop: + name: '{{ test_scoop_package1 }}' + global: yes + register: install_globally + +- name: assert install package globally + assert: + that: + - install_globally is changed + +- name: install package globally + win_scoop: + name: '{{ test_scoop_package1 }}' + global: yes + register: install_globally_idempotent + +- name: assert install package globally (idempotent) + assert: + that: + - not install_globally_idempotent is changed + +- name: remove package + win_scoop: + name: '{{ test_scoop_package1 }}' + state: absent + register: remove + +- name: assert remove package + assert: + that: + - remove is changed + +- name: remove package (idempotent) + win_scoop: + name: '{{ test_scoop_package1 }}' + state: absent + register: remove_idempotent + +- name: assert remove package (idempotent) + assert: + that: + - not remove_idempotent is changed + +- name: remove package globally + win_scoop: + name: '{{ test_scoop_package1 }}' + state: absent + global: yes + register: remove_globally + +- name: assert remove package + assert: + that: + - remove_globally is changed + +- name: remove package globally (idempotent) + win_scoop: + name: '{{ test_scoop_package1 }}' + state: absent + global: yes + register: remove_globally_idempotent + +- name: assert remove package globally (idempotent) + assert: + that: + - not remove_globally_idempotent is changed + +- name: install package before check mode test + win_scoop: + name: '{{ test_scoop_package1 }}' + +- name: remove package check mode + win_scoop: + name: '{{ test_scoop_package1 }}' + state: absent + check_mode: yes + register: remove_check + +- name: assert remove package check mode + assert: + that: + - remove_check is changed + - remove_check.stdout is not defined + +- name: install multiple packages locally + win_scoop: + name: '{{ test_scoop_packages }}' + register: install_multiple + +- name: assert install multiple packages locally + assert: + that: + - install_multiple is changed + +- name: install multiple packages locally (idempotent) + win_scoop: + name: '{{ test_scoop_packages }}' + register: install_multiple_idempotent + +- name: assert install multiple packages locally (idempotent) + assert: + that: + - not install_multiple_idempotent is changed + +- name: remove multiple packages locally + win_scoop: + name: '{{ test_scoop_packages }}' + state: absent + register: remove_multiple + +- name: assert remove multiple packages + assert: + that: + - remove_multiple is changed + +- name: remove multiple packages locally (idempotent) + win_scoop: + name: '{{ test_scoop_packages }}' + state: absent + register: remove_multiple_idempotent + +- name: remove multiple packages locally (idempotent) + assert: + that: + - not remove_multiple_idempotent is changed diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/aliases b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/aliases new file mode 100644 index 000000000..9a9d0737d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/aliases @@ -0,0 +1,3 @@ +shippable/windows/group2 +skip/windows/2012 # Need pwsh 5+ +skip/windows/2012-R2 # Need pwsh 5+ diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/defaults/main.yml new file mode 100644 index 000000000..c39f462e1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/defaults/main.yml @@ -0,0 +1,9 @@ +--- +test_scoop_bucket1: + name: extras +test_scoop_bucket2: + name: versions + repo: https://github.com/ScoopInstaller/Versions +test_scoop_buckets: + - '{{ test_scoop_bucket1 }}' + - '{{ test_scoop_bucket2 }}' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/tasks/main.yml new file mode 100644 index 000000000..fd7d4dac2 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/tasks/main.yml @@ -0,0 +1,18 @@ +--- +- name: install git + win_scoop: + name: git + +- name: ensure test buckets are removed + win_scoop_bucket: + name: '{{ item.name }}' + state: absent + with_items: '{{ test_scoop_buckets }}' + +- name: run tests + include_tasks: tests.yml + +- name: uninstall git + win_scoop: + name: git + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/tasks/tests.yml new file mode 100644 index 000000000..f98eb4677 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_scoop_bucket/tasks/tests.yml @@ -0,0 +1,86 @@ +--- +- name: add known bucket + win_scoop_bucket: + name: '{{ test_scoop_bucket1.name }}' + register: add_known + +- name: assert add add known bucket + assert: + that: + - add_known is changed + +- name: add known bucket (idempotent) + win_scoop_bucket: + name: '{{ test_scoop_bucket1.name }}' + register: add_known_idempotent + +- name: assert add known bucket (idempotent) + assert: + that: + - not add_known_idempotent is changed + +- name: remove known bucket + win_scoop_bucket: + name: '{{ test_scoop_bucket1.name }}' + state: absent + register: remove_known + +- name: assert remove known bucket + assert: + that: + - remove_known is changed + +- name: remove known bucket (idempotent) + win_scoop_bucket: + name: '{{ test_scoop_bucket1.name }}' + state: absent + register: remove_known_idempotent + +- name: assert remove package + assert: + that: + - not remove_known_idempotent is changed + +- name: add custom bucket + win_scoop_bucket: + name: '{{ test_scoop_bucket2.name }}' + repo: '{{ test_scoop_bucket2.repo }}' + register: add_custom + +- name: assert add add custom bucket + assert: + that: + - add_custom is changed + +- name: add custom bucket (idempotent) + win_scoop_bucket: + name: '{{ test_scoop_bucket2.name }}' + repo: '{{ test_scoop_bucket2.repo }}' + register: add_custom_idempotent + +- name: assert add custom bucket (idempotent) + assert: + that: + - not add_custom_idempotent is changed + +- name: remove custom bucket + win_scoop_bucket: + name: '{{ test_scoop_bucket2.name }}' + state: absent + register: remove_custom + +- name: assert remove custom bucket + assert: + that: + - remove_custom is changed + +- name: remove custom bucket (idempotent) + win_scoop_bucket: + name: '{{ test_scoop_bucket2.name }}' + state: absent + register: remove_custom_idempotent + +- name: assert remove package + assert: + that: + - not remove_custom_idempotent is changed
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_security_policy/aliases b/ansible_collections/community/windows/tests/integration/targets/win_security_policy/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_security_policy/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_security_policy/library/test_win_security_policy.ps1 b/ansible_collections/community/windows/tests/integration/targets/win_security_policy/library/test_win_security_policy.ps1 new file mode 100644 index 000000000..caafbdddb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_security_policy/library/test_win_security_policy.ps1 @@ -0,0 +1,55 @@ +#!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/ansible_collections/community/windows/tests/integration/targets/win_security_policy/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_security_policy/tasks/main.yml new file mode 100644 index 000000000..4ea4a0522 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_security_policy/tasks/main.yml @@ -0,0 +1,71 @@ +--- +- 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 + +- name: get current value for the LegalNoticeText + ansible.windows.win_reg_stat: + path: HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System + name: LegalNoticeText + register: original_legal_notice + +- 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: set LegalNoticeText with non-ASCII chars + ansible.windows.win_regedit: + path: HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System + name: LegalNoticeText + data: "Légal Noticé\r\nNewline" + + - name: run tests + include_tasks: tests.yml + + # https://github.com/ansible-collections/community.windows/issues/153 + - name: get the value for the LegalNoticeText after running tests + ansible.windows.win_reg_stat: + path: HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System + name: LegalNoticeText + register: legal_notice + + - name: verify the non-ASCII chars weren't corrupted + assert: + that: + - legal_notice.value == "Légal Noticé\r\nNewline" + + 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}}" + + - name: reset LegalNoticeText + ansible.windows.win_regedit: + path: HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System + name: LegalNoticeText + data: '{{ original_legal_notice.value }}' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_security_policy/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_security_policy/tasks/tests.yml new file mode 100644 index 000000000..724b6010a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/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 == '' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_shortcut/aliases b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/aliases new file mode 100644 index 000000000..4cd27b3cb --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/aliases @@ -0,0 +1 @@ +shippable/windows/group1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/clean.yml b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/clean.yml new file mode 100644 index 000000000..3a9c9051e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/clean.yml @@ -0,0 +1,37 @@ +# Test code for the file module. +# (c) 2017, Dag Wieers <dag@wieers.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 up Ansible website link + ansible.windows.win_file: + path: '%UserProfile%\Desktop\Ansible website.url' + state: absent + +- name: Clean up Registry Editor shortcut + ansible.windows.win_file: + path: '%Public%\Desktop\Registry Editor.lnk' + state: absent + +- name: Clean up Shell path shortcut + ansible.windows.win_file: + path: '%Public%\bin.lnk' + state: absent + +- name: Clean up Executable shortcut + ansible.windows.win_file: + path: '%Public%\Desktop\cmd.lnk' + state: absent diff --git a/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/main.yml new file mode 100644 index 000000000..cf04ea3b5 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/main.yml @@ -0,0 +1,34 @@ +# Test code for the file module. +# (c) 2017, Dag Wieers <dag@wieers.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 slate + import_tasks: clean.yml + +- name: Test in normal mode + import_tasks: tests.yml + vars: + in_check_mode: no + +- name: Clean slate + import_tasks: clean.yml + +- name: Test in check-mode + import_tasks: tests.yml + vars: + in_check_mode: yes + check_mode: yes diff --git a/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/tests.yml new file mode 100644 index 000000000..79e145c06 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_shortcut/tasks/tests.yml @@ -0,0 +1,367 @@ +# Test code for the file module. +# (c) 2017, Dag Wieers <dag@wieers.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: get current user profile location + raw: $env:USERPROFILE + check_mode: no + register: profile_result + +- set_fact: + profile_dir: '{{ profile_result.stdout_lines[0] }}' + +- name: Add Ansible website link on the desktop + win_shortcut: + src: https://ansible.com/ + dest: '%UserProfile%\Desktop\Ansible website.url' + state: present + register: ansible_website_link_add + +- name: Check there was a change + assert: + that: + - ansible_website_link_add.changed == true + - ansible_website_link_add.dest == profile_dir + '\Desktop\Ansible website.url' + - ansible_website_link_add.src == 'https://ansible.com/' + +- name: Add Ansible website link on the desktop again + win_shortcut: + src: https://ansible.com/ + dest: '%UserProfile%\Desktop\Ansible website.url' + state: present + register: ansible_website_link_add_again + +- name: Check there was no change (normal mode) + assert: + that: + - ansible_website_link_add_again.changed == false + - ansible_website_link_add_again.dest == profile_dir + '\Desktop\Ansible website.url' + - ansible_website_link_add_again.src == 'https://ansible.com/' + when: not in_check_mode + +- name: Check there was a change (check-mode) + assert: + that: + - ansible_website_link_add_again.changed == true + - ansible_website_link_add_again.dest == profile_dir + '\Desktop\Ansible website.url' + - ansible_website_link_add_again.src == 'https://ansible.com/' + when: in_check_mode + +- name: Remove link + win_shortcut: + dest: '%UserProfile%\Desktop\Ansible website.url' + state: absent + register: ansible_website_link_remove + +- name: Check there was a change (normal mode) + assert: + that: + - ansible_website_link_remove.changed == true + - ansible_website_link_remove.dest == profile_dir + '\Desktop\Ansible website.url' + when: not in_check_mode + +- name: Check there was no change (check-mode) + assert: + that: + - ansible_website_link_remove.changed == false + - ansible_website_link_remove.dest == profile_dir + '\Desktop\Ansible website.url' + when: in_check_mode + +- name: Remove link again + win_shortcut: + dest: '%UserProfile%\Desktop\Ansible website.url' + state: absent + register: ansible_website_link_remove_again + +- name: Check there was no change + assert: + that: + - ansible_website_link_remove_again.changed == false + - ansible_website_link_remove_again.dest == profile_dir + '\Desktop\Ansible website.url' + +- name: Add a regedit shortcut on the desktop + win_shortcut: + description: "Registry Editor" + src: regedit.exe + dest: '%Public%\Desktop\Registry Editor.lnk' + state: present + register: regedit_shortcut_add + +- name: Check there was a change + assert: + that: + - regedit_shortcut_add.changed == true + - regedit_shortcut_add.description == 'Registry Editor' + - regedit_shortcut_add.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_add.src == 'C:\\Windows\\regedit.exe' + +- name: Add a regedit shortcut on the desktop again + win_shortcut: + description: "Registry Editor" + src: regedit.exe + dest: '%Public%\Desktop\Registry Editor.lnk' + state: present + register: regedit_shortcut_add_again + +- name: Check there was no change (normal mode) + assert: + that: + - regedit_shortcut_add_again.changed == false + - regedit_shortcut_add_again.description == 'Registry Editor' + - regedit_shortcut_add_again.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_add_again.src == 'C:\\Windows\\regedit.exe' + when: not in_check_mode + +- name: Check there was a change (check-mode) + assert: + that: + - regedit_shortcut_add_again.changed == true + - regedit_shortcut_add_again.description == 'Registry Editor' + - regedit_shortcut_add_again.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_add_again.src == 'C:\\Windows\\regedit.exe' + when: in_check_mode + +- name: Update a regedit shortcut on the desktop + win_shortcut: + description: "Registry Editor" + src: C:\BogusPath\regedit.exe + dest: '%Public%\Desktop\Registry Editor.lnk' + state: present + register: regedit_shortcut_update + +- name: Check there was a change + assert: + that: + - regedit_shortcut_update.changed == true + - regedit_shortcut_update.description == 'Registry Editor' + - regedit_shortcut_update.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_update.src == 'C:\\BogusPath\\regedit.exe' + +- name: Update a regedit shortcut on the desktop again + win_shortcut: + description: "Registry Editor" + src: C:\BogusPath\regedit.exe + dest: '%Public%\Desktop\Registry Editor.lnk' + state: present + register: regedit_shortcut_update_again + +- name: Check there was no change (normal mode) + assert: + that: + - regedit_shortcut_update_again.changed == false + - regedit_shortcut_update_again.description == 'Registry Editor' + - regedit_shortcut_update_again.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_update_again.src == 'C:\\BogusPath\\regedit.exe' + when: not in_check_mode + +- name: Check there was a change (check-mode) + assert: + that: + - regedit_shortcut_update_again.changed == true + - regedit_shortcut_update_again.description == 'Registry Editor' + - regedit_shortcut_update_again.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_update_again.src == 'C:\\BogusPath\\regedit.exe' + when: in_check_mode + +- name: Add an (explicit) icon + win_shortcut: + description: "Registry Editor" + src: C:\Windows\regedit.exe + dest: '%Public%\Desktop\Registry Editor.lnk' + icon: 'C:\Windows\regedit.exe,0' + state: present + register: regedit_shortcut_add_icon + +- name: Check there was a change + assert: + that: + - regedit_shortcut_add_icon.changed == true + - regedit_shortcut_add_icon.description == 'Registry Editor' + - regedit_shortcut_add_icon.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_add_icon.icon == 'C:\\Windows\\regedit.exe,0' + - regedit_shortcut_add_icon.src == 'C:\\Windows\\regedit.exe' + +- name: Add an (explicit) icon again + win_shortcut: + description: "Registry Editor" + src: C:\Windows\regedit.exe + dest: '%Public%\Desktop\Registry Editor.lnk' + icon: 'C:\Windows\regedit.exe,0' + state: present + register: regedit_shortcut_add_icon_again + +- name: Check there was no change (normal mode) + assert: + that: + - regedit_shortcut_add_icon_again.changed == false + - regedit_shortcut_add_icon_again.description == 'Registry Editor' + - regedit_shortcut_add_icon_again.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_add_icon_again.icon == 'C:\\Windows\\regedit.exe,0' + - regedit_shortcut_add_icon_again.src == 'C:\\Windows\\regedit.exe' + when: not in_check_mode + +- name: Check there was a change (check-mode) + assert: + that: + - regedit_shortcut_add_icon_again.changed == true + - regedit_shortcut_add_icon_again.description == 'Registry Editor' + - regedit_shortcut_add_icon_again.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + - regedit_shortcut_add_icon_again.icon == 'C:\\Windows\\regedit.exe,0' + - regedit_shortcut_add_icon_again.src == 'C:\\Windows\\regedit.exe' + when: in_check_mode + +- name: Remove shortcut + win_shortcut: + dest: '%Public%\Desktop\Registry Editor.lnk' + state: absent + register: regedit_shortcut_remove + +- name: Check there was a change (normal mode) + assert: + that: + - regedit_shortcut_remove.changed == true + - regedit_shortcut_remove.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + when: not in_check_mode + +- name: Check there was no change (check-mode) + assert: + that: + - regedit_shortcut_remove.changed == false + - regedit_shortcut_remove.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + when: in_check_mode + +- name: Remove shortcut again + win_shortcut: + dest: '%Public%\Desktop\Registry Editor.lnk' + state: absent + register: regedit_shortcut_remove_again + +- name: Check there was no change + assert: + that: + - regedit_shortcut_remove_again.changed == false + - regedit_shortcut_remove_again.dest == 'C:\\Users\\Public\\Desktop\\Registry Editor.lnk' + +- name: Create shortcut to shell path + win_shortcut: + dest: '%Public%\bin.lnk' + src: shell:RecycleBinFolder + state: present + register: shell_add + +- name: Check there was a change + assert: + that: + - shell_add is changed + - shell_add.dest == 'C:\\Users\\Public\\bin.lnk' + - shell_add.src == 'shell:RecycleBinFolder' + +- name: Create shortcut to shell path again + win_shortcut: + dest: '%Public%\bin.lnk' + src: shell:RecycleBinFolder + state: present + register: shell_add_again + +- name: Check there was no change (normal mode) + assert: + that: + - not shell_add_again is changed + - shell_add_again.src == 'shell:RecycleBinFolder' + when: not in_check_mode + +- name: Check there was a change (check-mode) + assert: + that: + - shell_add_again is changed + when: in_check_mode + +- name: Change shortcut to another shell path + win_shortcut: + dest: '%Public%\bin.lnk' + src: shell:Start Menu + state: present + register: shell_change + +- name: Check there was a change + assert: + that: + - shell_change is changed + - shell_change.src == 'shell:Start Menu' + +- name: Create shortcut to an executable without run as admin + win_shortcut: + dest: '%Public%\Desktop\cmd.lnk' + src: '%SystemRoot%\System32\cmd.exe' + state: present + register: shell_exe_limited + +- name: Get run as admin flag state + ansible.windows.win_shell: | + $shortcut = "$env:Public\Desktop\cmd.lnk" + $flags = [System.BitConverter]::ToUInt32([System.IO.FIle]::ReadAllBytes($shortcut), 20) + ($flags -band 0x00002000) -eq 0x00002000 + register: shell_exe_limited_actual + +- name: Check that run as admin flag wasn't set (normal mode) + assert: + that: + - shell_exe_limited is changed + - not shell_exe_limited_actual.stdout_lines[0]|bool + when: not in_check_mode + +- name: Check that exe shortcut results in a change (check-mode) + assert: + that: + - shell_exe_limited is changed + when: in_check_mode + +- name: Set shortcut to run as admin + win_shortcut: + dest: '%Public%\Desktop\cmd.lnk' + src: '%SystemRoot%\System32\cmd.exe' + run_as_admin: True + state: present + register: shell_exe_admin + +- name: Get run as admin flag state + ansible.windows.win_shell: | + $shortcut = "$env:Public\Desktop\cmd.lnk" + $flags = [System.BitConverter]::ToUInt32([System.IO.FIle]::ReadAllBytes($shortcut), 20) + ($flags -band 0x00002000) -eq 0x00002000 + register: shell_exe_admin_actual + +- name: Check that run as admin flag was set (normal mode) + assert: + that: + - shell_exe_admin is changed + - shell_exe_admin_actual.stdout_lines[0]|bool + when: not in_check_mode + +- name: Set shortcut to run as admin again + win_shortcut: + dest: '%Public%\Desktop\cmd.lnk' + src: '%SystemRoot%\System32\cmd.exe' + run_as_admin: True + state: present + register: shell_exe_admin_again + +- name: Check that set run as admin wasn't changed (normal mode) + assert: + that: + - not shell_exe_admin_again is changed + when: not in_check_mode diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/aliases b/ansible_collections/community/windows/tests/integration/targets/win_snmp/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/cleanup.yml b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/cleanup.yml new file mode 100644 index 000000000..ef446cb73 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/cleanup.yml @@ -0,0 +1,16 @@ +--- + - name: Make sure there are no existing SNMP configuration settings + ansible.windows.win_regedit: + path: "{{ item }}" + state: absent + loop: + - "{{ permitted_managers_key }}" + - "{{ valid_communities_key }}" + + - name: Create skeleton registry keys for SNMP + ansible.windows.win_regedit: + path: "{{ item }}" + state: present + loop: + - "{{ permitted_managers_key }}" + - "{{ valid_communities_key }}" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/cleanup_using_module.yml b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/cleanup_using_module.yml new file mode 100644 index 000000000..472a03e7e --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/cleanup_using_module.yml @@ -0,0 +1,26 @@ +--- + - name: Set no SNMP community or SNMP manager + register: snmp_cleanup + win_snmp: + action: set + community_strings: [] + permitted_managers: [] + + - name: Check registry for no SNMP community + register: snmp_cleanup_reg_community + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: snmp-cleanup + + - name: Check registry for no SNMP manager + register: snmp_cleanup_reg_manager + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 1 + + - name: Asset SNMP set operation results in no remaining SNMP details + assert: + that: + - snmp_cleanup.changed + - snmp_cleanup_reg_community.exists == false + - snmp_cleanup_reg_manager.exists == false diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/main.yml new file mode 100644 index 000000000..09000d6e9 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/main.yml @@ -0,0 +1,8 @@ +--- + - include_tasks: cleanup.yml + - include_tasks: snmp_community.yml + - include_tasks: cleanup.yml + - include_tasks: snmp_managers.yml + - include_tasks: output_only.yml + - include_tasks: cleanup_using_module.yml + - include_tasks: cleanup.yml diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/output_only.yml b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/output_only.yml new file mode 100644 index 000000000..7115da45d --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/output_only.yml @@ -0,0 +1,24 @@ +--- + # Already tested + - name: Add an SNMP manager and an SNMP community + win_snmp: + action: add + community_strings: + - snmp-cleanup + permitted_managers: + - 192.168.1.1 + + - name: Run without options + register: snmp_no_options + win_snmp: + + - name: Assert no changes occurred when no options provided + assert: + that: + - not snmp_no_options.changed + + - name: Assert community strings and permitted managers are correctly returned + assert: + that: + - "'snmp-cleanup' in snmp_no_options.community_strings" + - "'192.168.1.1' in snmp_no_options.permitted_managers" diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/snmp_community.yml b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/snmp_community.yml new file mode 100644 index 000000000..d206d179c --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/snmp_community.yml @@ -0,0 +1,165 @@ +--- + - name: Add initial SNMP community + register: snmp_community + win_snmp: + action: add + community_strings: + - ansible-ro-test + + - name: Check initial SNMP community exists in registry + register: snmp_community_reg + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test + + - name: Assert initial SNMP community is correct + assert: + that: + - snmp_community is changed + - snmp_community_reg.exists + - snmp_community_reg.type == 'REG_DWORD' + - snmp_community_reg.value == 4 + + - name: Add initial SNMP community again + register: snmp_community_again + win_snmp: + action: add + community_strings: + - ansible-ro-test + + - name: Check no change occurred when adding SNMP community again + assert: + that: + - snmp_community_again is not changed + + - name: Add next SNMP community + register: snmp_community_next + win_snmp: + action: add + community_strings: + - ansible-ro-test-next + + - name: Check initial SNMP community still exists in registry + register: snmp_community_reg_orig + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test + + - name: Check next SNMP community exists in registry + register: snmp_community_reg_next + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test-next + + - name: Assert initial SNMP community still exists + assert: + that: + - snmp_community_reg_orig.exists + - snmp_community_reg_orig.type == 'REG_DWORD' + - snmp_community_reg_orig.value == 4 + + - name: Assert next SNMP community exists + assert: + that: + - snmp_community_next is changed + - snmp_community_reg_next.exists + - snmp_community_reg_next.type == 'REG_DWORD' + - snmp_community_reg_next.value == 4 + + - name: Replace SNMP community + register: snmp_community_replace + win_snmp: + action: set + community_strings: + - ansible-ro-test-replace + + - name: Check initial SNMP community does not exist in registry + register: snmp_community_reg_orig_replace + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test + + - name: Check next SNMP community does not exist in registry + register: snmp_community_reg_next_replace + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test-next + + - name: Check replace SNMP community exists in registry + register: snmp_community_reg_replace + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test-replace + + - name: Assert replace SNMP community exists and others are replaced + assert: + that: + - snmp_community_replace is changed + - snmp_community_reg_orig_replace.exists == false + - snmp_community_reg_next_replace.exists == false + - snmp_community_reg_replace.exists + - snmp_community_reg_replace.type == 'REG_DWORD' + - snmp_community_reg_replace.value == 4 + + # This task has already been tested + - name: Add another SNMP community before testing removal + win_snmp: + action: add + community_strings: + - ansible-ro-remove-add + + - name: Remove the replaced SNMP community + register: snmp_community_remove + win_snmp: + action: remove + community_strings: + - ansible-ro-test-replace + + - name: Check replace SNMP community is removed in registry + register: snmp_community_reg_remove + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test-replace + + - name: Check SNMP community that was added for testing removal exists in registry + register: snmp_community_reg_remove_add + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-remove-add + + - name: Assert removal of SNMP community succeeded and next SNMP community remains + assert: + that: + - snmp_community_remove is changed + - snmp_community_reg_remove.exists == false + - snmp_community_reg_remove_add.exists + - snmp_community_reg_remove_add.type == 'REG_DWORD' + - snmp_community_reg_remove_add.value == 4 + + - name: Remove the replaced SNMP community (again) + register: snmp_community_remove + win_snmp: + action: remove + community_strings: + - ansible-ro-test-replace + + - name: Check replace SNMP community is removed in registry (again) + register: snmp_community_reg_remove + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-test-replace + + - name: Check SNMP community that was added for testing removal exists in registry (again) + register: snmp_community_reg_remove_add + ansible.windows.win_reg_stat: + path: "{{ valid_communities_key }}" + name: ansible-ro-remove-add + + - name: Assert removal of SNMP community succeeded and next SNMP community remains (again) + assert: + that: + - snmp_community_remove is not changed + - snmp_community_reg_remove.exists == false + - snmp_community_reg_remove_add.exists + - snmp_community_reg_remove_add.type == 'REG_DWORD' + - snmp_community_reg_remove_add.value == 4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/snmp_managers.yml b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/snmp_managers.yml new file mode 100644 index 000000000..227281d81 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/tasks/snmp_managers.yml @@ -0,0 +1,158 @@ +--- + - name: Add initial SNMP manager + register: snmp_manager + win_snmp: + action: add + permitted_managers: + - 192.168.1.1 + + - name: Check initial SNMP manager exists in registry + register: snmp_manager_reg + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 1 + + - name: Assert initial SNMP manager is correct + assert: + that: + - snmp_manager is changed + - snmp_manager_reg.exists + - snmp_manager_reg.type == 'REG_SZ' + - snmp_manager_reg.value == '192.168.1.1' + + - name: Add initial SNMP manager again + register: snmp_manager_again + win_snmp: + action: add + permitted_managers: + - 192.168.1.1 + + - name: Check no change occurred when adding SNMP manager again + assert: + that: + - snmp_manager_again is not changed + + - name: Add next SNMP manager + register: snmp_manager_next + win_snmp: + action: add + permitted_managers: + - 192.168.1.2 + + - name: Check initial SNMP manager still exists in registry + register: snmp_manager_reg_orig + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 1 + + - name: Check next SNMP manager exists in registry + register: snmp_manager_reg_next + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 2 + + - name: Assert initial SNMP manager still exists + assert: + that: + - snmp_manager_reg_orig.exists + - snmp_manager_reg_orig.type == 'REG_SZ' + - snmp_manager_reg_orig.value == '192.168.1.1' + + - name: Assert next SNMP manager exists + assert: + that: + - snmp_manager_next is changed + - snmp_manager_reg_next.exists + - snmp_manager_reg_next.type == 'REG_SZ' + - snmp_manager_reg_next.value == '192.168.1.2' + + - name: Replace SNMP manager + register: snmp_manager_replace + win_snmp: + action: set + permitted_managers: + - 192.168.1.10 + + - name: Check next SNMP manager does not exist in registry + register: snmp_manager_reg_next_replace + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 2 + + - name: Check replace SNMP manager exists in registry (overrides original slot) + register: snmp_manager_reg_replace + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 1 + + - name: Assert replace SNMP manager exists and others are replaced + assert: + that: + - snmp_manager_replace is changed + - snmp_manager_reg_next_replace.exists == false + - snmp_manager_reg_replace.exists + - snmp_manager_reg_replace.type == 'REG_SZ' + - snmp_manager_reg_replace.value == '192.168.1.10' + + # This task has already been tested + - name: Add another SNMP manager before testing removal + win_snmp: + action: add + permitted_managers: + - 192.168.1.20 + + - name: Remove the replaced SNMP manager + register: snmp_manager_remove + win_snmp: + action: remove + permitted_managers: + - 192.168.1.10 + + - name: Check replace SNMP manager is removed in registry + register: snmp_manager_reg_remove + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 1 + + - name: Check SNMP manager that was added for testing removal exists in registry + register: snmp_manager_reg_remove_add + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 2 + + - name: Assert removal of SNMP manager succeeded and next SNMP manager remains + assert: + that: + - snmp_manager_remove is changed + - snmp_manager_reg_remove.exists == false + - snmp_manager_reg_remove_add.exists + - snmp_manager_reg_remove_add.type == 'REG_SZ' + - snmp_manager_reg_remove_add.value == '192.168.1.20' + + - name: Remove the replaced SNMP manager (again) + register: snmp_manager_remove + win_snmp: + action: remove + permitted_managers: + - 192.168.1.10 + + - name: Check replace SNMP manager is removed in registry (again) + register: snmp_manager_reg_remove + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 1 + + - name: Check SNMP manager that was added for testing removal exists in registry (again) + register: snmp_manager_reg_remove_add + ansible.windows.win_reg_stat: + path: "{{ permitted_managers_key }}" + name: 2 + + - name: Assert removal of SNMP manager succeeded and next SNMP manager remains (again) + assert: + that: + - snmp_manager_remove is not changed + - snmp_manager_reg_remove.exists == false + - snmp_manager_reg_remove_add.exists + - snmp_manager_reg_remove_add.type == 'REG_SZ' + - snmp_manager_reg_remove_add.value == '192.168.1.20' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_snmp/vars/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_snmp/vars/main.yml new file mode 100644 index 000000000..610be839f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_snmp/vars/main.yml @@ -0,0 +1,3 @@ +--- + permitted_managers_key: 'HKLM:\System\CurrentControlSet\services\SNMP\Parameters\PermittedManagers' + valid_communities_key: 'HKLM:\System\CurrentControlSet\services\SNMP\Parameters\ValidCommunities' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_timezone/aliases b/ansible_collections/community/windows/tests/integration/targets/win_timezone/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_timezone/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_timezone/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_timezone/tasks/main.yml new file mode 100644 index 000000000..63f449e46 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_timezone/tasks/main.yml @@ -0,0 +1,19 @@ +- name: Determine if server has tzutil.exe installed + ansible.windows.win_command: tzutil.exe /l + register: tzutil + ignore_errors: yes + +- name: Only run tests if tzutil.exe is installed + when: tzutil.rc == 0 + block: + + - name: Test in normal mode + import_tasks: tests.yml + vars: + in_check_mode: no + + - name: Test in check-mode + import_tasks: tests.yml + vars: + in_check_mode: yes + check_mode: yes diff --git a/ansible_collections/community/windows/tests/integration/targets/win_timezone/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_timezone/tasks/tests.yml new file mode 100644 index 000000000..e03f4f1e1 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_timezone/tasks/tests.yml @@ -0,0 +1,100 @@ +# NOTE: Set to a known starting value, store original +- name: Change starting timezone to GMT + win_timezone: + timezone: GMT Standard Time + register: original + +# NOTE: We don't know if it changed, we don't care +- name: Test GMT timezone + assert: + that: + - original.timezone == 'GMT Standard Time' + +- name: Change timezone to GMT+1 + win_timezone: + timezone: Romance Standard Time + register: romance + +- name: Test GMT+1 timezone + assert: + that: + - romance is changed + - romance.previous_timezone == 'GMT Standard Time' + - romance.timezone == 'Romance Standard Time' + when: not in_check_mode + +- name: Test GMT+1 timezone + assert: + that: + - romance is changed + - romance.previous_timezone == original.timezone + - romance.timezone == 'Romance Standard Time' + when: in_check_mode + +- name: Change timezone to GMT+1 again + win_timezone: + timezone: Romance Standard Time + register: romance + +- name: Test GMT+1 timezone + assert: + that: + - romance is not changed + - romance.previous_timezone == 'Romance Standard Time' + - romance.timezone == 'Romance Standard Time' + when: not in_check_mode + +- name: Test GMT+1 timezone + assert: + that: + - romance is changed + - romance.previous_timezone == original.timezone + - romance.timezone == 'Romance Standard Time' + when: in_check_mode + +- name: Change timezone to GMT+6 + win_timezone: + timezone: Central Standard Time + register: central + +- name: Test GMT-6 timezone + assert: + that: + - central is changed + - central.previous_timezone == 'Romance Standard Time' + - central.timezone == 'Central Standard Time' + when: not in_check_mode + +- name: Test GMT+1 timezone + assert: + that: + - central is changed + - central.previous_timezone == original.timezone + - central.timezone == 'Central Standard Time' + when: in_check_mode + +- name: Change timezone to dstoff + win_timezone: + timezone: Eastern Standard Time_dstoff + register: dstoff_result + +- name: Test dstoff timezone + assert: + that: + - dstoff_result is changed + - dstoff_result.timezone == 'Eastern Standard Time_dstoff' + +- name: Change timezone to GMT+666 + win_timezone: + timezone: Dag's Standard Time + register: dag + ignore_errors: yes + +- name: Test GMT+666 timezone + assert: + that: + - dag is failed + +- name: Restore original timezone + win_timezone: + timezone: '{{ original.timezone }}'
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_toast/aliases b/ansible_collections/community/windows/tests/integration/targets/win_toast/aliases new file mode 100644 index 000000000..ebd7be746 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_toast/aliases @@ -0,0 +1,2 @@ +shippable/windows/group1 +disabled diff --git a/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/main.yml new file mode 100644 index 000000000..735d55b1a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/main.yml @@ -0,0 +1,13 @@ +- name: Set up tests + import_tasks: setup.yml + +- name: Test in normal mode + import_tasks: tests.yml + vars: + in_check_mode: no + +- name: Test in check mode + import_tasks: tests.yml + vars: + in_check_mode: yes + check_mode: yes diff --git a/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/setup.yml b/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/setup.yml new file mode 100644 index 000000000..869bc5f51 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/setup.yml @@ -0,0 +1,27 @@ +- name: Get OS version + ansible.windows.win_shell: '[Environment]::OSVersion.Version.Major' + register: os_version + +- name: Get logged in user count (using explorer exe as a proxy) + ansible.windows.win_shell: (get-process -name explorer -EA silentlyContinue).Count + register: user_count + +- name: debug os_version + debug: + var: os_version + verbosity: 2 + +- name: debug user_count + debug: + var: user_count + verbosity: 2 + +- name: Set fact if toast cannot be made + set_fact: + can_toast: False + when: os_version.stdout|int < 10 + +- name: Set fact if toast can be made + set_fact: + can_toast: True + when: os_version.stdout|int >= 10 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/tests.yml new file mode 100644 index 000000000..d1d4ece10 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_toast/tasks/tests.yml @@ -0,0 +1,106 @@ +- name: Warn user + win_toast: + expire: 10 + msg: Keep calm and carry on. + register: msg_result + ignore_errors: True + +- name: Test msg_result when can_toast is true (normal mode, users) + assert: + that: + - msg_result is not failed + - msg_result.time_taken > 10 + when: + - can_toast == True + - in_check_mode == False + - user_count.stdout|int > 0 + +- name: Test msg_result when can_toast is true (normal mode, no users) + assert: + that: + - msg_result is not failed + - msg_result.time_taken > 0.1 + - msg_result.toast_sent == False + when: + - can_toast == True + - in_check_mode == False + - user_count.stdout|int == 0 + +- name: Test msg_result when can_toast is true (check mode, users) + assert: + that: + - msg_result is not failed + - msg_result.time_taken > 0.1 + when: + - can_toast == True + - in_check_mode == True + +- name: Test msg_result when can_toast is true (check mode, no users) + assert: + that: + - msg_result is not failed + - msg_result.time_taken > 0.1 + - msg_result.toast_sent == False + when: + - can_toast == True + - in_check_mode == True + - user_count.stdout|int == 0 + +- name: Test msg_result when can_toast is false + assert: + that: + - msg_result is failed + when: can_toast == False + +- name: Warn user again + win_toast: + expire: 10 + msg: Keep calm and carry on. + register: msg_result2 + ignore_errors: True + +- name: Test msg_result2 when can_toast is true (normal mode, users) + assert: + that: + - msg_result2 is not failed + - msg_result2.time_taken > 10 + when: + - can_toast == True + - in_check_mode == False + - user_count.stdout|int > 0 + +- name: Test msg_result2 when can_toast is true (normal mode, no users) + assert: + that: + - msg_result2 is not failed + - msg_result2.time_taken > 0.1 + when: + - can_toast == True + - in_check_mode == False + - user_count.stdout|int == 0 + +- name: Test msg_result2 when can_toast is true (check mode, users) + assert: + that: + - msg_result2 is not failed + - msg_result2.time_taken > 0.1 + when: + - can_toast == True + - in_check_mode == False + - user_count.stdout|int > 0 + +- name: Test msg_result2 when can_toast is true (check mode, no users) + assert: + that: + - msg_result2 is not failed + - msg_result2.time_taken > 0.1 + when: + - can_toast == True + - in_check_mode == False + - user_count.stdout|int == 0 + +- name: Test msg_result2 when can_toast is false + assert: + that: + - msg_result2 is failed + when: can_toast == False diff --git a/ansible_collections/community/windows/tests/integration/targets/win_unzip/aliases b/ansible_collections/community/windows/tests/integration/targets/win_unzip/aliases new file mode 100644 index 000000000..4f4664b68 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_unzip/aliases @@ -0,0 +1 @@ +shippable/windows/group5 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_unzip/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_unzip/defaults/main.yml new file mode 100644 index 000000000..52d808d88 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_unzip/defaults/main.yml @@ -0,0 +1 @@ +win_unzip_dir: '{{ remote_tmp_dir }}\win_unzip .ÅÑŚÌβŁÈ [$!@^&test(;)]' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_unzip/files/create_crafty_zip_files.py b/ansible_collections/community/windows/tests/integration/targets/win_unzip/files/create_crafty_zip_files.py new file mode 100644 index 000000000..8845b4862 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_unzip/files/create_crafty_zip_files.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import os +import shutil +import sys +import zipfile + +# Each key is a zip file and the vaule is the list of files that will be created +# and placed in the archive +zip_files = { + 'hat1': [r'hat/..\rabbit.txt'], + 'hat2': [r'hat/..\..\rabbit.txt'], + 'handcuffs': [r'..\..\houidini.txt'], + 'prison': [r'..\houidini.txt'], +} + +# Accept an argument of where to create the files, defaulting to +# the current working directory. +try: + output_dir = sys.argv[1] +except IndexError: + output_dir = os.getcwd() + +if not os.path.isdir(output_dir): + os.mkdir(output_dir) + +os.chdir(output_dir) + +for name, files in zip_files.items(): + # Create the files to go in the zip archive + for entry in files: + dirname = os.path.dirname(entry) + if dirname: + if os.path.isdir(dirname): + shutil.rmtree(dirname) + os.mkdir(dirname) + + with open(entry, 'w') as e: + e.write('escape!\n') + + # Create the zip archive with the files + filename = '%s.zip' % name + if os.path.isfile(filename): + os.unlink(filename) + + with zipfile.ZipFile(filename, 'w') as zf: + for entry in files: + zf.write(entry) + + # Cleanup + if dirname: + shutil.rmtree(dirname) + + for entry in files: + try: + os.unlink(entry) + except OSError: + pass diff --git a/ansible_collections/community/windows/tests/integration/targets/win_unzip/files/create_zip.py b/ansible_collections/community/windows/tests/integration/targets/win_unzip/files/create_zip.py new file mode 100644 index 000000000..41b6ff068 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_unzip/files/create_zip.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys +import tempfile +import zipfile + + +def main(): + filename = b"caf\xc3\xa9.txt" + + with tempfile.NamedTemporaryFile() as temp: + with open(temp.name, mode="wb") as fd: + fd.write(filename) + + with open(sys.argv[1], mode="wb") as fd: + with zipfile.ZipFile(fd, "w") as zip: + zip.write(temp.name, filename.decode('utf-8')) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/windows/tests/integration/targets/win_unzip/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_unzip/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_unzip/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_unzip/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_unzip/tasks/main.yml new file mode 100644 index 000000000..82054d1f4 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_unzip/tasks/main.yml @@ -0,0 +1,171 @@ +- name: create test directory + ansible.windows.win_file: + path: '{{ win_unzip_dir }}\output' + state: directory + +- name: create local zip file with non-ascii chars + script: create_zip.py {{ output_dir + '/win_unzip.zip' | quote }} + delegate_to: localhost + +- name: copy across zip to Windows host + ansible.windows.win_copy: + src: '{{ output_dir }}/win_unzip.zip' + dest: '{{ win_unzip_dir }}\win_unzip.zip' + +- name: unarchive zip (check) + win_unzip: + src: '{{ win_unzip_dir }}\win_unzip.zip' + dest: '{{ win_unzip_dir }}\output' + register: unzip_check + check_mode: yes + +- name: get result of unarchive zip (check) + ansible.windows.win_stat: + path: '{{ win_unzip_dir }}\output\café.txt' + register: unzip_actual_check + +- name: assert result of unarchive zip (check) + assert: + that: + - unzip_check is changed + - not unzip_check.removed + - not unzip_actual_check.stat.exists + +- name: unarchive zip + win_unzip: + src: '{{ win_unzip_dir }}\win_unzip.zip' + dest: '{{ win_unzip_dir }}\output' + register: unzip + +- name: get result of unarchive zip + ansible.windows.slurp: + path: '{{ win_unzip_dir }}\output\café.txt' + register: unzip_actual + +- name: assert result of unarchive zip + assert: + that: + - unzip is changed + - not unzip.removed + - unzip_actual.content | b64decode == 'café.txt' + +# Module is not idempotent, will always change without creates +- name: unarchive zip again without creates + win_unzip: + src: '{{ win_unzip_dir }}\win_unzip.zip' + dest: '{{ win_unzip_dir }}\output' + register: unzip_again + +- name: assert unarchive zip again without creates + assert: + that: + - unzip_again is changed + - not unzip_again.removed + +- name: unarchive zip with creates + win_unzip: + src: '{{ win_unzip_dir }}\win_unzip.zip' + dest: '{{ win_unzip_dir }}\outout' + creates: '{{ win_unzip_dir }}\output\café.txt' + register: unzip_again_creates + +- name: assert unarchive zip with creates + assert: + that: + - not unzip_again_creates is changed + - not unzip_again_creates.removed + +- name: unarchive zip with delete (check) + win_unzip: + src: '{{ win_unzip_dir }}\win_unzip.zip' + dest: '{{ win_unzip_dir }}\output' + delete_archive: yes + register: unzip_delete_check + check_mode: yes + +- name: get result of unarchive zip with delete (check) + ansible.windows.win_stat: + path: '{{ win_unzip_dir }}\win_unzip.zip' + register: unzip_delete_actual_check + +- name: assert unarchive zip with delete (check) + assert: + that: + - unzip_delete_check is changed + - unzip_delete_check.removed + - unzip_delete_actual_check.stat.exists + +- name: unarchive zip with delete + win_unzip: + src: '{{ win_unzip_dir }}\win_unzip.zip' + dest: '{{ win_unzip_dir }}\output' + delete_archive: yes + register: unzip_delete + +- name: get result of unarchive zip with delete + ansible.windows.win_stat: + path: '{{ win_unzip_dir }}\win_unzip.zip' + register: unzip_delete_actual + +- name: assert unarchive zip with delete + assert: + that: + - unzip_delete is changed + - unzip_delete.removed + - not unzip_delete_actual.stat.exists + +# Path traversal tests (CVE-2020-1737) +- name: Create zip files + script: create_crafty_zip_files.py {{ output_dir }} + delegate_to: localhost + +- name: Copy zip files to Windows host + ansible.windows.win_copy: + src: "{{ output_dir }}/{{ item }}.zip" + dest: "{{ win_unzip_dir }}/" + loop: + - hat1 + - hat2 + - handcuffs + - prison + +- name: Perform first trick + win_unzip: + src: '{{ win_unzip_dir }}\hat1.zip' + dest: '{{ win_unzip_dir }}\output' + register: hat_trick1 + +- name: Check for file + ansible.windows.win_stat: + path: '{{ win_unzip_dir }}\output\rabbit.txt' + register: rabbit + +- name: Perform next tricks (which should all fail) + win_unzip: + src: '{{ win_unzip_dir }}\{{ item }}.zip' + dest: '{{ win_unzip_dir }}\output' + ignore_errors: yes + register: escape + loop: + - hat2 + - handcuffs + - prison + +- name: Search for files + ansible.windows.win_find: + recurse: yes + paths: + - '{{ win_unzip_dir }}' + patterns: + - '*houdini.txt' + - '*rabbit.txt' + register: files + +- name: Check results + assert: + that: + - rabbit.stat.exists + - hat_trick1 is success + - escape.results | map(attribute='failed') | unique | list == [True] + - files.matched == 1 + - files.files[0]['filename'] == 'rabbit.txt' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_user_profile/aliases b/ansible_collections/community/windows/tests/integration/targets/win_user_profile/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_user_profile/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_user_profile/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_user_profile/tasks/main.yml new file mode 100644 index 000000000..67a9bccc9 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_user_profile/tasks/main.yml @@ -0,0 +1,42 @@ +--- +- name: set custom user facts + set_fact: + test_username: ansible_test + test_password: '{{ "password123!" + lookup("password", "/dev/null chars=ascii_letters,digits length=9") }}' + +- name: create test account + ansible.windows.win_user: + name: '{{ test_username }}' + password: '{{ test_password }}' + state: present + register: test_username_info + +- block: + - name: check if profile exists + ansible.windows.win_stat: + path: C:\temp\{{ test_username }} + register: profile_path + + - name: assert that profile doesn't exist before the test + assert: + that: + - not profile_path.stat.exists + + - name: run tests + include_tasks: tests.yml + + always: + - name: remove test account + ansible.windows.win_user: + name: '{{ test_username }}' + state: absent + + - name: remove test account profile + win_user_profile: + name: '{{ item }}' + state: absent + remove_multiple: True + with_items: + - '{{ test_username }}' + - '{{ test_username }}.000' + - test_username_profile diff --git a/ansible_collections/community/windows/tests/integration/targets/win_user_profile/tasks/tests.yml b/ansible_collections/community/windows/tests/integration/targets/win_user_profile/tasks/tests.yml new file mode 100644 index 000000000..52e8754b9 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_user_profile/tasks/tests.yml @@ -0,0 +1,374 @@ +--- +- name: create profile (check mode) + win_user_profile: + username: '{{ test_username }}' + state: present + register: create_profile_check + check_mode: True + +- name: check if profile was created (check mode) + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: create_profile_actual_check + +- name: assert create profile (check mode) + assert: + that: + - create_profile_check is changed + - create_profile_check.path|lower == "c:\\users\\" + test_username + - not create_profile_actual_check.stat.exists + +- name: create profile + win_user_profile: + username: '{{ test_username }}' + state: present + register: create_profile + +- name: check if profile was created + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: create_profile_actual + +- name: assert create profile + assert: + that: + - create_profile is changed + - create_profile.path|lower == "c:\\users\\" + test_username + - create_profile_actual.stat.exists + +- name: create profile (idempotent) + win_user_profile: + username: '{{ test_username }}' + state: present + register: create_profile_again + +- name: assert create profile (idempotent) + assert: + that: + - not create_profile_again is changed + - create_profile_again.path|lower == "c:\\users\\" + test_username + +- name: remove profile (check mode) + win_user_profile: + username: '{{ test_username }}' + state: absent + register: remove_profile_check + check_mode: True + +- name: check if profile was removed (check mode) + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: remove_profile_actual_check + +- name: assert remove profile (check mode) + assert: + that: + - remove_profile_check is changed + - remove_profile_check.path|lower == "c:\\users\\" + test_username + - remove_profile_actual_check.stat.exists + +- name: remove profile + win_user_profile: + username: '{{ test_username }}' + state: absent + register: remove_profile + +- name: check if profile was removed + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: remove_profile_actual + +- name: assert remove profile + assert: + that: + - remove_profile is changed + - remove_profile.path|lower == "c:\\users\\" + test_username + - not remove_profile_actual.stat.exists + +- name: remove profile (idempotent) + win_user_profile: + username: '{{ test_username }}' + state: absent + register: remove_profile_again + +- name: assert remove profile (idempotent) + assert: + that: + - not remove_profile_again is changed + - remove_profile_again.path == None + +- name: create profile with specific base path + win_user_profile: + username: '{{ test_username }}' + name: test_username_profile + state: present + register: create_profile_basename + +- name: check if profile with specific base path was created + ansible.windows.win_stat: + path: C:\Users\test_username_profile + register: create_profile_basename_actual + +- name: assert create profile with specific base path + assert: + that: + - create_profile_basename is changed + - create_profile_basename.path|lower == "c:\\users\\test_username_profile" + - create_profile_basename_actual.stat.exists + +- name: remove profile with specific base path + win_user_profile: + username: '{{ test_username }}' + state: absent + register: remove_profile_basename + +- name: check if profile with specific base path was removed + ansible.windows.win_stat: + path: C:\Users\test_username_profile + register: remove_profile_basename_actual + +- name: assert remove profile with specific base path + assert: + that: + - remove_profile_basename is changed + - remove_profile_basename.path|lower == "c:\\users\\test_username_profile" + - not remove_profile_basename_actual.stat.exists + +- name: create dummy profile folder + ansible.windows.win_file: + path: C:\Users\{{ test_username }} + state: directory + +- block: + - name: create profile folder with conflict (check mode) + win_user_profile: + username: '{{ test_username }}' + state: present + register: create_profile_conflict_check + check_mode: True + + - name: get result of create profile folder with conflict (check mode) + ansible.windows.win_stat: + path: C:\Users\{{ test_username }}.000 + register: create_profile_conflict_actual_check + + - name: assert create profile folder with conflict (check mode) + assert: + that: + - create_profile_conflict_check is changed + # The check mode path calc is dumb, doesn't check for conflicts + - create_profile_conflict_check.path|lower == "c:\\users\\" + test_username + - not create_profile_conflict_actual_check.stat.exists + + - name: create profile folder with conflict + win_user_profile: + username: '{{ test_username }}' + state: present + register: create_profile_conflict + + - name: get result of create profile with conflict + ansible.windows.win_stat: + path: C:\Users\{{ test_username }}.000 + register: create_profile_conflict_actual + + - name: assert create profile folder with conflict + assert: + that: + - create_profile_conflict is changed + - create_profile_conflict.path|lower == "c:\\users\\" + test_username + ".000" + - create_profile_conflict_actual.stat.exists + + - name: remove profile with conflict + win_user_profile: + username: '{{ test_username }}' + state: absent + register: remove_profile_conflict + + - name: get result of profile folder after remove + ansible.windows.win_stat: + path: C:\Users\{{ test_username }}.000 + register: remove_profile_conflict_actual + + - name: get result of dummy folder after remove + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: remove_profile_conflict_dummy + + - name: assert remove profile with conflict + assert: + that: + - remove_profile_conflict is changed + - remove_profile_conflict.path|lower == "c:\\users\\" + test_username + ".000" + - not remove_profile_conflict_actual.stat.exists + - remove_profile_conflict_dummy.stat.exists + + always: + - name: remove dummy profile folder + ansible.windows.win_file: + path: C:\Users\{{ test_username }} + state: absent + +- name: create profile for deleted user by sid test + win_user_profile: + username: '{{ test_username_info.sid }}' + state: present + +- name: delete user for deleted user with sid test + ansible.windows.win_user: + name: '{{ test_username }}' + state: absent + +- name: remove profile for remove profile by sid test + win_user_profile: + username: '{{ test_username_info.sid }}' + state: absent + register: remove_profile_deleted_sid + +- name: check if profile was deleted for deleted user using a SID + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: remove_profile_deleted_sid_actual + +- name: assert remove profile for deleted user using a SID + assert: + that: + - remove_profile_deleted_sid is changed + - remove_profile_deleted_sid.path|lower == "c:\\users\\" + test_username + - not remove_profile_deleted_sid_actual.stat.exists + +- name: recreate user for deleted user by name test + ansible.windows.win_user: + name: '{{ test_username }}' + password: '{{ test_password }}' + state: present + register: test_orphan_user1 + +- name: create profile for deleted user by name test + win_user_profile: + username: '{{ test_username }}' + state: present + +- name: delete user for remove profile by name test + ansible.windows.win_user: + name: '{{ test_username }}' + state: absent + +- name: remove profile for deleted user using a name + win_user_profile: + name: '{{ test_username }}' + state: absent + register: remove_profile_deleted_name + +- name: check if profile was deleted for deleted user using a name + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: remove_profile_deleted_name_actual + +- name: assert remove profile for deleted user using a name + assert: + that: + - remove_profile_deleted_name is changed + - remove_profile_deleted_name.path|lower == "c:\\users\\" + test_username + - not remove_profile_deleted_name_actual.stat.exists + +- name: remove profile for deleted user using a name (idempotent) + win_user_profile: + name: '{{ test_username }}' + state: absent + register: remove_profile_deleted_name_again + +- name: assert remove profile for deleted user using a name (idempotent) + assert: + that: + - not remove_profile_deleted_name_again is changed + +- name: recreate user for remove multiple user test + ansible.windows.win_user: + name: '{{ test_username }}' + password: '{{ test_password }}' + state: present + register: test_orphan_user1 + +- name: create new profile for remove multiple user test + win_user_profile: + username: '{{ test_username }}' + state: present + register: orphan_user1_profile + +- name: remove user 1 for remove multiple user test + ansible.windows.win_user: + name: '{{ test_username }}' + state: absent + +# win_file has issues with paths exceeding MAX_PATH, need to use rmdir instead +- name: remove profile folder for user 1 + ansible.windows.win_shell: rmdir /S /Q {{ orphan_user1_profile.path}} + args: + executable: cmd.exe + +- name: create user 2 for remove multiple user test + ansible.windows.win_user: + name: '{{ test_username }}' + password: '{{ test_password }}' + state: present + register: test_orphan_user2 + +- name: create new profile for orphan user 2 + win_user_profile: + username: '{{ test_username }}' + state: present + register: orphan_user2_profile + +- name: remove orphan user 2 for remove multiple user test + ansible.windows.win_user: + name: '{{ test_username }}' + state: present + +- name: fail to remove multiple profiles without flag + win_user_profile: + name: '{{ test_username }}' + state: absent + register: fail_remove_multiple + ignore_errors: True + +- name: check if profile was removed + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: fail_remove_multiple_actual + +- name: assert that profile was not actually deleted + assert: + that: + - fail_remove_multiple.msg == "Found multiple profiles matching the path 'C:\\Users\\" + test_username + "', set 'remove_multiple=True' to remove all the profiles for this match" + - fail_remove_multiple_actual.stat.exists + +- name: remove multiple profiles + win_user_profile: + name: '{{ test_username }}' + state: absent + remove_multiple: True + register: remove_multiple + +- name: get result of remove multiple profiles + ansible.windows.win_stat: + path: C:\Users\{{ test_username }} + register: remove_multiple_actual + +- name: check that orphan user 1 reg profile has been removed + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\{{ test_orphan_user1.sid }} + register: remove_orphan1_actual + +- name: check that orphan user 2 reg profile has been removed + ansible.windows.win_reg_stat: + path: HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\{{ test_orphan_user2.sid }} + register: remove_orphan2_actual + +- name: assert remove multiple profiles + assert: + that: + - remove_multiple is changed + - remove_multiple.path|lower == "c:\\users\\" + test_username + - not remove_multiple_actual.stat.exists + - not remove_orphan1_actual.exists + - not remove_orphan2_actual.exists diff --git a/ansible_collections/community/windows/tests/integration/targets/win_wait_for_process/aliases b/ansible_collections/community/windows/tests/integration/targets/win_wait_for_process/aliases new file mode 100644 index 000000000..215e0b069 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_wait_for_process/aliases @@ -0,0 +1 @@ +shippable/windows/group4 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_wait_for_process/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_wait_for_process/tasks/main.yml new file mode 100644 index 000000000..3ec692cb4 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_wait_for_process/tasks/main.yml @@ -0,0 +1,201 @@ +--- +- name: Get powershell version + ansible.windows.win_shell: $PSVersionTable.PSVersion.Major + register: powershell_version + +- name: Ensure Spooler service is started + ansible.windows.win_service: + name: Spooler + state: started + +- name: Wait for non-existing process to not exist + win_wait_for_process: + process_name_exact: + - ansible_foobar + timeout: 30 + state: absent + register: absent_nonexisting_process + +- assert: + that: + - absent_nonexisting_process is success + - absent_nonexisting_process is not changed + - absent_nonexisting_process.elapsed > 0 + - absent_nonexisting_process.elapsed < 30 + - absent_nonexisting_process.matched_processes|length == 0 + +- name: Wait for non-existing process until timeout + win_wait_for_process: + process_name_exact: ansible_foobar + timeout: 30 + state: present + ignore_errors: yes + register: present_nonexisting_process + +- assert: + that: + - present_nonexisting_process is failed + - present_nonexisting_process is not changed + - present_nonexisting_process.elapsed > 30 + - present_nonexisting_process.msg == 'Timed out while waiting for process(es) to start' + - present_nonexisting_process.matched_processes|length == 0 + +- name: Wait for existing process to exist + win_wait_for_process: + process_name_exact: spoolsv + timeout: 30 + state: present + register: present_existing_process + +- assert: + that: + - present_existing_process is success + - present_existing_process is not changed + - present_existing_process.elapsed > 0 + - present_existing_process.elapsed < 30 + - present_existing_process.matched_processes|length > 0 + +- name: Wait for existing process until timeout + win_wait_for_process: + process_name_exact: + - spoolsv + timeout: 30 + state: absent + ignore_errors: yes + register: absent_existing_process + +- assert: + that: + - absent_existing_process is failed + - absent_existing_process is not changed + - absent_existing_process.elapsed > 30 + - absent_existing_process.matched_processes|length > 0 + - absent_existing_process.msg == 'Timeout while waiting for process(es) to stop' + +- name: Wait for existing process to exist (using owner) + win_wait_for_process: + process_name_exact: spoolsv + owner: SYSTEM + timeout: 30 + state: present + ignore_errors: yes + register: present_existing_owner_process + +- assert: + that: + - present_existing_owner_process is success + - present_existing_owner_process is not changed + - present_existing_owner_process.elapsed > 0 + - present_existing_owner_process.elapsed < 30 + - present_existing_owner_process.matched_processes|length > 0 + when: powershell_version.stdout_lines[0]|int >= 4 + +- assert: + that: + - present_existing_owner_process is failed + - present_existing_owner_process is not changed + - present_existing_owner_process.elapsed == 0 + - present_existing_owner_process.matched_processes|length == 0 + - present_existing_owner_process.msg == "This version of Powershell does not support filtering processes by 'owner'." + when: powershell_version.stdout_lines[0]|int < 4 + +- name: Wait for Spooler service to stop + win_wait_for_process: + process_name_exact: + - spoolsv + - other_process # Tests that just 1 needs to match + timeout: 60 + state: absent + async: 30 + poll: 0 + register: spoolsv_process + +- name: Stop the Spooler service + ansible.windows.win_service: + name: Spooler + force_dependent_services: yes + state: stopped + +- name: Check on async task + async_status: + jid: '{{ spoolsv_process.ansible_job_id }}' + until: absent_spoolsv_process is finished + retries: 20 + register: absent_spoolsv_process + +- assert: + that: + - absent_spoolsv_process is success + - absent_spoolsv_process is not changed + - absent_spoolsv_process is finished + - absent_spoolsv_process.elapsed > 0 + - absent_spoolsv_process.elapsed < 30 + - absent_spoolsv_process.matched_processes|length == 1 + +- name: Wait for Spooler service to start + win_wait_for_process: + process_name_exact: spoolsv + timeout: 60 + state: present + async: 60 + poll: 0 + register: spoolsv_process + +- name: Start the spooler service + ansible.windows.win_service: + name: Spooler + force_dependent_services: yes + state: started + +- name: Check on async task + async_status: + jid: '{{ spoolsv_process.ansible_job_id }}' + until: present_spoolsv_process is finished + retries: 10 + register: present_spoolsv_process + +- assert: + that: + - present_spoolsv_process is success + - present_spoolsv_process is not changed + - present_spoolsv_process is finished + - present_spoolsv_process.elapsed > 0 + - present_spoolsv_process.elapsed < 60 + - present_spoolsv_process.matched_processes|length == 1 + +- name: Start a new long-running process + ansible.windows.win_shell: | + Start-Sleep -Seconds 15 + async: 40 + poll: 0 + register: sleep_pid + +- name: Wait for PID to start + win_wait_for_process: + pid: '{{ sleep_pid.ansible_async_watchdog_pid }}' + timeout: 20 + state: present + register: present_sleep_pid + +- assert: + that: + - present_sleep_pid is success + - present_sleep_pid is not changed + - present_sleep_pid.elapsed > 0 + - present_sleep_pid.elapsed < 15 + - present_sleep_pid.matched_processes|length == 1 + +- name: Wait for PID to stop + win_wait_for_process: + pid: '{{ sleep_pid.ansible_async_watchdog_pid }}' + timeout: 20 + state: absent + register: absent_sleep_pid + +- assert: + that: + - absent_sleep_pid is success + - absent_sleep_pid is not changed + - absent_sleep_pid.elapsed > 0 + - absent_sleep_pid.elapsed < 15 + - absent_sleep_pid.matched_processes|length == 1 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_wakeonlan/aliases b/ansible_collections/community/windows/tests/integration/targets/win_wakeonlan/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_wakeonlan/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_wakeonlan/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_wakeonlan/tasks/main.yml new file mode 100644 index 000000000..169362b00 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_wakeonlan/tasks/main.yml @@ -0,0 +1,9 @@ +- name: Send a magic Wake-on-LAN packet to 00:00:5E:00:53:66 + win_wakeonlan: + mac: 00:00:5E:00:53:66 + broadcast: 192.0.2.255 + +- name: Send a magic Wake-On-LAN packet on port 9 to 00-00-5E-00-53-66 + win_wakeonlan: + mac: 00-00-5E-00-53-66 + port: 9 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_xml/aliases b/ansible_collections/community/windows/tests/integration/targets/win_xml/aliases new file mode 100644 index 000000000..3cf5b97e8 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_xml/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/ansible_collections/community/windows/tests/integration/targets/win_xml/files/books.xml b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/books.xml new file mode 100644 index 000000000..e38ee15d4 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/books.xml @@ -0,0 +1,10 @@ +<?xml version='1.0' encoding='utf-8'?> +<books> + <works lang="en"> + <title lang="en" isbn13="123412341234X">A Great Book</title> + <title lang="en" isbn13="1234109823400">Best Book Ever</title> + <title lang="en" isbn13="123412121234X">Worst Book Ever</title> + <title lang="en" isbn13="423412341234X">Another Book</title> + <title lang="en" isbn13="523412341234X">Worst Book Ever Two</title> + </works> +</books> diff --git a/ansible_collections/community/windows/tests/integration/targets/win_xml/files/config.xml b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/config.xml new file mode 100644 index 000000000..b5241b351 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/config.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<config> + <string key="foo">bar</string> + <setting></setting> +</config> diff --git a/ansible_collections/community/windows/tests/integration/targets/win_xml/files/log4j.xml b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/log4j.xml new file mode 100644 index 000000000..54b76cf7f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/log4j.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> +<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false"> + <appender name="stdout" class="org.apache.log4j.ConsoleAppender" > + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %5p %c{2}: %m%n"/> + </layout> + </appender> + + <appender name="file" class="org.apache.log4j.DailyRollingFileAppender"> + <param name="append" value="true" /> + <param name="encoding" value="UTF-8" /> + <param name="file" value="mylogfile.log" /> + <param name="DatePattern" value="'.'yyyy-MM-dd" /> + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="[%-25d{ISO8601}] %-5p %x %C{1} -- %m\n" /> + </layout> + </appender> + + <logger name="org.springframework.security.web.FilterChainProxy" additivity="false"> + <level value="error"/> + <appender-ref ref="file" /> + </logger> + + <logger name="org.springframework.security.web.context.HttpSessionSecurityContextRepository" additivity="false"> + <level value="error"/> + <appender-ref ref="file" /> + </logger> + + <logger name="org.springframework.security.web.context.SecurityContextPersistenceFilter" additivity="false"> + <level value="error"/> + <appender-ref ref="file" /> + </logger> + + <logger name="org.springframework.security.web.access.intercept" additivity="false"> + <level value="error"/> + <appender-ref ref="stdout" /> + </logger> + + <logger name="org.apache.commons.digester" additivity="false"> + <level value="info"/> + <appender-ref ref="stdout" /> + </logger> + + <root> + <priority value="debug"/> + <appender-ref ref="stdout"/> + </root> +</log4j:configuration> diff --git a/ansible_collections/community/windows/tests/integration/targets/win_xml/files/plane.zip b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/plane.zip Binary files differnew file mode 100644 index 000000000..8157182aa --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_xml/files/plane.zip diff --git a/ansible_collections/community/windows/tests/integration/targets/win_xml/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_xml/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_xml/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_xml/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_xml/tasks/main.yml new file mode 100644 index 000000000..b51dc563f --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_xml/tasks/main.yml @@ -0,0 +1,361 @@ +# test code for the Windows xml module +# (c) 2017, Richard Levenberg <richard.levenberg@cosocloud.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: copy a test .xml file + ansible.windows.win_copy: + src: config.xml + dest: "{{ remote_tmp_dir }}\\config.xml" + +- name: add an element that only has a text child node + win_xml: + path: "{{ remote_tmp_dir }}\\config.xml" + fragment: '<string key="answer">42</string>' + xpath: '/config' + register: element_add_result + +- name: check element add result + assert: + that: + - element_add_result is changed + +- name: try to add the element that only has a text child node again + win_xml: + path: "{{ remote_tmp_dir }}\\config.xml" + fragment: '<string key="answer">42</string>' + xpath: '/config' + register: element_add_result_second + +- name: check element add result + assert: + that: + - not element_add_result_second is changed + +- name: copy a test log4j.xml + ansible.windows.win_copy: + src: log4j.xml + dest: "{{ remote_tmp_dir }}\\log4j.xml" + +- name: change an attribute to fatal logging + win_xml: + path: "{{ remote_tmp_dir }}\\log4j.xml" + xpath: '/log4j:configuration/logger[@name="org.apache.commons.digester"]/level' + type: attribute + attribute: 'value' + fragment: 'FATAL' + +- name: try to change the attribute again + win_xml: + path: "{{ remote_tmp_dir }}\\log4j.xml" + xpath: '/log4j:configuration/logger[@name="org.apache.commons.digester"]/level' + type: attribute + attribute: 'value' + fragment: 'FATAL' + register: attribute_changed_result + +- name: check attribute change result + assert: + that: + - attribute_changed_result is not changed + +- name: try to add a new attribute + win_xml: + path: "{{ remote_tmp_dir }}\\config.xml" + xpath: '/config/string[@key="foo"]' + type: attribute + attribute: spam + fragment: 'ham' + register: element_add_attribute_result + +- name: check element add attribute result + assert: + that: + - element_add_attribute_result is changed + +- name: try to set the added attribute to the same value again + win_xml: + path: "{{ remote_tmp_dir }}\\config.xml" + xpath: '/config/string[@key="foo"]' + type: attribute + attribute: spam + fragment: 'ham' + register: element_add_attribute_result_second + +- name: check element add attribute result + assert: + that: + - not element_add_attribute_result_second is changed + +- name: try to add a new element in empty element + win_xml: + path: "{{ remote_tmp_dir }}\\config.xml" + xpath: '/config/setting' + fragment: "<property />" + type: element + register: element_add_empty_element + +- name: check element add in empty element result + assert: + that: + - element_add_empty_element is changed + +- name: try to add a new element in empty element again + win_xml: + path: "{{ remote_tmp_dir }}\\config.xml" + xpath: '/config/setting' + fragment: "<property />" + type: element + register: element_add_empty_element_second + +- name: check element add in empty element result + assert: + that: + - not element_add_empty_element_second is changed + +# This testing is for https://github.com/ansible/ansible/issues/48471 +# The issue was that an .xml with no encoding declaration, but a UTF8 BOM +# with some UTF-8 characters was being written out with garbage characters. +# The characters added by win_xml were not UTF-8 characters. + +- name: copy test files (https://github.com/ansible/ansible/issues/48471) + ansible.windows.win_copy: + src: plane.zip + dest: "{{ remote_tmp_dir }}\\plane.zip" + +- name: unarchive the test files + win_unzip: + src: "{{ remote_tmp_dir }}\\plane.zip" + dest: "{{ remote_tmp_dir }}\\" + +- name: change a text value in a file with UTF8 BOM and armenian characters in the description + win_xml: + path: "{{ remote_tmp_dir }}\\plane-utf8-bom-armenian-characters.xml" + xpath: '/plane/year' + type: text + fragment: '1988' + +- name: register the sha1 of the new file + ansible.windows.win_stat: + path: "{{ remote_tmp_dir }}\\plane-utf8-bom-armenian-characters.xml" + get_checksum: yes + register: sha1_checksum + +- name: verify the checksum + assert: + that: + - sha1_checksum.stat.checksum == 'e3e18c3066e1bfce9a5cf87c81353fa174440944' + +- name: change a text value in a file with UTF8 BOM and armenian characters in the description + win_xml: + path: "{{ remote_tmp_dir }}\\plane-utf8-bom-armenian-characters.xml" + xpath: '/plane/year' + type: text + fragment: '1989' + backup: yes + register: test_backup + +- name: check backup_file + ansible.windows.win_stat: + path: '{{ test_backup.backup_file }}' + register: backup_file + +- name: Check backup_file + assert: + that: + - test_backup is changed + - backup_file.stat.exists == true + +- name: change a text value in a file with UTF-16 BE BOM and Chinese characters in the description + win_xml: + path: "{{ remote_tmp_dir }}\\plane-utf16be-bom-chinese-characters.xml" + xpath: '/plane/year' + type: text + fragment: '1988' + +- name: register the sha1 of the new file + ansible.windows.win_stat: + path: "{{ remote_tmp_dir }}\\plane-utf16be-bom-chinese-characters.xml" + get_checksum: yes + register: sha1_checksum + +- name: verify the checksum + assert: + that: + - sha1_checksum.stat.checksum == 'de86f79b409383447cf4cf112b20af8ffffcfdbf' + +# features added ansible 2.8 +# count + +- name: count logger nodes in log4j.xml + win_xml: + path: "{{ remote_tmp_dir }}\\log4j.xml" + xpath: //logger + count: yes + register: logger_node_count + +- name: verify node count + assert: + that: + - logger_node_count.count == 5 + +# multiple attribute change +- name: ensure //logger/level value attributes are set to debug + win_xml: + path: "{{ remote_tmp_dir }}\\log4j.xml" + xpath: '//logger/level[@value="error"]' + type: attribute + attribute: value + fragment: debug + count: yes + register: logger_level_value_attrs + +- name: verify //logger/level value attributes + assert: + that: + - logger_level_value_attrs.count == 4 + - logger_level_value_attrs.changed == true + - logger_level_value_attrs.msg == 'attribute changed' + +- name: ensure //logger/level value attributes are set to debug (idempotency) + win_xml: + path: "{{ remote_tmp_dir }}\\log4j.xml" + xpath: '//logger/level[@value="error"]' + type: attribute + attribute: value + fragment: debug + count: yes + register: logger_level_value_attrs_again + +- name: verify //logger/level value attributes again (idempotency) + assert: + that: + - logger_level_value_attrs_again.count == 0 + - logger_level_value_attrs_again.changed == false + - logger_level_value_attrs_again.msg == 'The supplied xpath did not match any nodes. If this is unexpected, check your xpath is valid for the xml file at supplied path.' + +# multiple text nodes +- name: ensure test books.xml is present + ansible.windows.win_copy: + src: books.xml + dest: '{{ remote_tmp_dir }}\books.xml' + +- name: demonstrate multi text replace by replacing all title text elements + win_xml: + path: '{{ remote_tmp_dir }}\books.xml' + xpath: //works/title + type: text + fragment: _TITLE_TEXT_REMOVED_BY_WIN_XML_MODULE_ + count: yes + register: multi_text + +- name: verify multi text change + assert: + that: + - multi_text.changed == true + - multi_text.count == 5 + - multi_text.msg == 'text changed' + +- name: demonstrate multi text replace by replacing all title text elements again (idempotency) + win_xml: + path: '{{ remote_tmp_dir }}\books.xml' + xpath: //works/title + type: text + fragment: _TITLE_TEXT_REMOVED_BY_WIN_XML_MODULE_ + count: yes + register: multi_text_again + +- name: verify multi text again change (idempotency) + assert: + that: + - multi_text_again.changed == false + - multi_text_again.count == 5 + - multi_text_again.msg == 'not changed' + +# multiple element + +#- name: ensure a fresh test books.xml is present +# ansible.windows.win_copy: +# src: books.xml +# dest: '{{ remote_tmp_dir }}\books.xml' + +- name: demonstrate multi element should append new information element from fragment + win_xml: + path: '{{ remote_tmp_dir }}\books.xml' + xpath: //works/title + type: element + fragment: <information>This element added by ansible</information> + count: yes + register: multi_element + +- name: verify multi element + assert: + that: + - multi_element.changed == true + - multi_element.count == 5 + - multi_element.msg == 'element changed' + +- name: demonstrate multi element unchanged (idempotency) + win_xml: + path: '{{ remote_tmp_dir }}\books.xml' + xpath: //works/title + type: element + fragment: <information>This element added by ansible</information> + count: yes + register: multi_element_again + +- name: verify multi element again (idempotency) + assert: + that: + - multi_element_again.changed == false + - multi_element_again.count == 5 + - multi_element_again.msg == 'not changed' + +# multiple attributes on differing parent nodes + +- name: ensure all attribute lang=nl + win_xml: + path: '{{ remote_tmp_dir }}\books.xml' + xpath: //@lang + type: attribute + attribute: lang + fragment: nl + count: yes + register: multi_attr + +- name: verify multi attribute + assert: + that: + - multi_attr.changed == true + - multi_attr.count == 6 + - multi_attr.msg == 'attribute changed' + +- name: ensure all attribute lang=nl (idempotency) + win_xml: + path: '{{ remote_tmp_dir }}\books.xml' + xpath: //@lang + type: attribute + attribute: lang + fragment: nl + count: yes + register: multi_attr_again + +- name: verify multi attribute (idempotency) + assert: + that: + - multi_attr_again.changed == false + - multi_attr_again.count == 6 + - multi_attr_again.msg == 'not changed' diff --git a/ansible_collections/community/windows/tests/integration/targets/win_zip/aliases b/ansible_collections/community/windows/tests/integration/targets/win_zip/aliases new file mode 100644 index 000000000..b6fdaeae0 --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_zip/aliases @@ -0,0 +1 @@ +shippable/windows/group5
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_zip/defaults/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_zip/defaults/main.yml new file mode 100644 index 000000000..1b2520d5a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_zip/defaults/main.yml @@ -0,0 +1,2 @@ +win_zip_name: .ÅÑŚÌβŁÈ [$!@^&test(;)] 👋 +win_zip_dir: '{{ remote_tmp_dir }}\win_zip {{ win_zip_name }}'
\ No newline at end of file diff --git a/ansible_collections/community/windows/tests/integration/targets/win_zip/meta/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_zip/meta/main.yml new file mode 100644 index 000000000..9f37e96cd --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_zip/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: +- setup_remote_tmp_dir diff --git a/ansible_collections/community/windows/tests/integration/targets/win_zip/tasks/main.yml b/ansible_collections/community/windows/tests/integration/targets/win_zip/tasks/main.yml new file mode 100644 index 000000000..bdf98641a --- /dev/null +++ b/ansible_collections/community/windows/tests/integration/targets/win_zip/tasks/main.yml @@ -0,0 +1,165 @@ +--- +- set_fact: + zip_info: | + param ($Path) + + $ErrorActionPreference = 'Stop' + $Ansible.Changed = $false + + $utf8 = New-Object -TypeName Text.UTF8Encoding -ArgumentList $false + Add-Type -AssemblyName System.IO.Compression -ErrorAction Stop + Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop + + $zip = $null + $fs = [IO.File]::OpenRead($Path) + try { + $zip = New-Object -TypeName IO.Compression.ZipArchive -ArgumentList $fs, 'Read', $false, $utf8 + + $zip.Entries | Select-Object -Propert FullName, Length + } + finally { + if ($zip) { $zip.Dispose() } + $fs.Dispose() + } + +- name: create testdir\src directory for CI + ansible.windows.win_file: + path: '{{ win_zip_dir }}\src\' + state: directory + +- name: create testdir\zipped directory for CI + ansible.windows.win_file: + path: '{{ win_zip_dir }}\zipped\' + state: directory + +- name: create test files for CI + ansible.builtin.win_copy: + content: | + This is a test file. + dest: '{{ win_zip_dir }}\src\{{ win_zip_name }}.txt' + +# Case01: Check file compression +- name: compress a file (check) + win_zip: + src: '{{ win_zip_dir }}\src\{{ win_zip_name }}.txt' + dest: '{{ win_zip_dir }}\zipped\test_file.zip' + register: zip_check + check_mode: yes + +- name: get result of compress zip (check) + ansible.windows.win_stat: + path: '{{ win_zip_dir }}\zipped\test_file.zip' + register: zip_actual_check + +- name: assert result of zip (check) + assert: + that: + - zip_check is changed + - not zip_actual_check.stat.exists + +- name: compress a file + win_zip: + src: '{{ win_zip_dir }}\src\{{ win_zip_name }}.txt' + dest: '{{ win_zip_dir }}\zipped\test_file.zip' + register: zip + +- name: get result of compress zip + ansible.windows.win_powershell: + script: '{{ zip_info }}' + parameters: + Path: '{{ win_zip_dir }}\zipped\test_file.zip' + register: zip_actual + +- name: assert result of zip + assert: + that: + - zip is changed + - zip_actual.output | length == 1 + - zip_actual.output[0]['FullName'] == win_zip_name + '.txt' + - zip_actual.output[0]['Length'] == 21 + +# Case02: Check directory compression +- name: compress a directory (check) + win_zip: + src: '{{ win_zip_dir }}\src\' + dest: '{{ win_zip_dir }}\test_dir.zip' + register: zip_check + check_mode: yes + +- name: get result of compress zip (check) + ansible.windows.win_stat: + path: '{{ win_zip_dir }}\test_dir.zip' + register: zip_actual_check + +- name: assert result of zip (check) + assert: + that: + - zip_check is changed + - not zip_actual_check.stat.exists + +- name: compress a directory + win_zip: + src: '{{ win_zip_dir }}\src\' + dest: '{{ win_zip_dir }}\test_dir.zip' + register: zip + +- name: get result of compress zip + ansible.windows.win_powershell: + script: '{{ zip_info }}' + parameters: + Path: '{{ win_zip_dir }}\test_dir.zip' + register: zip_actual + +- name: assert result of zip + assert: + that: + - zip is changed + - zip_actual.output | length == 1 + # Should contain the original base directory + - zip_actual.output[0]['FullName'] == "src/" + win_zip_name + '.txt' + - zip_actual.output[0]['Length'] == 21 + +- name: compress a directories contents + win_zip: + src: '{{ win_zip_dir }}\src\*' + dest: '{{ win_zip_dir }}\test_dir_content.zip' + register: zip + +- name: get result of compress zip + ansible.windows.win_powershell: + script: '{{ zip_info }}' + parameters: + Path: '{{ win_zip_dir }}\test_dir_content.zip' + register: zip_actual + +- name: assert result of zip + assert: + that: + - zip is changed + - zip_actual.output | length == 1 + # Should not contain the original base directory + - zip_actual.output[0]['FullName'] == win_zip_name + '.txt' + - zip_actual.output[0]['Length'] == 21 + +- name: compress a file with existing dest (check) + win_zip: + src: '{{ win_zip_dir }}\src\{{ win_zip_name }}.txt' + dest: '{{ win_zip_dir }}\zipped\test_file.zip' + register: zip_check + check_mode: yes + +- name: assert result of zip (check) + assert: + that: + - not zip_check is changed + +- name: compress a file to existing dest + win_zip: + src: '{{ win_zip_dir }}\src\{{ win_zip_name }}.txt' + dest: '{{ win_zip_dir }}\zipped\test_file.zip' + register: zip + +- name: assert result of zip + assert: + that: + - not zip is changed |