1 #!powershell
2
3 # Copyright: (c) 2020 VMware, Inc. All Rights Reserved.
4 # SPDX-License-Identifier: GPL-3.0-only
5 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7 #AnsibleRequires -CSharpUtil Ansible.Basic
8
9 $spec = @{
10 options = @{
11 name = @{ type = "str"; required = $true }
12 type = @{ type = "str"; choices = "primary", "secondary", "forwarder", "stub" }
13 replication = @{ type = "str"; choices = "forest", "domain", "legacy", "none" }
14 dynamic_update = @{ type = "str"; choices = "secure", "none", "nonsecureandsecure" }
15 state = @{ type = "str"; choices = "absent", "present"; default = "present" }
16 forwarder_timeout = @{ type = "int" }
17 dns_servers = @{ type = "list"; elements = "str" }
18 }
19 supports_check_mode = $true
20 }
21
22 $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
23 $check_mode = $module.CheckMode
24
25 $name = $module.Params.name
26 $type = $module.Params.type
27 $replication = $module.Params.replication
28 $dynamic_update = $module.Params.dynamic_update
29 $state = $module.Params.state
30 $dns_servers = $module.Params.dns_servers
31 $forwarder_timeout = $module.Params.forwarder_timeout
32
33 $parms = @{ name = $name }
34
Get-DnsZoneObject()35 Function Get-DnsZoneObject {
36 Param([PSObject]$Object)
37 $parms = @{
38 name = $Object.ZoneName.toLower()
39 type = $Object.ZoneType.toLower()
40 paused = $Object.IsPaused
41 shutdown = $Object.IsShutdown
42 }
43
44 if ($Object.DynamicUpdate) { $parms.dynamic_update = $Object.DynamicUpdate.toLower() }
45 if ($Object.IsReverseLookupZone) { $parms.reverse_lookup = $Object.IsReverseLookupZone }
46 if ($Object.ZoneType -like 'forwarder' ) { $parms.forwarder_timeout = $Object.ForwarderTimeout }
47 if ($Object.MasterServers) { $parms.dns_servers = $Object.MasterServers.IPAddressToString }
48 if (-not $Object.IsDsIntegrated) {
49 $parms.replication = "none"
50 $parms.zone_file = $Object.ZoneFile
51 } else {
52 $parms.replication = $Object.ReplicationScope.toLower()
53 }
54
55 return $parms | Sort-Object
56 }
57
Compare-DnsZonenull58 Function Compare-DnsZone {
59 Param(
60 [PSObject]$Original,
61 [PSObject]$Updated)
62
63 if ($Original -eq $false) { return $false }
64 $props = @('ZoneType', 'DynamicUpdate', 'IsDsIntegrated', 'MasterServers', 'ForwarderTimeout', 'ReplicationScope')
65 $x = Compare-Object $Original $Updated -Property $props
66 $x.Count -eq 0
67 }
68
69 # attempt import of module
70 Try { Import-Module DnsServer }
71 Catch { $module.FailJson("The DnsServer module failed to load properly: $($_.Exception.Message)", $_) }
72
73 Try {
74 # determine current zone state
75 $current_zone = Get-DnsServerZone -name $name
76 $module.Diff.before = Get-DnsZoneObject -Object $current_zone
77 if (-not $type) { $type = $current_zone.ZoneType.toLower() }
78 if ($current_zone.ZoneType -like $type) { $current_zone_type_match = $true }
79 # check for fast fails
80 if ($current_zone.ReplicationScope -like 'none' -and $replication -in @('legacy', 'forest', 'domain')) {
81 $module.FailJson("Converting a file backed DNS zone to Active Directory integrated zone is unsupported")
82 }
83 if ($current_zone.ReplicationScope -in @('legacy', 'forest', 'domain') -and $replication -like 'none') {
84 $module.FailJson("Converting Active Directory integrated zone to a file backed DNS zone is unsupported")
85 }
86 if ($current_zone.IsDsIntegrated -eq $false -and $parms.DynamicUpdate -eq 'secure') {
87 $module.FailJson("The secure dynamic update option is only available for Active Directory integrated zones")
88 }
89 } Catch {
90 $module.Diff.before = ""
91 $current_zone = $false
92 }
93
94 if ($state -eq "present") {
95 # parse replication/zonefile
96 if (-not $replication -and $current_zone) {
97 $parms.ReplicationScope = $current_zone.ReplicationScope
98 } elseif ((($replication -eq 'none') -or (-not $replication)) -and (-not $current_zone)) {
99 $parms.ZoneFile = "$name.dns"
100 } elseif (($replication -eq 'none') -and ($current_zone)) {
101 $parms.ZoneFile = "$name.dns"
102 } else {
103 $parms.ReplicationScope = $replication
104 }
105 # parse param
106 if ($dynamic_update) { $parms.DynamicUpdate = $dynamic_update }
107 if ($dns_servers) { $parms.MasterServers = $dns_servers }
108 if ($type -in @('stub','forwarder','secondary') -and -not $current_zone -and -not $dns_servers) {
109 $module.FailJson("The dns_servers param is required when creating new stub, forwarder or secondary zones")
110 }
111 switch ($type) {
112 "primary" {
113 # remove irrelevant params
114 $parms.Remove('MasterServers')
115 if ($parms.ZoneFile -and ($dynamic_update -in @('secure','nonsecureandsecure'))) {
116 $parms.Remove('DynamicUpdate')
117 $module.Warn("Secure DNS updates are available only for Active Directory-integrated zones")
118 }
119 if (-not $current_zone) {
120 # create zone
121 Try { Add-DnsServerPrimaryZone @parms -WhatIf:$check_mode }
122 Catch { $module.FailJson("Failed to add $type zone $($name): $($_.Exception.Message)", $_) }
123 } else {
124 # update zone
125 if (-not $current_zone_type_match) {
126 Try {
127 if ($current_zone.ReplicationScope) { $parms.ReplicationScope = $current_zone.ReplicationScope } else { $parms.Remove('ReplicationScope') }
128 if ($current_zone.ZoneFile) { $parms.ZoneFile = $current_zone.ZoneFile } else { $parms.Remove('ReplicationScope') }
129 if ($current_zone.IsShutdown) { $module.FailJson("Failed to convert DNS zone $($name): this zone is shutdown and cannot be modified") }
130 ConvertTo-DnsServerPrimaryZone @parms -Force -WhatIf:$check_mode
131 }
132 Catch { $module.FailJson("Failed to convert DNS zone $($name): $($_.Exception.Message)", $_) }
133 }
134 Try {
135 if (-not $parms.ZoneFile) { Set-DnsServerPrimaryZone -Name $name -ReplicationScope $parms.ReplicationScope -WhatIf:$check_mode }
136 if ($dynamic_update) { Set-DnsServerPrimaryZone -Name $name -DynamicUpdate $parms.DynamicUpdate -WhatIf:$check_mode }
137 }
138 Catch { $module.FailJson("Failed to set properties on the zone $($name): $($_.Exception.Message)", $_) }
139 }
140 }
141 "secondary" {
142 # remove irrelevant params
143 $parms.Remove('ReplicationScope')
144 $parms.Remove('DynamicUpdate')
145 if (-not $current_zone) {
146 # enforce param
147 $parms.ZoneFile = "$name.dns"
148 # create zone
149 Try { Add-DnsServerSecondaryZone @parms -WhatIf:$check_mode }
150 Catch { $module.FailJson("Failed to add $type zone $($name): $($_.Exception.Message)", $_) }
151 } else {
152 # update zone
153 if (-not $current_zone_type_match) {
154 $parms.MasterServers = $current_zone.MasterServers
155 $parms.ZoneFile = $current_zone.ZoneFile
156 if ($current_zone.IsShutdown) { $module.FailJson("Failed to convert DNS zone $($name): this zone is shutdown and cannot be modified") }
157 Try { ConvertTo-DnsServerSecondaryZone @parms -Force -WhatIf:$check_mode }
158 Catch { $module.FailJson("Failed to convert DNS zone $($name): $($_.Exception.Message)", $_) }
159 }
160 Try { if ($dns_servers) { Set-DnsServerSecondaryZone -Name $name -MasterServers $dns_servers -WhatIf:$check_mode } }
161 Catch { $module.FailJson("Failed to set properties on the zone $($name): $($_.Exception.Message)", $_) }
162 }
163 }
164 "stub" {
165 $parms.Remove('DynamicUpdate')
166 if (-not $current_zone) {
167 # create zone
168 Try { Add-DnsServerStubZone @parms -WhatIf:$check_mode }
169 Catch { $module.FailJson("Failed to add $type zone $($name): $($_.Exception.Message)", $_) }
170 } else {
171 # update zone
172 if (-not $current_zone_type_match) { $module.FailJson("Failed to convert DNS zone $($name) to $type, unsupported conversion") }
173 Try {
174 if ($parms.ReplicationScope) { Set-DnsServerStubZone -Name $name -ReplicationScope $parms.ReplicationScope -WhatIf:$check_mode }
175 if ($forwarder_timeout) { Set-DnsServerStubZone -Name $name -ForwarderTimeout $forwarder_timeout -WhatIf:$check_mode }
176 if ($dns_servers) { Set-DnsServerStubZone -Name $name -MasterServers $dns_servers -WhatIf:$check_mode }
177 }
178 Catch { $module.FailJson("Failed to set properties on the zone $($name): $($_.Exception.Message)", $_) }
179 }
180 }
181 "forwarder" {
182 # remove irrelevant params
183 $parms.Remove('DynamicUpdate')
184 $parms.Remove('ZoneFile')
185 if ($forwarder_timeout -and ($forwarder_timeout -in 0..15)) {
186 $parms.ForwarderTimeout = $forwarder_timeout
187 }
188 if ($forwarder_timeout -and -not ($forwarder_timeout -in 0..15)) {
189 $module.Warn("The forwarder_timeout param must be an integer value between 0 and 15")
190 }
191 if ($parms.ReplicationScope -eq 'none'){$parms.Remove('ReplicationScope')}
192 if (-not $current_zone) {
193 # create zone
194 Try { Add-DnsServerConditionalForwarderZone @parms -WhatIf:$check_mode }
195 Catch { $module.FailJson("Failed to add $type zone $($name): $($_.Exception.Message)", $_) }
196 } else {
197 # update zone
198 if (-not $current_zone_type_match) { $module.FailJson("Failed to convert DNS zone $($name) to $type, unsupported conversion") }
199 Try {
200 if ($parms.ReplicationScope) { Set-DnsServerConditionalForwarderZone -Name $name -ReplicationScope $parms.ReplicationScope -WhatIf:$check_mode }
201 if ($forwarder_timeout) { Set-DnsServerConditionalForwarderZone -Name $name -ForwarderTimeout $forwarder_timeout -WhatIf:$check_mode }
202 if ($dns_servers) { Set-DnsServerConditionalForwarderZone -Name $name -MasterServers $dns_servers -WhatIf:$check_mode }
203 }
204 Catch { $module.FailJson("Failed to set properties on the zone $($name): $($_.Exception.Message)", $_) }
205 }
206 }
207 }
208 }
209
210 if ($state -eq "absent") {
211 if ($current_zone -and -not $check_mode) {
212 Try {
213 Remove-DnsServerZone -Name $name -Force -WhatIf:$check_mode
214 $module.Result.changed = $true
215 $module.Diff.after = ""
216 } Catch {
217 $module.FailJson("Failed to remove DNS zone: $($_.Exception.Message)", $_)
218 }
219 }
220 $module.ExitJson()
221 }
222
223 # determine if a change was made
224 Try {
225 $new_zone = Get-DnsServerZone -Name $name
226 if (-not (Compare-DnsZone -Original $current_zone -Updated $new_zone)) {
227 $module.Result.changed = $true
228 $module.Result.zone = Get-DnsZoneObject -Object $new_zone
229 $module.Diff.after = Get-DnsZoneObject -Object $new_zone
230 }
231
232 # simulate changes if check mode
233 if ($check_mode) {
234 $new_zone = @{}
235 $current_zone.PSObject.Properties | ForEach-Object {
236 if($parms[$_.Name]) {
237 $new_zone[$_.Name] = $parms[$_.Name]
238 } else {
239 $new_zone[$_.Name] = $_.Value
240 }
241 }
242 $module.Diff.after = Get-DnsZoneObject -Object $new_zone
243 }
244 } Catch {
245 $module.FailJson("Failed to lookup new zone $($name): $($_.Exception.Message)", $_)
246 }
247
248 $module.ExitJson()