mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 12:50:22 -07:00
win_dns_record (#51925)
* win_dns: Initial work * win_dns: initial commit * Renaming win_dns -> win_dns_record * win_dns_record: Fix record leakage in output * win_dns_record: Fix erroneous minimums enforcement It is apparently completely legitimate to specify a TTL that is below minimum; it will just get ignored in favor of the server's minimum. * win_dns_record: Fix new-host changes reported incorrectly * win_dns_record: Fix TTL changes reported incorrectly * win_dns_record: Fix existing records not recognized * win_dns_record: Remove obsolete object * Refactorize check mode * Add computer_name parameter * Refactorize diff and changed to read DNS end state * Fix pslint tests PSUseDeclaredVarsMoreThanAssignments and PSAvoidUsingCmdletAliases * Minor fix, misnamed variable. * win_dns_record: Fix "changed" state in check mode * win_dns_record: cleanups * win_dns_record: fix TTL update not changed regression * Add initial integration tests * win_dns_record: integration tests * win_dns_record: Reverted 9cf5f2d8e6507cf477ab9e7ca166b1857169d6b5 The approach from that commit breaks check mode. * win_dns_record: de-scope some records These are either esoteric (meaning limited realworld testing) or require additional thought to do properly (eg MX, which has its "priority" level). * win_dns_records tests: Ensure DNS services are installed * Update lib/ansible/modules/windows/win_dns_record.py Co-Authored-By: johnboy2 * Update lib/ansible/modules/windows/win_dns_record.py Co-Authored-By: johnboy2 * Aggregated suggestions from dagwieers * Fix bad powershell test argument * win_dns_record partially converted to new Powershell module interface win_dns_record converted to new Powershell module interface, except diff and required_if * win_dns_record: convert diff support * win_dns_record: convert diff support to after-before style * Don't test for Add-WindowsFeature * win_dns_record: Fix diff When check mode is used diff changes must be simulated. * Style consistency/clean-ups * Fix integration test typos * Improve readability of diff output The original intention of the diff output was to resemble zone file records (except that the zone-name is added onto each record). In that light, the missing *record class* information (always "IN" in our case) was an oversight. This just makes the diff output more "instantly readable" for DNS gurus. * win_dns_record: Add diff tests * Fix ansible-test sanity check fails * Apply suggestions from code review Added suggestions from dagwieers Co-Authored-By: johnboy2 <john@jnelson.ca> * win_dns_record: Skip 2008 and friends * Reword error messages so they start capitalized. * Fix sanity error * win_dns_record: Document ttl range * win_dns_record: Additional supportability barriers in tests * win_dns_record: Typo * win_dns_record: Sanity fix * win_dns_record: Use OS-test only for compat checking
This commit is contained in:
parent
9744ef80a0
commit
38346d0337
12 changed files with 1111 additions and 0 deletions
149
lib/ansible/modules/windows/win_dns_record.ps1
Normal file
149
lib/ansible/modules/windows/win_dns_record.ps1
Normal file
|
@ -0,0 +1,149 @@
|
|||
#!powershell
|
||||
|
||||
# Copyright: (c) 2019, Hitachi ID Systems, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
name = @{ type = "str"; required = $true }
|
||||
state = @{ type = "str"; choices = "absent", "present"; default = "present" }
|
||||
ttl = @{ type = "int"; default = "3600" }
|
||||
type = @{ type = "str"; choices = "A","AAAA","CNAME","PTR"; required = $true }
|
||||
value = @{ type = "list"; elements = "str"; default = @() ; aliases=@( 'values' )}
|
||||
zone = @{ type = "str"; required = $true }
|
||||
computer_name = @{ type = "str" }
|
||||
}
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$name = $module.Params.name
|
||||
$state = $module.Params.state
|
||||
$ttl = $module.Params.ttl
|
||||
$type = $module.Params.type
|
||||
$values = $module.Params.value
|
||||
$zone = $module.Params.zone
|
||||
$dns_computer_name = $module.Params.computer_name
|
||||
|
||||
|
||||
$extra_args = @{}
|
||||
if ($null -ne $dns_computer_name) {
|
||||
$extra_args.ComputerName = $dns_computer_name
|
||||
}
|
||||
|
||||
if ($state -eq 'present') {
|
||||
if ($values.Count -eq 0) {
|
||||
$module.FailJson("Parameter 'values' must be non-empty when state='present'")
|
||||
}
|
||||
} else {
|
||||
if ($values.Count -ne 0) {
|
||||
$module.FailJson("Parameter 'values' must be undefined or empty when state='absent'")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# TODO: add warning for forest minTTL override -- see https://docs.microsoft.com/en-us/windows/desktop/ad/configuration-of-ttl-limits
|
||||
if ($ttl -lt 1 -or $ttl -gt 31557600) {
|
||||
$module.FailJson("Parameter 'ttl' must be between 1 and 31557600")
|
||||
}
|
||||
$ttl = New-TimeSpan -Seconds $ttl
|
||||
|
||||
|
||||
if (($type -eq 'CNAME' -or $type -eq 'PTR') -and $null -ne $values -and $values.Count -gt 0 -and $zone[-1] -ne '.') {
|
||||
# CNAMEs and PTRs should be '.'-terminated, or record matching will fail
|
||||
$values = $values | ForEach-Object {
|
||||
if ($_ -Like "*.") { $_ } else { "$_." }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$record_argument_name = @{
|
||||
A = "IPv4Address";
|
||||
AAAA = "IPv6Address";
|
||||
CNAME = "HostNameAlias";
|
||||
# MX = "MailExchange";
|
||||
# NS = "NameServer";
|
||||
PTR = "PtrDomainName";
|
||||
# TXT = "DescriptiveText"
|
||||
}[$type]
|
||||
|
||||
|
||||
$changes = @{
|
||||
before = "";
|
||||
after = ""
|
||||
}
|
||||
|
||||
|
||||
$records = Get-DnsServerResourceRecord -ZoneName $zone -Name $name -RRType $type -Node -ErrorAction:Ignore @extra_args | Sort-Object
|
||||
if ($null -ne $records) {
|
||||
# We use [Hashtable]$required_values below as a set rather than a map.
|
||||
# It provides quick lookup to test existing DNS record against. By removing
|
||||
# items as each is processed, whatever remains at the end is missing
|
||||
# content (that needs to be added).
|
||||
$required_values = @{}
|
||||
foreach ($value in $values) {
|
||||
$required_values[$value.ToString()] = $null
|
||||
}
|
||||
|
||||
foreach ($record in $records) {
|
||||
$record_value = $record.RecordData.$record_argument_name.ToString()
|
||||
|
||||
if ($required_values.ContainsKey($record_value)) {
|
||||
# This record matches one of the values; but does it match the TTL?
|
||||
if ($record.TimeToLive -ne $ttl) {
|
||||
$new_record = $record.Clone()
|
||||
$new_record.TimeToLive = $ttl
|
||||
Set-DnsServerResourceRecord -ZoneName $zone -OldInputObject $record -NewInputObject $new_record -WhatIf:$module.CheckMode @extra_args
|
||||
|
||||
$changes.before += "[$zone] $($record.HostName) $($record.TimeToLive.TotalSeconds) IN $type $record_value`n"
|
||||
$changes.after += "[$zone] $($record.HostName) $($ttl.TotalSeconds) IN $type $record_value`n"
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
|
||||
# Cross this one off the list, so we don't try adding it later
|
||||
$required_values.Remove($record_value)
|
||||
} else {
|
||||
# This record doesn't match any of the values, and must be removed
|
||||
$record | Remove-DnsServerResourceRecord -ZoneName $zone -Force -WhatIf:$module.CheckMode @extra_args
|
||||
|
||||
$changes.before += "[$zone] $($record.HostName) $($record.TimeToLive.TotalSeconds) IN $type $record_value`n"
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
}
|
||||
|
||||
# Whatever is left in $required_values needs to be added
|
||||
$values = $required_values.Keys
|
||||
}
|
||||
|
||||
|
||||
if ($null -ne $values -and $values.Count -gt 0) {
|
||||
foreach ($value in $values) {
|
||||
$splat_args = @{ $type = $true; $record_argument_name = $value }
|
||||
$module.Result.debug_splat_args = $splat_args
|
||||
try {
|
||||
Add-DnsServerResourceRecord -ZoneName $zone -Name $name -AllowUpdateAny -TimeToLive $ttl @splat_args -WhatIf:$module.CheckMode @extra_args
|
||||
} catch {
|
||||
$module.FailJson("Error adding DNS $type resource $name in zone $zone with value $value", $_)
|
||||
}
|
||||
$changes.after += "[$zone] $name $($ttl.TotalSeconds) IN $type $value`n"
|
||||
}
|
||||
|
||||
$module.Result.changed = $true
|
||||
}
|
||||
|
||||
if ($module.CheckMode) {
|
||||
# Simulated changes
|
||||
$module.Diff.before = $changes.before
|
||||
$module.Diff.after = $changes.after
|
||||
} else {
|
||||
# Real changes
|
||||
$records_end = Get-DnsServerResourceRecord -ZoneName $zone -Name $name -RRType $type -Node -ErrorAction:Ignore @extra_args | Sort-Object
|
||||
|
||||
$module.Diff.before = @($records | ForEach-Object { "[$zone] $($_.HostName) $($_.TimeToLive.TotalSeconds) IN $type $($_.RecordData.$record_argument_name.ToString())`n" }) -join ''
|
||||
$module.Diff.after = @($records_end | ForEach-Object { "[$zone] $($_.HostName) $($_.TimeToLive.TotalSeconds) IN $type $($_.RecordData.$record_argument_name.ToString())`n" }) -join ''
|
||||
}
|
||||
|
||||
$module.ExitJson()
|
88
lib/ansible/modules/windows/win_dns_record.py
Normal file
88
lib/ansible/modules/windows/win_dns_record.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2019, Hitachi ID Systems, Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# This is a windows documentation stub. The actual code lives in the .ps1
|
||||
# file of the same name.
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: win_dns_record
|
||||
version_added: "2.8"
|
||||
short_description: Manage Windows Server DNS records
|
||||
description:
|
||||
- Manage DNS records within an existing Windows Server DNS zone.
|
||||
author: John Nelson (@johnboy2)
|
||||
requirements:
|
||||
- This module requires Windows 8, Server 2012, or newer.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the record.
|
||||
required: yes
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Whether the record should exist or not.
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
type: str
|
||||
ttl:
|
||||
description:
|
||||
- The "time to live" of the record, in seconds.
|
||||
- Ignored when C(state=absent).
|
||||
- Valid range is 1 - 31557600.
|
||||
- Note that an Active Directory forest can specify a minimum TTL, and will
|
||||
dynamically "round up" other values to that minimum.
|
||||
default: 3600
|
||||
type: int
|
||||
type:
|
||||
description:
|
||||
- The type of DNS record to manage.
|
||||
choices: [ A, AAAA, CNAME, PTR ]
|
||||
required: yes
|
||||
type: str
|
||||
value:
|
||||
description:
|
||||
- The value(s) to specify. Required when C(state=present).
|
||||
aliases: [ values ]
|
||||
type: list
|
||||
zone:
|
||||
description:
|
||||
- The name of the zone to manage (eg C(example.com)).
|
||||
- The zone must already exist.
|
||||
required: yes
|
||||
type: str
|
||||
computer_name:
|
||||
description:
|
||||
- Specifies a DNS server.
|
||||
- You can specify an IP address or any value that resolves to an IP
|
||||
address, such as a fully qualified domain name (FQDN), host name, or
|
||||
NETBIOS name.
|
||||
type: str
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Create database server alias
|
||||
win_dns_record:
|
||||
name: db1
|
||||
type: CNAME
|
||||
value: cgyl1404p.amer.example.com
|
||||
zone: amer.example.com
|
||||
|
||||
- name: Remove static record
|
||||
win_dns_record:
|
||||
name: db1
|
||||
type: A
|
||||
state: absent
|
||||
zone: amer.example.com
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
'''
|
Loading…
Add table
Add a link
Reference in a new issue