Add full IPv6 support to win_dns_client - Fixes #55962 (#57577)

* Add full IPv6 support to win_dns_client - Fixes #55962

* Fix missing cast

* Add type to win_dns_client.py

* Remove version_added again, to hopefully make ansibot happy. Even though it was added as a response to the bot...

* Fix $params undefined error, that was introduced by fixing the "global variable" linting issue

* Fix casting error

* Fix inverted logic

* Fix rebase error

* Fix assignment to readonly variable

* Fix "reset IPv4 DNS back to DHCP adapter_name"

* Fix legacy windows server support (2008/2008R2)

* Fix 2k8

* Remove unecessary pslint ignore

* Added IPv6 tests, changelog fragment and further docs
This commit is contained in:
Klaus Frank 2019-11-17 22:30:06 +01:00 committed by Jordan Borean
parent 480b106d65
commit 0efe5a666d
5 changed files with 127 additions and 88 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- win_dns_client - Added support for setting IPv6 DNS servers - https://github.com/ansible/ansible/issues/55962

View file

@ -12,6 +12,21 @@ Set-StrictMode -Version 2
$ErrorActionPreference = "Stop"
$ConfirmPreference = "None"
Set-Variable -Visibility Public -Option ReadOnly,AllScope,Constant -Name "AddressFamilies" -Value @(
[System.Net.Sockets.AddressFamily]::InterNetworkV6,
[System.Net.Sockets.AddressFamily]::InterNetwork
)
$result = @{changed=$false}
$params = Parse-Args -arguments $args -supports_check_mode $true
Set-Variable -Visibility Public -Option ReadOnly,AllScope,Constant -Name "log_path" -Value (
Get-AnsibleParam $params "log_path"
)
$adapter_names = Get-AnsibleParam $params "adapter_names" -Default "*"
$dns_servers = Get-AnsibleParam $params "dns_servers" -aliases "ipv4_addresses","ip_addresses","addresses" -FailIfEmpty $result
$check_mode = Get-AnsibleParam $params "_ansible_check_mode" -Default $false
Function Write-DebugLog {
Param(
[string]$msg
@ -23,10 +38,6 @@ Function Write-DebugLog {
$msg = "$date_str $msg"
Write-Debug $msg
$log_path = $null
$log_path = Get-AnsibleParam -obj $params -name "log_path"
if($log_path) {
Add-Content $log_path $msg
}
@ -74,7 +85,7 @@ Function Get-DnsClientServerAddressLegacy {
return @(
# IPv4 values
[PSCustomObject]@{InterfaceAlias=$InterfaceAlias;InterfaceIndex=$idx;AddressFamily=2;ServerAddresses=$adapter_config.DNSServerSearchOrder};
# IPv6, only here for completeness since we don't support it yet
# IPv6 values
[PSCustomObject]@{InterfaceAlias=$InterfaceAlias;InterfaceIndex=$idx;AddressFamily=23;ServerAddresses=@()};
)
}
@ -99,7 +110,7 @@ Function Set-DnsClientServerAddressLegacy {
$arguments = @{}
}
Else {
$arguments = @{ DNSServerSearchOrder = $ServerAddresses }
$arguments = @{ DNSServerSearchOrder = [string[]]$ServerAddresses }
}
$res = Invoke-CimMethod -InputObject $adapter_config -MethodName SetDNSServerSearchOrder -Arguments $arguments
@ -112,40 +123,47 @@ If(-not $(Get-Command Set-DnsClientServerAddress -ErrorAction SilentlyContinue))
New-Alias Set-DnsClientServerAddress Set-DnsClientServerAddressLegacy
}
Function Get-DnsClientMatch {
Function Test-DnsClientMatch {
Param(
[string] $adapter_name,
[string[]] $ipv4_addresses
[System.Net.IPAddress[]] $dns_servers
)
Write-DebugLog ("Getting DNS config for adapter {0}" -f $adapter_name)
$current_dns_all = Get-DnsClientServerAddress -InterfaceAlias $adapter_name
$current_dns = ([System.Net.IPAddress[]](
(Get-DnsClientServerAddress -InterfaceAlias $adapter_name).ServerAddresses) | Where-Object {
(Assert-IPAddress $_) -and ($_.AddressFamily -in $AddressFamilies)
}
)
Write-DebugLog ("Current DNS settings: {0}" -f ([string[]]$dns_servers -join ", "))
Write-DebugLog ("Current DNS settings: " + $($current_dns_all | Out-String))
$current_dns_v4 = ($current_dns_all | Where-Object AddressFamily -eq 2 <# IPv4 #>).ServerAddresses
If (($null -eq $current_dns_v4) -and ($null -eq $ipv4_addresses)) {
$v4_match = $True
if(($null -eq $current_dns) -and ($null -eq $dns_servers)) {
Write-DebugLog "Neither are dns servers configured nor specified within the playbook."
return $true
} elseif ($null -eq $current_dns) {
Write-DebugLog "There are currently no dns servers specified, but they should be present."
return $false
} elseif ($null -eq $dns_servers) {
Write-DebugLog "There are currently dns servers specified, but they should be absent."
return $false
}
ElseIf (($null -eq $current_dns_v4) -or ($null -eq $ipv4_addresses)) {
$v4_match = $False
foreach($address in $current_dns) {
if($address -notin $dns_servers) {
Write-DebugLog "There are currently fewer dns servers present than specified within the playbook."
return $false
}
}
Else {
$v4_match = @(Compare-Object $current_dns_v4 $ipv4_addresses -SyncWindow 0).Count -eq 0
foreach($address in $dns_servers) {
if($address -notin $current_dns) {
Write-DebugLog "There are currently further dns servers present than specified within the playbook."
return $false
}
}
# TODO: implement IPv6
Write-DebugLog ("Current DNS settings match ({0}) : {1}" -f ($ipv4_addresses -join ", "), $v4_match)
return $v4_match
Write-DebugLog ("Current DNS settings match ({0})." -f ([string[]]$dns_servers -join ", "))
return $true
}
Function Validate-IPAddress {
Function Assert-IPAddress {
Param([string] $address)
$addrout = $null
@ -157,72 +175,54 @@ Function Set-DnsClientAddresses
{
Param(
[string] $adapter_name,
[string[]] $ipv4_addresses
[System.Net.IPAddress[]] $dns_servers
)
Write-DebugLog ("Setting DNS addresses for adapter {0} to ({1})" -f $adapter_name, ($ipv4_addresses -join ", "))
Write-DebugLog ("Setting DNS addresses for adapter {0} to ({1})" -f $adapter_name, ([string[]]$dns_servers -join ", "))
If ($null -eq $ipv4_addresses) {
If ($dns_servers) {
Set-DnsClientServerAddress -InterfaceAlias $adapter_name -ServerAddresses $dns_servers
} Else {
Set-DnsClientServerAddress -InterfaceAlias $adapter_name -ResetServerAddress
}
Else {
# this silently ignores invalid IPs, so we validate parseability ourselves up front...
Set-DnsClientServerAddress -InterfaceAlias $adapter_name -ServerAddresses $ipv4_addresses
}
# TODO: implement IPv6
}
$result = @{changed=$false}
$params = Parse-Args -arguments $args -supports_check_mode $true
$adapter_names = Get-AnsibleParam $params "adapter_names" -Default "*"
$ipv4_addresses = Get-AnsibleParam $params "ipv4_addresses" -FailIfEmpty $result
If($ipv4_addresses -is [string]) {
If($ipv4_addresses.Length -gt 0) {
$ipv4_addresses = @($ipv4_addresses)
}
Else {
$ipv4_addresses = @()
if($dns_servers -is [string]) {
if($dns_servers.Length -gt 0) {
$dns_servers = @($dns_servers)
} else {
$dns_servers = @()
}
}
# Using object equals here, to check for exact match (without implicit type conversion)
if([System.Object]::Equals($adapter_names, "*")) {
$adapter_names = Get-NetAdapter | Select-Object -ExpandProperty Name
}
if($adapter_names -is [string]) {
$adapter_names = @($adapter_names)
}
$check_mode = Get-AnsibleParam $params "_ansible_check_mode" -Default $false
Try {
Write-DebugLog ("Validating adapter name {0}" -f $adapter_names)
$adapters = @($adapter_names)
If($adapter_names -eq "*") {
$adapters = Get-NetAdapter | Select-Object -ExpandProperty Name
}
# TODO: add support for an actual list of adapter names
# validate network adapter names
ElseIf(@(Get-NetAdapter | Where-Object Name -eq $adapter_names).Count -eq 0) {
throw "Invalid network adapter name: {0}" -f $adapter_names
}
Write-DebugLog ("Validating IP addresses ({0})" -f ($ipv4_addresses -join ", "))
$invalid_addresses = @($ipv4_addresses | Where-Object { -not (Validate-IPAddress $_) })
If($invalid_addresses.Count -gt 0) {
Write-DebugLog ("Validating IP addresses ({0})" -f ($dns_servers -join ", "))
$invalid_addresses = @($dns_servers | Where-Object { -not (Assert-IPAddress $_) })
if($invalid_addresses.Count -gt 0) {
throw "Invalid IP address(es): ({0})" -f ($invalid_addresses -join ", ")
}
ForEach($adapter_name in $adapters) {
$result.changed = $result.changed -or (-not (Get-DnsClientMatch $adapter_name $ipv4_addresses))
If($result.changed) {
If(-not $check_mode) {
Set-DnsClientAddresses $adapter_name $ipv4_addresses
}
Else {
foreach($adapter_name in $adapter_names) {
Write-DebugLog ("Validating adapter name {0}" -f $adapter_name)
if(-not (Get-DnsClientServerAddress -InterfaceAlias $adapter_name)) {
# TODO: add support for an actual list of adapter names
# validate network adapter names
throw "Invalid network adapter name: {0}" -f $adapter_name
}
if(-not (Test-DnsClientMatch $adapter_name $dns_servers)) {
$result.changed = $true
if(-not $check_mode) {
Set-DnsClientAddresses $adapter_name $dns_servers
} else {
Write-DebugLog "Check mode, skipping"
}
}

View file

@ -19,15 +19,18 @@ options:
adapter_names:
description:
- Adapter name or list of adapter names for which to manage DNS settings ('*' is supported as a wildcard value).
- The adapter name used is the connection caption in the Network Control Panel or via C(Get-NetAdapter), eg C(Local Area Connection).
type: str
- The adapter name used is the connection caption in the Network Control Panel or the InterfaceAlias of C(Get-DnsClientServerAddress).
type: list
required: yes
ipv4_addresses:
dns_servers:
description:
- Single or ordered list of DNS server IPv4 addresses to configure for lookup. An empty list will configure the adapter to use the
- Single or ordered list of DNS servers (IPv4 and IPv6 addresses) to configure for lookup. An empty list will configure the adapter to use the
DHCP-assigned values on connections where DHCP is enabled, or disable DNS lookup on statically-configured connections.
type: str
- IPv6 DNS servers can only be set on Windows Server 2012 or newer, older hosts can only set IPv4 addresses.
- Before 2.10 use ipv4_addresses instead.
type: list
required: yes
aliases: [ "ipv4_addresses", "ip_addresses", "addresses" ]
notes:
- When setting an empty list of DNS server addresses on an adapter with DHCP enabled, a change will always be registered, since it is not possible to
detect the difference between a DHCP-sourced server value and one that is statically set.
@ -39,20 +42,27 @@ EXAMPLES = r'''
- name: Set a single address on the adapter named Ethernet
win_dns_client:
adapter_names: Ethernet
ipv4_addresses: 192.168.34.5
dns_servers: 192.168.34.5
- name: Set multiple lookup addresses on all visible adapters (usually physical adapters that are in the Up state), with debug logging to a file
win_dns_client:
adapter_names: '*'
ipv4_addresses:
dns_servers:
- 192.168.34.5
- 192.168.34.6
log_path: C:\dns_log.txt
- name: Set IPv6 DNS servers on the adapter named Ethernet
win_dns_client:
adapter_names: Ethernet
dns_servers:
- '2001:db8::2'
- '2001:db8::3'
- name: Configure all adapters whose names begin with Ethernet to use DHCP-assigned DNS values
win_dns_client:
adapter_names: 'Ethernet*'
ipv4_addresses: []
dns_servers: []
'''
RETURN = r'''

View file

@ -176,3 +176,31 @@
that:
- set_dhcp is changed
- set_dhcp_actual.stdout_lines == []
# Legacy WMI does not support setting IPv6 addresses so we can only test this on newer hosts that have the new cmdlets
- name: check if server supports IPv6
win_shell: if (Get-Command -Name Get-NetAdapter -ErrorAction SilentlyContinue) { $true } else { $false }
changed_when: no
register: new_os
- name: run IPv6 tests
when: new_os.stdout | trim | bool
block:
- name: set IPv6 DNS address
win_dns_client:
adapter_names: '{{ network_adapter_name }}'
dns_servers:
- 2001:db8::1
- 2001:db8::2
register: set_ipv6
- name: get result of set IPv6 DNS address
win_shell: (Get-DnsClientServerAddress -InterfaceAlias '{{ network_adapter_name }}' -AddressFAmily IPv6).ServerAddresses
changed_when: no
register: set_ipv6_actual
- name: assert set IPv6 DNS address
assert:
that:
- set_ipv6 is changed
- set_ipv6_actual.stdout_lines == ['2001:db8::1', '2001:db8::2']

View file

@ -4987,7 +4987,6 @@ lib/ansible/modules/windows/win_copy.ps1 pslint:PSUseApprovedVerbs
lib/ansible/modules/windows/win_credential.ps1 pslint:PSCustomUseLiteralPath
lib/ansible/modules/windows/win_credential.ps1 validate-modules:parameter-type-not-in-doc
lib/ansible/modules/windows/win_dns_client.ps1 pslint:PSCustomUseLiteralPath
lib/ansible/modules/windows/win_dns_client.ps1 pslint:PSUseApprovedVerbs
lib/ansible/modules/windows/win_domain.ps1 pslint:PSAvoidUsingEmptyCatchBlock # Keep
lib/ansible/modules/windows/win_domain.ps1 pslint:PSUseApprovedVerbs
lib/ansible/modules/windows/win_domain_controller.ps1 pslint:PSAvoidGlobalVars # New PR