From 0a3da471f5ced651bb991a4b157ce528ed968ec3 Mon Sep 17 00:00:00 2001 From: nwsparks Date: Mon, 1 Jan 2018 19:30:18 -0500 Subject: [PATCH] win_iis_webbinding rewrite (#33958) * Begin rewrite of win_iis_webbinding Add integration testing, check mode and idempotency Add support for SNI Fix replacing SSL cert on existing bindings * finished up initial rewrite of win_iis_webbinding * updated test to remove tests as filters * updated win_iis_webbinding docs * fix more doc/formatting issues win_iis_webbinding * Removed string empty defaults for certs. Added a few new helpful comments. * Revert "Removed string empty defaults for certs. Added a few new helpful" This reverts commit 48f35faea8d5294b34e1aa842a95c9352b90257f. --- .../modules/windows/win_iis_webbinding.ps1 | 547 ++++++++++++++---- .../modules/windows/win_iis_webbinding.py | 146 +++-- .../targets/win_iis_webbinding/aliases | 1 + .../win_iis_webbinding/defaults/main.yml | 30 + .../library/test_get_webbindings.ps1 | 122 ++++ .../win_iis_webbinding/tasks/failures.yml | 74 +++ .../targets/win_iis_webbinding/tasks/http.yml | 372 ++++++++++++ .../win_iis_webbinding/tasks/https-ge6.2.yml | 459 +++++++++++++++ .../win_iis_webbinding/tasks/https-lt6.2.yml | 423 ++++++++++++++ .../targets/win_iis_webbinding/tasks/main.yml | 62 ++ .../win_iis_webbinding/tasks/setup.yml | 88 +++ 11 files changed, 2153 insertions(+), 171 deletions(-) create mode 100644 test/integration/targets/win_iis_webbinding/aliases create mode 100644 test/integration/targets/win_iis_webbinding/defaults/main.yml create mode 100644 test/integration/targets/win_iis_webbinding/library/test_get_webbindings.ps1 create mode 100644 test/integration/targets/win_iis_webbinding/tasks/failures.yml create mode 100644 test/integration/targets/win_iis_webbinding/tasks/http.yml create mode 100644 test/integration/targets/win_iis_webbinding/tasks/https-ge6.2.yml create mode 100644 test/integration/targets/win_iis_webbinding/tasks/https-lt6.2.yml create mode 100644 test/integration/targets/win_iis_webbinding/tasks/main.yml create mode 100644 test/integration/targets/win_iis_webbinding/tasks/setup.yml diff --git a/lib/ansible/modules/windows/win_iis_webbinding.ps1 b/lib/ansible/modules/windows/win_iis_webbinding.ps1 index 9b212ff968f..8f020c31961 100644 --- a/lib/ansible/modules/windows/win_iis_webbinding.ps1 +++ b/lib/ansible/modules/windows/win_iis_webbinding.ps1 @@ -1,131 +1,454 @@ #!powershell -# (c) 2015, Henrik Wallström -# -# 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 . +# Copyright: (c) 2017, Noah Sparks +# Copyright: (c) 2015, Henrik Wallström +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +#Requires -Module Ansible.ModuleUtils.Legacy -# WANT_JSON -# POWERSHELL_COMMON +$params = Parse-Args -arguments $args -supports_check_mode $true +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false -$params = Parse-Args $args; - -$name = Get-AnsibleParam $params -name "name" -failifempty $true +$name = Get-AnsibleParam $params -name "name" -type str -failifempty $true -aliases 'website' $state = Get-AnsibleParam $params "state" -default "present" -validateSet "present","absent" -$host_header = Get-AnsibleParam $params -name "host_header" -$protocol = Get-AnsibleParam $params -name "protocol" -$port = Get-AnsibleParam $params -name "port" -$ip = Get-AnsibleParam $params -name "ip" -$certificatehash = Get-AnsibleParam $params -name "certificate_hash" -default $false -$certificateStoreName = Get-AnsibleParam $params -name "certificate_store_name" -default "MY" +$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" -default '80' +$ip = Get-AnsibleParam $params -name "ip" -default '*' +$certificateHash = Get-AnsibleParam $params -name "certificate_hash" -type str -default ([string]::Empty) +$certificateStoreName = Get-AnsibleParam $params -name "certificate_store_name" -type str -default ([string]::Empty) +$sslFlags = Get-AnsibleParam $params -name "ssl_flags" -default '0' -ValidateSet '0','1','2','3' -$binding_parameters = @{ - Name = $name -}; - -If ($host_header) { - $binding_parameters.HostHeader = $host_header -} - -If ($protocol) { - $binding_parameters.Protocol = $protocol -} - -If ($port) { - $binding_parameters.Port = $port -} - -If ($ip) { - $binding_parameters.IPAddress = $ip -} - -# Ensure WebAdministration module is loaded -if ((Get-Module "WebAdministration" -ErrorAction SilentlyContinue) -eq $null){ - Import-Module WebAdministration -} - -function Create-Binding-Info { - return @{ - "bindingInformation" = $args[0].bindingInformation - "certificateHash" = $args[0].certificateHash - "certificateStoreName" = $args[0].certificateStoreName - "isDsMapperEnabled" = $args[0].isDsMapperEnabled - "protocol" = $args[0].protocol - "sslFlags" = $args[0].sslFlags - } -} - -# Result $result = @{ changed = $false - parameters = $binding_parameters - matched = @() - removed = @() - added = @() -}; +} + +################# +### Functions ### +################# +function Create-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 { + $bind_search_splat = @{ + 'name' = $args[0].name + 'protocol' = $args[0].protocol + 'port' = $args[0].port + 'ip' = $args[0].ip + 'hostheader' = $args[0].hostheader + } + + # if no bindings exist, get-webbinding fails with an error that can't be ignored via error actions on older systems + # let's ignore that specific error + If (-not $bind_search_splat['hostheader']) + { + Try { + Get-WebBinding @bind_search_splat | Where-Object {$_.BindingInformation.Split(':')[-1] -eq [string]::Empty} + } + Catch { + If (-not $_.Exception.Message.CompareTo('Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value')) + { + Throw $_.Exception.Message + } + } + } + Else + { + Try { + Get-WebBinding @bind_search_splat + } + Catch { + If (-not $_.Exception.Message.CompareTo('Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value')) + { + Throw $_.Exception.Message + } + } + } +} + +Function Get-CertificateSubjects { +Param ( +[string]$CertPath +) + If (-Not (Test-Path $CertPath) ) + { + Fail-Json -obj $result -message "Unable to locate certificate at $CertPath" + } + + $cert = get-item $CertPath + + If ([version][System.Environment]::OSVersion.Version -ge [version]6.2) + { + $cert.DnsNameList.unicode + } + Else + { + $san = $cert.extensions | Where-Object {$_.Oid.FriendlyName -eq 'Subject Alternative Name'} + If ($san) + { + $san.Format(1) -split '\r\n' | Where-Object {$_} | ForEach-Object { + ($_ -split '=')[-1] + } + } + Else + { + If ($cert.subject -like "*,*") + { + ($cert.Subject | Select-String "CN=(.*?),?").matches.groups[-1].value + } + Else + { + $cert.subject -replace "CN=",'' + } + } + } +} + + + +############################# +### Pre-Action Validation ### +############################# +$os_version = [version][System.Environment]::OSVersion.Version + +# Ensure WebAdministration module is loaded +If ($os_version -lt [version]'6.1') +{ + Try { + Add-PSSnapin WebAdministration + } + Catch { + Fail-Json -obj $result -message "The WebAdministration snap-in is not present. Please make sure it is installed." + } +} +Else +{ + Try { + Import-Module WebAdministration + } + Catch { + Fail-Json -obj $result -message "Failed to load WebAdministration module. Is IIS installed? $($_.Exception.Message)" + } +} + +# ensure website targetted exists. -Name filter doesn't work on 2k8r2 so do where-object instead +$website_check = get-website | Where-Object {$_.name -eq $name} +If (-not $website_check) +{ + Fail-Json -obj $result -message "Unable to retrieve website with name $Name. Make sure the website name is valid and exists." +} + +# if OS older than 2012 (6.2) and ssl flags are set, fail. Otherwise toggle sni_support +If ($os_version -lt [version]'6.2') +{ + If ($sslFlags -ne 0) + { + Fail-Json -obj $result -message "SNI and Certificate Store support is not available for systems older than 2012 (6.2)" + } + $sni_support = $false #will cause the sslflags check later to skip +} +Else +{ + $sni_support = $true +} + +# make sure ssl flags only specified with https protocol +If ($protocol -ne 'https' -and $sslFlags -gt 0) +{ + Fail-Json -obj $result -message "SSLFlags can only be set for HTTPS protocol" +} + +# validate certificate details if provided +# we don't do anything with cert on state: absent, so only validate present +If ($certificateHash -and $state -eq 'present') +{ + If ($protocol -ne 'https') + { + Fail-Json -obj $result -message "You can only provide a certificate thumbprint when protocol is set to https" + } + + #apply default for cert store name + If (-Not $certificateStoreName) + { + $certificateStoreName = 'my' + } + + #validate cert path + $cert_path = "cert:\LocalMachine\$certificateStoreName\$certificateHash" + If (-Not (Test-Path $cert_path) ) + { + Fail-Json -obj $result -message "Unable to locate certificate at $cert_path" + } + + #check if cert is wildcard and update results with useful info. + $cert_subjects = Get-CertificateSubjects $cert_path + $result.certificate_subjects = $cert_subjects + If ($cert_subjects | Where-Object {$_ -match '^\*'}) + { + $cert_is_wildcard = $true + $result.cert_is_wildcard = $cert_is_wildcard + } + Else + { + $cert_is_wildcard = $false + $result.cert_is_wildcard = $cert_is_wildcard + } + + If ($os_version -lt [version]6.2 -and $host_header -and -not $cert_is_wildcard) + { + Fail-Json -obj $result -message "You cannot specify host headers with SSL unless it is a wildcard certificate." + } + Elseif ($os_version -ge [version]6.2 -and $host_header -and (-not $cert_is_wildcard -and $sslFlags -eq 0)) + { + Fail-Json -obj $result -message "You cannot specify host headers with SSL unless it is a wildcard certificate or SNI is enabled." + } +} + +# make sure binding info is valid for central cert store if sslflags -gt 1 +If ($sslFlags -gt 1 -and ($certificateHash -ne [string]::Empty -or $certificateStoreName -ne [string]::Empty)) +{ + Fail-Json -obj $result -message "You set sslFlags to $sslFlags. This indicates you wish to use the Central Certificate Store feature. + This cannot be used in combination with certficiate_hash and certificate_store_name. When using the Central Certificate Store feature, + the certificate is automatically retrieved from the store rather than manually assigned to the binding." +} + +# make sure host_header: '*' only present when state: absent +If ($host_header -match '^\*$' -and $state -ne 'absent') +{ + Fail-Json -obj $result -message "host_header: '*' can only be used in combinaiton with state: absent" +} + +########################## +### start action items ### +########################## + +# 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 +} # Get bindings matching parameters -$curent_bindings = Get-WebBinding @binding_parameters -$curent_bindings | Foreach { - $result.matched += Create-Binding-Info $_ +Try { + $current_bindings = Get-SingleWebBinding $binding_parameters +} +Catch { + Fail-Json -obj $result -message "Failed to retrieve bindings with Get-SingleWebBinding - $($_.Exception.Message)" } -try { - # Add - if (-not $curent_bindings -and $state -eq 'present') { - New-WebBinding @binding_parameters -Force - - # Select certificate - if($certificateHash -ne $FALSE) { - - $ip = $binding_parameters["IPAddress"] - if((!$ip) -or ($ip -eq "*")) { - $ip = "0.0.0.0" - } - - $port = $binding_parameters["Port"] - if(!$port) { - $port = 443 - } - - $result.port = $port - $result.ip = $ip - - Push-Location IIS:\SslBindings\ - Get-Item Cert:\LocalMachine\$certificateStoreName\$certificateHash | New-Item "$($ip)!$($port)" - Pop-Location +################################################ +### Remove binding or exit if already absent ### +################################################ +If ($current_bindings -and $state -eq 'absent') +{ + Try { + # will remove multiple objects in the case of * host header + $current_bindings | Remove-WebBinding -WhatIf:$check_mode + $result.changed = $true + } + Catch { + Fail-Json -obj $result -message "Failed to remove the binding from IIS - $($_.Exception.Message)" } - $result.added += Create-Binding-Info (Get-WebBinding @binding_parameters) - $result.changed = $true - } + # removing bindings from iis may not also remove them from iis:\sslbindings - # Remove - if ($curent_bindings -and $state -eq 'absent') { - $curent_bindings | foreach { - Remove-WebBinding -InputObject $_ - $result.removed += Create-Binding-Info $_ + $result.operation_type = 'removed' + $result.binding_info = $current_bindings | ForEach-Object {Create-BindingInfo $_} + Exit-Json -obj $result +} +ElseIf (-Not $current_bindings -and $state -eq 'absent') +{ + # exit changed: false since it's already gone + Exit-Json -obj $result +} + + +################################ +### Modify existing bindings ### +################################ +<# +since we have already.binding_info the parameters available to get-webbinding, +we just need to check here for the ones that are not available which are the +ssl settings (hash, store, sslflags). If they aren't set we update here, or +exit with changed: false +#> +ElseIf ($current_bindings) +{ + #ran into a strange edge case in testing where I was able to retrieve bindings but not expand all the properties + #when adding a self-signed wildcard cert to a binding. it seemed to permanently break the binding. only removing it + #would cause the error to stop. + Try { + $null = $current_bindings | Select-Object * } + Catch { + Fail-Json -obj $result -message "Found a matching binding, but failed to expand it's properties (get-binding | FL *). In testing, this was caused by using a self-signed wildcard certificate. $($_.Exception.Message)" + } + + # check if there is a match on the ssl parameters + If ( ($current_bindings.sslFlags -ne $sslFlags -and $sni_support) -or + $current_bindings.certificateHash -ne $certificateHash -or + $current_bindings.certificateStoreName -ne $certificateStoreName) + { + # match/update SNI + If ($current_bindings.sslFlags -ne $sslFlags -and $sni_support) + { + Try { + Set-WebBinding -Name $name -IPAddress $ip -Port $port -HostHeader $host_header -PropertyName sslFlags -value $sslFlags -whatif:$check_mode + $result.changed = $true + } + Catch { + Fail-Json -obj $result -message "Failed to update sslFlags on binding - $($_.Exception.Message)" + } + + # Refresh the binding object since it has been changed + Try { + $current_bindings = Get-SingleWebBinding $binding_parameters + } + Catch { + Fail-Json -obj $result -message "Failed to refresh bindings after setting sslFlags - $($_.Exception.Message)" + } + } + # match/update certificate + If ($current_bindings.certificateHash -ne $certificateHash -or $current_bindings.certificateStoreName -ne $certificateStoreName) + { + If (-Not $check_mode) + { + Try { + $current_bindings.AddSslCertificate($certificateHash,$certificateStoreName) + } + Catch { + Fail-Json -obj $result -message "Failed to set new SSL certificate - $($_.Exception.Message)" + } + } + } + $result.changed = $true + $result.operation_type = 'updated' + $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State + $result.binding_info = Create-BindingInfo (Get-SingleWebBinding $binding_parameters) + Exit-Json -obj $result #exit changed true + } + Else + { + $result.operation_type = 'matched' + $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State + $result.binding_info = Create-BindingInfo (Get-SingleWebBinding $binding_parameters) + Exit-Json -obj $result #exit changed false + } +} + +######################## +### Add new bindings ### +######################## +ElseIf (-not $current_bindings -and $state -eq 'present') +{ + If ($certificateHash) + { + <# + Make sure a valid binding is specified. It's possible for another site to have a binding on the same IP:PORT. If + we bind to that same ip port without hostheader/sni it will cause a collision. Note, this check only matters for + https. Http will generate an error when new-webbinding is called if there is a conflict, unlike https. + + I couldn't think of a good way to handle scenarios involving wildcards. There's just too many to think about and I + wouldn't want to potentially hard fail valid scenarios here that I did not consider...so those can still collide. We just skip + validation anytime an existing binding is a wildcard. + + If a collision does occur, the website will be stopped. To help with this we'll return the website state into results. + #> + + #use this instead of get-webbinding. on 2k8r2 get-webbinding fails with an error if a site with no bindings exists + $binding_matches = (Get-Website).bindings.collection | Where-Object {$_.BindingInformation -eq "$ip`:$port`:"} + + #get dns names for all certs in matching bindings + $subjects = Foreach ($binding in $binding_matches) + { + $cert_path = "cert:\localmachine\$($binding.certificatestorename)\$($binding.certificatehash)" + Get-CertificateSubjects $cert_path + } + + #skip validating scenarios where existing certs are wildcard + If (-not ($subjects | Where-Object {$_ -match "^\*"})) + { + If ($sslFlags -eq 0 -and $binding_matches -and $os_version -gt [version]6.2) + { + Fail-Json -obj $result -message "A conflicting binding has been found on the same ip $ip and port $port. To continue, you will either have to remove the offending binding or enable sni" + } + ElseIf ($binding_matches -and $os_version -lt [version]6.2) + { + Fail-Json -obj $result -message "A conflicting binding has been found on the same ip $ip and port $port. To continue you will need to remove the existing binding or assign a new IP or Port to this one" + } + } + } + + # add binding. this creates the binding, but does not apply a certificate to it. + Try + { + If (-not $check_mode) + { + If ($sni_support) + { + New-WebBinding @binding_parameters -SslFlags $sslFlags -Force + } + Else + { + New-WebBinding @binding_parameters -Force + } + } + $result.changed = $true + } + Catch + { + $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State + Fail-Json -obj $result -message "Failed at creating new binding (note: creating binding and adding ssl are separate steps) - $($_.Exception.Message)" + } + + # add certificate to binding + If ($certificateHash -and -not $check_mode) + { + Try { + $new_binding = get-webbinding -Name $name -IPAddress $ip -port $port -Protocol $protocol -hostheader $host_header + $new_binding.addsslcertificate($certificateHash,$certificateStoreName) + } + Catch { + $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State + Fail-Json -obj $result -message "Failed to set new SSL certificate - $($_.Exception.Message)" + } + } + $result.changed = $true - } - - + $result.operation_type = 'added' + $result.website_state = (Get-Website | Where-Object {$_.Name -eq $Name}).State + $result.binding_info = Create-BindingInfo (Get-SingleWebBinding $binding_parameters) + Exit-Json $result } -catch { - Fail-Json $result $_.Exception.Message -} - -Exit-Json $result diff --git a/lib/ansible/modules/windows/win_iis_webbinding.py b/lib/ansible/modules/windows/win_iis_webbinding.py index 24a666f0a92..1dd173bc26d 100644 --- a/lib/ansible/modules/windows/win_iis_webbinding.py +++ b/lib/ansible/modules/windows/win_iis_webbinding.py @@ -1,22 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# (c) 2015, Henrik Wallström -# -# 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 . +# Copyright: (c) 2017, Noah Sparks +# Copyright: (c) 2017, Henrik Wallström +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ANSIBLE_METADATA = {'metadata_version': '1.1', @@ -28,7 +15,7 @@ DOCUMENTATION = r''' --- module: win_iis_webbinding version_added: "2.0" -short_description: Configures a IIS Web site. +short_description: Configures a IIS Web site binding. description: - Creates, Removes and configures a binding to an existing IIS Web site options: @@ -36,66 +23,51 @@ options: description: - Names of web site required: true - default: null - aliases: [] + aliases: [website] state: description: - State of the binding choices: - present - absent - required: false - default: null - aliases: [] + default: present port: description: - The port to bind to / use for the new site. - required: false - default: null - aliases: [] + default: 80 ip: description: - The IP address to bind to / use for the new site. - required: false - default: null - aliases: [] + default: '*' host_header: description: - The host header to bind to / use for the new site. - required: false - default: null - aliases: [] + - For state absent, you can use c('*') here to remove all bindings for a particular + protocol/ip/port combination. protocol: description: - The protocol to be used for the Web binding (usually HTTP, HTTPS, or FTP). - required: false - default: null - aliases: [] + default: http certificate_hash: description: - - Certificate hash for the SSL binding. The certificate hash is the unique identifier for the certificate. - required: false - default: null - aliases: [] + - Certificate hash (thumbprint) for the SSL binding. The certificate hash is the unique identifier for the certificate. certificate_store_name: description: - - Name of the certificate store where the certificate for the binding is located. - required: false - default: "My" - aliases: [] -author: Henrik Wallström + - Name of the certificate store where the certificate for the binding is located. + default: "my" + ssl_flags: + description: + - This parameter is only valid on Server 2012 and newer. + - Primarily used for enabling and disabling server name indication (SNI). + - Set to c(0) to disable SNI. + - Set to c(1) to enable SNI. + version_added: "2.5" +author: + - Noah Sparks (@nwsparks) + - Henrik Wallström ''' EXAMPLES = r''' -- name: Return binding information for an existing host - win_iis_webbinding: - name: Default Web Site - -- name: Return the HTTPS binding information for an existing host - win_iis_webbinding: - name: Default Web Site - protocol: https - - name: Add a HTTP binding on port 9090 win_iis_webbinding: name: Default Web Site @@ -112,20 +84,76 @@ EXAMPLES = r''' win_iis_webbinding: name: Default Web Site protocol: https - state: present - -- name: Add a HTTPS binding and select certificate to use - win_iis_webbinding: - name: Default Web Site - protocol: https + port: 443 certificate_hash: B0D0FA8408FC67B230338FCA584D03792DA73F4C state: present -- name: Website https biding to specific port +- name: Add a HTTPS binding with host header and SNI enabled win_iis_webbinding: name: Default Web Site protocol: https port: 443 + host_header: test.com + ssl_flags: 1 certificate_hash: D1A3AF8988FD32D1A3AF8988FD323792DA73F4C state: present + +- name: Remove all https bindings on port 443 + win_iis_webbinding: + name: Default Web Site + protocol: https + port: 443 + host_header: '*' + state: absent +''' + +RETURN = r''' +cert_is_wildcard: + description: + - Tells you if the certificate you are using is a wildcard + returned: when certificate_hash is defined + type: boolean + sample: false + version_added: "2.5" +certificate_subjects: + description: + - All of the subject names for the certificate you are using + returned: when certificate_hash is defined + type: list + sample: ["*.test.com","test.com"] + version_added: "2.5" +website_state: + description: + - The state of the website being targetted + - Can be helpful in case you accidentally cause a binding collision + which can result in the targetted site being stopped + returned: always + type: string + sample: "Started" + version_added: "2.5" +operation_type: + description: + - The type of operation performed + - Can be removed, updated, matched, or added + returned: on success + type: string + sample: "removed" + version_added: "2.5" +binding_info: + description: + - Information on the binding being manipulated + returned: on success + type: dictionary + sample: |- + "binding_info": { + "bindingInformation": "127.0.0.1:443:", + "certificateHash": "FF3910CE089397F1B5A77EB7BAFDD8F44CDE77DD", + "certificateStoreName": "MY", + "hostheader": "", + "ip": "127.0.0.1", + "port": 443, + "protocol": "https", + "sslFlags": "not supported" + } + version_added: "2.5" ''' diff --git a/test/integration/targets/win_iis_webbinding/aliases b/test/integration/targets/win_iis_webbinding/aliases new file mode 100644 index 00000000000..ee0ed5974e9 --- /dev/null +++ b/test/integration/targets/win_iis_webbinding/aliases @@ -0,0 +1 @@ +windows/ci/group2 diff --git a/test/integration/targets/win_iis_webbinding/defaults/main.yml b/test/integration/targets/win_iis_webbinding/defaults/main.yml new file mode 100644 index 00000000000..13f0bc333fa --- /dev/null +++ b/test/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/test/integration/targets/win_iis_webbinding/library/test_get_webbindings.ps1 b/test/integration/targets/win_iis_webbinding/library/test_get_webbindings.ps1 new file mode 100644 index 00000000000..084b6a10574 --- /dev/null +++ b/test/integration/targets/win_iis_webbinding/library/test_get_webbindings.ps1 @@ -0,0 +1,122 @@ +#!powershell + +# Copyright: (c) 2017, Noah Sparks +# 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 +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false + +$name = Get-AnsibleParam $params -name "name" -type str -failifempty $true -aliases 'website' +#$state = Get-AnsibleParam $params "state" -default "present" -validateSet "present","absent" +$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 '*' +$certificateHash = Get-AnsibleParam $params -name "certificate_hash" -type str +$certificateStoreName = Get-AnsibleParam $params -name "certificate_store_name" -type str +$sslFlags = Get-AnsibleParam $params -name "ssl_flags" -type int -default '0' -ValidateSet '0','1','2','3' + +$result = @{ + changed = $false +} + +function Create-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 { + $bind_search_splat = @{ + 'name' = $args[0].name + 'protocol' = $args[0].protocol + 'port' = $args[0].port + 'ip' = $args[0].ip + 'hostheader' = $args[0].hostheader + } + + # if no bindings exist, get-webbinding fails with an error that can't be ignored via error actions on older systems + # let's ignore that specific error + If (-not $bind_search_splat['hostheader']) + { + Try { + Get-WebBinding @bind_search_splat | Where-Object {$_.BindingInformation.Split(':')[-1] -eq [string]::Empty} + } + Catch { + If (-not $_.Exception.Message.CompareTo('Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value')) + { + Throw $_.Exception.Message + } + } + } + Else + { + Try { + Get-WebBinding @bind_search_splat + } + Catch { + If (-not $_.Exception.Message.CompareTo('Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value')) + { + Throw $_.Exception.Message + } + } + } +} + +# 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 +} + +# 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 = Create-BindingInfo $current_bindings + } + Catch { + Fail-Json -obj $result -message "Failed to create binding info - $($_.Exception.Message)" + } + + $result.binding = $binding_info +} +exit-json -obj $result diff --git a/test/integration/targets/win_iis_webbinding/tasks/failures.yml b/test/integration/targets/win_iis_webbinding/tasks/failures.yml new file mode 100644 index 00000000000..61753f5ab7b --- /dev/null +++ b/test/integration/targets/win_iis_webbinding/tasks/failures.yml @@ -0,0 +1,74 @@ +- name: failure check bind with host header but no wc or sni + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: test.com + protocol: https + ip: '*' + port: 443 + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + ssl_flags: 0 + register: failure + failed_when: + - failure.msg != "You cannot specify host headers with SSL unless it is a wildcard certificate." + - failure.msg != "You cannot specify host headers with SSL unless it is a wildcard certificate or SNI is enabled." + +- 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 sites + win_iis_website: + name: "{{ item.name }}" + state: started + ip: 127.0.0.1 + port: "{{ item.port }}" + physical_path: c:\inetpub\wwwroot + with_items: + - {name: testconflict1, port: 8080} + - {name: testconflict2, port: 8081} + + - name: add https binding to testconflict1 + win_iis_webbinding: + name: testconflict1 + state: present + protocol: https + port: 443 + ip: 127.0.0.1 + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + + - name: add https binding to testconflict2 (expect failure) + win_iis_webbinding: + name: testconflict2 + state: present + protocol: https + ip: 127.0.0.1 + port: 443 + certificate_hash: "{{ thumbprint1.stdout_lines[0] }}" + register: failure + failed_when: '"A conflicting binding has been found on the same ip" not in failure.msg' + + - debug: + var: failure + verbosity: 1 + + always: + - name: remove websites + win_iis_website: + name: "{{ item }}" + state: absent + with_items: + - testconflict1 + - testconflict2 diff --git a/test/integration/targets/win_iis_webbinding/tasks/http.yml b/test/integration/targets/win_iis_webbinding/tasks/http.yml new file mode 100644 index 00000000000..5b435068de2 --- /dev/null +++ b/test/integration/targets/win_iis_webbinding/tasks/http.yml @@ -0,0 +1,372 @@ +#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 + +#bulk remove cm +#add multiple bindings - verify they're present +- name: bulk add http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: present + host_header: "{{ item }}" + protocol: http + ip: '*' + port: 80 + register: http_header + with_items: + - test1.com + - test2.com + - test3.com + +- name: assert that 3 bindings were added + assert: + that: + - http_header is changed + - http_header | json_query('results[*].binding_info') | length == 3 + +#cm remove with host_header: '*' - verify changed true and that bulk remove tries to get them all +#remove with host_header: '*' + +- name: bulk remove http binding with header + win_iis_webbinding: + name: "{{ test_iis_site_name }}" + state: absent + host_header: '*' + protocol: http + ip: '*' + port: 80 + register: http_header + +- name: get binding info header + test_get_webbindings: + name: "{{ test_iis_site_name }}" + host_header: "{{ item }}" + protocol: http + ip: '*' + port: 80 + register: get_http_header + changed_when: false + with_items: + - test1.com + - test2.com + - test3.com + +- name: bulk remove assert that bindings are gone + assert: + that: + - http_header is changed + - http_header.binding_info | length == 3 diff --git a/test/integration/targets/win_iis_webbinding/tasks/https-ge6.2.yml b/test/integration/targets/win_iis_webbinding/tasks/https-ge6.2.yml new file mode 100644 index 00000000000..f883c673ff0 --- /dev/null +++ b/test/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/test/integration/targets/win_iis_webbinding/tasks/https-lt6.2.yml b/test/integration/targets/win_iis_webbinding/tasks/https-lt6.2.yml new file mode 100644 index 00000000000..1950641e8dc --- /dev/null +++ b/test/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/test/integration/targets/win_iis_webbinding/tasks/main.yml b/test/integration/targets/win_iis_webbinding/tasks/main.yml new file mode 100644 index 00000000000..a561112a463 --- /dev/null +++ b/test/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] | version_compare('6.2','lt') + - include_tasks: https-ge6.2.yml + when: os_version.stdout_lines[0] | version_compare('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 + 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 + win_feature: + name: Web-Server + state: absent + includ_sub_features: True + include_management_tools: True + register: feature_uninstall + + - name: reboot after feature install + win_reboot: + when: feature_uninstall.reboot_required + when: os_version.stdout_lines[0] | version_compare('6.1','gt') diff --git a/test/integration/targets/win_iis_webbinding/tasks/setup.yml b/test/integration/targets/win_iis_webbinding/tasks/setup.yml new file mode 100644 index 00000000000..96e9cbd344b --- /dev/null +++ b/test/integration/targets/win_iis_webbinding/tasks/setup.yml @@ -0,0 +1,88 @@ +- name: reboot before feature install to ensure server is in clean state + win_reboot: + +- name: ensure IIS features are installed + win_feature: + name: Web-Server + state: present + includ_sub_features: True + include_management_tools: True + register: feature_install + +- name: reboot after feature install + 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 }}" + host_header: '*' + with_items: + - {protocol: http, port: 80} + - {protocol: https, port: 443} + +- name: copy certreq file + 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 + 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 + 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 +- debug: var=thumbprint2.stdout +- debug: var=thumbprint_wc.stdout