diff --git a/changelogs/fragments/56033-win_iis_webapplication-add-authentication-options.yml b/changelogs/fragments/56033-win_iis_webapplication-add-authentication-options.yml new file mode 100644 index 00000000000..410e1ad9851 --- /dev/null +++ b/changelogs/fragments/56033-win_iis_webapplication-add-authentication-options.yml @@ -0,0 +1,2 @@ +minor_changes: + - "win_iis_webapplication - add new options ``connect_as``, ``username``, ``password``." diff --git a/lib/ansible/modules/windows/win_iis_webapplication.ps1 b/lib/ansible/modules/windows/win_iis_webapplication.ps1 index 587de19cb69..92fdbbd9e50 100644 --- a/lib/ansible/modules/windows/win_iis_webapplication.ps1 +++ b/lib/ansible/modules/windows/win_iis_webapplication.ps1 @@ -13,6 +13,9 @@ $site = Get-AnsibleParam -obj $params -name "site" -type "str" -failifempty $tru $state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "absent","present" $physical_path = Get-AnsibleParam -obj $params -name "physical_path" -type "str" -aliases "path" $application_pool = Get-AnsibleParam -obj $params -name "application_pool" -type "str" +$connect_as = Get-AnsibleParam -obj $params -name 'connect_as' -type 'str' -validateset 'specific_user', 'pass_through' +$username = Get-AnsibleParam -obj $params -name "username" -type "str" -failifempty ($connect_as -eq 'specific_user') +$password = Get-AnsibleParam -obj $params -name "password" -type "str" -failifempty ($connect_as -eq 'specific_user') $result = @{ application_pool = $application_pool @@ -90,6 +93,29 @@ try { $result.changed = $true } } + + # Change username and password if needed + $app_user = Get-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' + $app_pass = Get-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'password' + if ($connect_as -eq 'pass_through') { + if ($app_user -ne '') { + Clear-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' -WhatIf:$check_mode + $result.changed = $true + } + if ($app_pass -ne '') { + Clear-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'password' -WhatIf:$check_mode + $result.changed = $true + } + } elseif ($connect_as -eq 'specific_user') { + if ($app_user -ne $username) { + Set-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' -Value $username -WhatIf:$check_mode + $result.changed = $true + } + if ($app_pass -ne $password) { + Set-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'password' -Value $password -WhatIf:$check_mode + $result.changed = $true + } + } } } catch { Fail-Json $result $_.Exception.Message @@ -98,6 +124,13 @@ try { # When in check-mode or on removal, this may fail $application = Get-WebApplication -Site $site -Name $name if ($application) { + $app_user = Get-ItemProperty -Path "IIS:\Sites\$($site)\$($name)" -Name 'userName' + if ($app_user -eq '') { + $result.connect_as = 'pass_through' + } else { + $result.connect_as = 'specific_user' + } + $result.physical_path = $application.PhysicalPath $result.application_pool = $application.ApplicationPool } diff --git a/lib/ansible/modules/windows/win_iis_webapplication.py b/lib/ansible/modules/windows/win_iis_webapplication.py index 3e293d989b2..2fad6a48072 100644 --- a/lib/ansible/modules/windows/win_iis_webapplication.py +++ b/lib/ansible/modules/windows/win_iis_webapplication.py @@ -42,6 +42,26 @@ options: - The application pool in which the new site executes. - If not specified, the application pool of the current website will be used. type: str + connect_as: + description: + - The type of authentication to use for this application. Either C(pass_through) or C(specific_user) + - If C(pass_through), IIS will use the identity of the user or application pool identity to access the file system or network. + - If C(specific_user), IIS will use the credentials provided in I(username) and I(password) to access the file system or network. + type: str + choices: [pass_through, specific_user] + version_added: '2.10' + username: + description: + - Specifies the user name of an account that can access configuration files and content for this application. + - Required when I(connect_as) is set to C(specific_user). + type: str + version_added: '2.10' + password: + description: + - The password associated with I(username). + - Required when I(connect_as) is set to C(specific_user). + type: str + version_added: '2.10' seealso: - module: win_iis_virtualdirectory - module: win_iis_webapppool @@ -71,4 +91,9 @@ physical_path: returned: success type: str sample: C:\apps\acme\api +connect_as: + description: How IIS will try to authenticate to the physical_path. + returned: when the application exists + type: str + sample: specific_user ''' diff --git a/test/integration/targets/win_iis_webapplication/aliases b/test/integration/targets/win_iis_webapplication/aliases new file mode 100644 index 00000000000..3cf5b97e805 --- /dev/null +++ b/test/integration/targets/win_iis_webapplication/aliases @@ -0,0 +1 @@ +shippable/windows/group3 diff --git a/test/integration/targets/win_iis_webapplication/defaults/main.yml b/test/integration/targets/win_iis_webapplication/defaults/main.yml new file mode 100644 index 00000000000..e5a582dee14 --- /dev/null +++ b/test/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/test/integration/targets/win_iis_webapplication/meta/main.yml b/test/integration/targets/win_iis_webapplication/meta/main.yml new file mode 100644 index 00000000000..e3dd5fb100d --- /dev/null +++ b/test/integration/targets/win_iis_webapplication/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: +- setup_remote_tmp_dir diff --git a/test/integration/targets/win_iis_webapplication/tasks/main.yml b/test/integration/targets/win_iis_webapplication/tasks/main.yml new file mode 100644 index 00000000000..83614f17f30 --- /dev/null +++ b/test/integration/targets/win_iis_webapplication/tasks/main.yml @@ -0,0 +1,70 @@ +--- +# Cannot use win_feature to install IIS on Server 2008. +# Run a brief check and skip hosts that don't support +# that operation + +# Run on Server 2012 and higher +- block: + - name: ensure IIS features are installed + win_feature: + name: Web-Server + state: present + include_management_tools: True + register: feature_install + + - name: reboot after feature install + win_reboot: + when: feature_install.reboot_required + + # may be possible that copy corrupts the file + - name: Get iis configuration checksum + win_stat: + path: '{{ ansible_env.SystemRoot }}\System32\inetsrv\config\applicationHost.config' + checksum_algorithm: sha1 + register: stat_result + + - name: take a copy of the original iis configuration + win_copy: + src: '{{ ansible_env.SystemRoot }}\System32\inetsrv\config\applicationHost.config' + dest: '{{ ansible_env.TEMP }}\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 + win_copy: + src: '{{ ansible_env.TEMP }}\applicationHost.config' + dest: '{{ ansible_env.SystemRoot }}\System32\inetsrv\config\applicationHost.config' + remote_src: yes + register: copy_result + + - assert: + that: + - "stat_result.stat.checksum == copy_result.checksum" + + when: ansible_distribution_version is version('6.2','ge') diff --git a/test/integration/targets/win_iis_webapplication/tasks/tests.yml b/test/integration/targets/win_iis_webapplication/tasks/tests.yml new file mode 100644 index 00000000000..135cccfeca9 --- /dev/null +++ b/test/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"