Skip to content

Commit 1c833e7

Browse files
Migrate win_iis_website and win_iis_webbinding
2 parents 7035882 + 2717358 commit 1c833e7

File tree

13 files changed

+2359
-0
lines changed

13 files changed

+2359
-0
lines changed
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
#!powershell
2+
3+
# Copyright: (c) 2017, Noah Sparks <[email protected]>
4+
# Copyright: (c) 2015, Henrik Wallström <[email protected]>
5+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6+
7+
#Requires -Module Ansible.ModuleUtils.Legacy
8+
9+
$params = Parse-Args -arguments $args -supports_check_mode $true
10+
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
11+
12+
$name = Get-AnsibleParam $params -name "name" -type str -failifempty $true -aliases 'website'
13+
$state = Get-AnsibleParam $params "state" -default "present" -validateSet "present", "absent"
14+
$host_header = Get-AnsibleParam $params -name "host_header" -type str
15+
$protocol = Get-AnsibleParam $params -name "protocol" -type str -default 'http'
16+
$port = Get-AnsibleParam $params -name "port" -default '80'
17+
$ip = Get-AnsibleParam $params -name "ip" -default '*'
18+
$certificateHash = Get-AnsibleParam $params -name "certificate_hash" -type str -default ([string]::Empty)
19+
$certificateStoreName = Get-AnsibleParam $params -name "certificate_store_name" -type str -default ([string]::Empty)
20+
$sslFlags = Get-AnsibleParam $params -name "ssl_flags" -default '0' -ValidateSet '0', '1', '2', '3'
21+
22+
$result = @{
23+
changed = $false
24+
}
25+
26+
#################
27+
### Functions ###
28+
#################
29+
function New-BindingInfo {
30+
$ht = @{
31+
'bindingInformation' = $args[0].bindingInformation
32+
'ip' = $args[0].bindingInformation.split(':')[0]
33+
'port' = [int]$args[0].bindingInformation.split(':')[1]
34+
'hostheader' = $args[0].bindingInformation.split(':')[2]
35+
#'isDsMapperEnabled' = $args[0].isDsMapperEnabled
36+
'protocol' = $args[0].protocol
37+
'certificateStoreName' = $args[0].certificateStoreName
38+
'certificateHash' = $args[0].certificateHash
39+
}
40+
41+
#handle sslflag support
42+
If ([version][System.Environment]::OSVersion.Version -lt [version]'6.2') {
43+
$ht.sslFlags = 'not supported'
44+
}
45+
Else {
46+
$ht.sslFlags = [int]$args[0].sslFlags
47+
}
48+
49+
Return $ht
50+
}
51+
52+
# Used instead of get-webbinding to ensure we always return a single binding
53+
# We can't filter properly with get-webbinding...ex get-webbinding ip * returns all bindings
54+
# pass it $binding_parameters hashtable
55+
function Get-SingleWebBinding {
56+
57+
Try {
58+
$site_bindings = get-webbinding -name $args[0].name
59+
}
60+
Catch {
61+
# 2k8r2 throws this error when you run get-webbinding with no bindings in iis
62+
$msg = 'Cannot process argument because the value of argument "obj" is null. Change the value of argument "obj" to a non-null value'
63+
If (-not $_.Exception.Message.CompareTo($msg)) {
64+
Throw $_.Exception.Message
65+
}
66+
Else { return }
67+
}
68+
69+
Foreach ($binding in $site_bindings) {
70+
$splits = $binding.bindingInformation -split ':'
71+
72+
if (
73+
$args[0].protocol -eq $binding.protocol -and
74+
$args[0].ipaddress -eq $splits[0] -and
75+
$args[0].port -eq $splits[1] -and
76+
$args[0].hostheader -eq $splits[2]
77+
) {
78+
Return $binding
79+
}
80+
}
81+
}
82+
83+
84+
#############################
85+
### Pre-Action Validation ###
86+
#############################
87+
$os_version = [version][System.Environment]::OSVersion.Version
88+
89+
# Ensure WebAdministration module is loaded
90+
If ($os_version -lt [version]'6.1') {
91+
Try {
92+
Add-PSSnapin WebAdministration
93+
}
94+
Catch {
95+
Fail-Json -obj $result -message "The WebAdministration snap-in is not present. Please make sure it is installed."
96+
}
97+
}
98+
Else {
99+
Try {
100+
Import-Module WebAdministration
101+
}
102+
Catch {
103+
Fail-Json -obj $result -message "Failed to load WebAdministration module. Is IIS installed? $($_.Exception.Message)"
104+
}
105+
}
106+
107+
# ensure website targetted exists. -Name filter doesn't work on 2k8r2 so do where-object instead
108+
$website_check = get-website | Where-Object { $_.name -eq $name }
109+
If (-not $website_check) {
110+
Fail-Json -obj $result -message "Unable to retrieve website with name $Name. Make sure the website name is valid and exists."
111+
}
112+
113+
# if OS older than 2012 (6.2) and ssl flags are set, fail. Otherwise toggle sni_support
114+
If ($os_version -lt [version]'6.2') {
115+
If ($sslFlags -ne 0) {
116+
Fail-Json -obj $result -message "SNI and Certificate Store support is not available for systems older than 2012 (6.2)"
117+
}
118+
$sni_support = $false #will cause the sslflags check later to skip
119+
}
120+
Else {
121+
$sni_support = $true
122+
}
123+
124+
# make sure ssl flags only specified with https protocol
125+
If ($protocol -ne 'https' -and $sslFlags -gt 0) {
126+
Fail-Json -obj $result -message "SSLFlags can only be set for HTTPS protocol"
127+
}
128+
129+
# validate certificate details if provided
130+
# we don't do anything with cert on state: absent, so only validate present
131+
If ($certificateHash -and $state -eq 'present') {
132+
If ($protocol -ne 'https') {
133+
Fail-Json -obj $result -message "You can only provide a certificate thumbprint when protocol is set to https"
134+
}
135+
136+
#apply default for cert store name
137+
If (-Not $certificateStoreName) {
138+
$certificateStoreName = 'my'
139+
}
140+
141+
#validate cert path
142+
$cert_path = "cert:\LocalMachine\$certificateStoreName\$certificateHash"
143+
If (-Not (Test-Path -LiteralPath $cert_path) ) {
144+
Fail-Json -obj $result -message "Unable to locate certificate at $cert_path"
145+
}
146+
}
147+
148+
# make sure binding info is valid for central cert store if sslflags -gt 1
149+
If ($sslFlags -gt 1 -and ($certificateHash -ne [string]::Empty -or $certificateStoreName -ne [string]::Empty)) {
150+
Fail-Json -obj $result -message "You set sslFlags to $sslFlags. This indicates you wish to use the Central Certificate Store feature.
151+
This cannot be used in combination with certficiate_hash and certificate_store_name. When using the Central Certificate Store feature,
152+
the certificate is automatically retrieved from the store rather than manually assigned to the binding."
153+
}
154+
155+
# disallow host_header: '*'
156+
If ($host_header -eq '*') {
157+
Fail-Json -obj $result -message "To make or remove a catch-all binding, please omit the host_header parameter entirely rather than specify host_header *"
158+
}
159+
160+
##########################
161+
### start action items ###
162+
##########################
163+
164+
# create binding search splat
165+
$binding_parameters = @{
166+
Name = $name
167+
Protocol = $protocol
168+
Port = $port
169+
IPAddress = $ip
170+
}
171+
172+
# insert host header to search if specified, otherwise it will return * (all bindings matching protocol/ip)
173+
If ($host_header) {
174+
$binding_parameters.HostHeader = $host_header
175+
}
176+
Else {
177+
$binding_parameters.HostHeader = [string]::Empty
178+
}
179+
180+
# Get bindings matching parameters
181+
Try {
182+
$current_bindings = Get-SingleWebBinding $binding_parameters
183+
}
184+
Catch {
185+
Fail-Json -obj $result -message "Failed to retrieve bindings with Get-SingleWebBinding - $($_.Exception.Message)"
186+
}
187+
188+
################################################
189+
### Remove binding or exit if already absent ###
190+
################################################
191+
If ($current_bindings -and $state -eq 'absent') {
192+
Try {
193+
#there is a bug in this method that will result in all bindings being removed if the IP in $current_bindings is a *
194+
#$current_bindings | Remove-WebBinding -verbose -WhatIf:$check_mode
195+
196+
#another method that did not work. It kept failing to match on element and removed everything.
197+
#$element = @{protocol="$protocol";bindingInformation="$ip`:$port`:$host_header"}
198+
#Remove-WebconfigurationProperty -filter $current_bindings.ItemXPath -Name Bindings.collection -AtElement $element -WhatIf #:$check_mode
199+
200+
#this method works
201+
[array]$bindings = Get-WebconfigurationProperty -filter $current_bindings.ItemXPath -Name Bindings.collection
202+
203+
$index = Foreach ($item in $bindings) {
204+
If ( $protocol -eq $item.protocol -and $current_bindings.bindingInformation -eq $item.bindingInformation ) {
205+
$bindings.indexof($item)
206+
break
207+
}
208+
}
209+
210+
Remove-WebconfigurationProperty -filter $current_bindings.ItemXPath -Name Bindings.collection -AtIndex $index -WhatIf:$check_mode
211+
$result.changed = $true
212+
}
213+
214+
Catch {
215+
Fail-Json -obj $result -message "Failed to remove the binding from IIS - $($_.Exception.Message)"
216+
}
217+
218+
# removing bindings from iis may not also remove them from iis:\sslbindings
219+
220+
$result.operation_type = 'removed'
221+
$result.binding_info = $current_bindings | ForEach-Object { New-BindingInfo $_ }
222+
Exit-Json -obj $result
223+
}
224+
ElseIf (-Not $current_bindings -and $state -eq 'absent') {
225+
# exit changed: false since it's already gone
226+
Exit-Json -obj $result
227+
}
228+
229+
230+
################################
231+
### Modify existing bindings ###
232+
################################
233+
<#
234+
since we have already have the parameters available to get-webbinding,
235+
we just need to check here for the ones that are not available which are the
236+
ssl settings (hash, store, sslflags). If they aren't set we update here, or
237+
exit with changed: false
238+
#>
239+
ElseIf ($current_bindings) {
240+
#ran into a strange edge case in testing where I was able to retrieve bindings but not expand all the properties
241+
#when adding a self-signed wildcard cert to a binding. it seemed to permanently break the binding. only removing it
242+
#would cause the error to stop.
243+
Try {
244+
$null = $current_bindings | Select-Object *
245+
}
246+
Catch {
247+
$msg = -join @(
248+
"Found a matching binding, but failed to expand it's properties (get-binding | FL *). "
249+
"In testing, this was caused by using a self-signed wildcard certificate. $($_.Exception.Message)"
250+
)
251+
Fail-Json -obj $result -message $msg
252+
}
253+
254+
# check if there is a match on the ssl parameters
255+
If ( ($current_bindings.sslFlags -ne $sslFlags -and $sni_support) -or
256+
$current_bindings.certificateHash -ne $certificateHash -or
257+
$current_bindings.certificateStoreName -ne $certificateStoreName) {
258+
# match/update SNI
259+
If ($current_bindings.sslFlags -ne $sslFlags -and $sni_support) {
260+
Try {
261+
Set-WebBinding -Name $name -IPAddress $ip -Port $port -HostHeader $host_header -PropertyName sslFlags -value $sslFlags -whatif:$check_mode
262+
$result.changed = $true
263+
}
264+
Catch {
265+
Fail-Json -obj $result -message "Failed to update sslFlags on binding - $($_.Exception.Message)"
266+
}
267+
268+
# Refresh the binding object since it has been changed
269+
Try {
270+
$current_bindings = Get-SingleWebBinding $binding_parameters
271+
}
272+
Catch {
273+
Fail-Json -obj $result -message "Failed to refresh bindings after setting sslFlags - $($_.Exception.Message)"
274+
}
275+
}
276+
# match/update certificate
277+
If ($current_bindings.certificateHash -ne $certificateHash -or $current_bindings.certificateStoreName -ne $certificateStoreName) {
278+
If (-Not $check_mode) {
279+
Try {
280+
$current_bindings.AddSslCertificate($certificateHash, $certificateStoreName)
281+
}
282+
Catch {
283+
Fail-Json -obj $result -message "Failed to set new SSL certificate - $($_.Exception.Message)"
284+
}
285+
}
286+
}
287+
$result.changed = $true
288+
$result.operation_type = 'updated'
289+
$result.website_state = (Get-Website | Where-Object { $_.Name -eq $Name }).State
290+
$result.binding_info = New-BindingInfo (Get-SingleWebBinding $binding_parameters)
291+
Exit-Json -obj $result #exit changed true
292+
}
293+
Else {
294+
$result.operation_type = 'matched'
295+
$result.website_state = (Get-Website | Where-Object { $_.Name -eq $Name }).State
296+
$result.binding_info = New-BindingInfo (Get-SingleWebBinding $binding_parameters)
297+
Exit-Json -obj $result #exit changed false
298+
}
299+
}
300+
301+
########################
302+
### Add new bindings ###
303+
########################
304+
ElseIf (-not $current_bindings -and $state -eq 'present') {
305+
# add binding. this creates the binding, but does not apply a certificate to it.
306+
Try {
307+
If (-not $check_mode) {
308+
If ($sni_support) {
309+
New-WebBinding @binding_parameters -SslFlags $sslFlags -Force
310+
}
311+
Else {
312+
New-WebBinding @binding_parameters -Force
313+
}
314+
}
315+
$result.changed = $true
316+
}
317+
Catch {
318+
$result.website_state = (Get-Website | Where-Object { $_.Name -eq $Name }).State
319+
Fail-Json -obj $result -message "Failed at creating new binding (note: creating binding and adding ssl are separate steps) - $($_.Exception.Message)"
320+
}
321+
322+
# add certificate to binding
323+
If ($certificateHash -and -not $check_mode) {
324+
Try {
325+
#$new_binding = get-webbinding -Name $name -IPAddress $ip -port $port -Protocol $protocol -hostheader $host_header
326+
$new_binding = Get-SingleWebBinding $binding_parameters
327+
$new_binding.addsslcertificate($certificateHash, $certificateStoreName)
328+
}
329+
Catch {
330+
$result.website_state = (Get-Website | Where-Object { $_.Name -eq $Name }).State
331+
Fail-Json -obj $result -message "Failed to set new SSL certificate - $($_.Exception.Message)"
332+
}
333+
}
334+
335+
$result.changed = $true
336+
$result.operation_type = 'added'
337+
$result.website_state = (Get-Website | Where-Object { $_.Name -eq $Name }).State
338+
339+
# incase there are no bindings we do a check before calling New-BindingInfo
340+
$web_binding = Get-SingleWebBinding $binding_parameters
341+
if ($web_binding) {
342+
$result.binding_info = New-BindingInfo $web_binding
343+
}
344+
else {
345+
$result.binding_info = $null
346+
}
347+
Exit-Json $result
348+
}

0 commit comments

Comments
 (0)