DSCResources/DSC_IPAddress/DSC_IPAddress.psm1
$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' # Import the Networking Common Modules Import-Module -Name (Join-Path -Path $modulePath ` -ChildPath (Join-Path -Path 'NetworkingDsc.Common' ` -ChildPath 'NetworkingDsc.Common.psm1')) Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') # Import Localization Strings $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' <# .SYNOPSIS Returns the current state of an IP address assigned to an interface. .PARAMETER IPAddress The desired IP address. .PARAMETER InterfaceAlias Alias of the network interface for which the IP address should be set. .PARAMETER AddressFamily IP address family. .PARAMETER KeepExistingAddress Indicates whether or not existing IP addresses on an interface will be retained. #> function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [Parameter()] [ValidateNotNullOrEmpty()] [System.String[]] $IPAddress, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $InterfaceAlias, [Parameter(Mandatory = $true)] [ValidateSet('IPv4', 'IPv6')] [System.String] $AddressFamily = 'IPv4', [Parameter()] [System.Boolean] $KeepExistingAddress = $false ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($script:localizedData.GettingIPAddressMessage) ) -join '') $getNetIPAddressParameters = @{ InterfaceAlias = $InterfaceAlias AddressFamily = $AddressFamily } $currentIPAddress = Get-NetIPAddress @getNetIPAddressParameters $currentIPAddressWithPrefix = $currentIPAddress | Foreach-Object { "$($_.IPAddress)/$($_.prefixLength)" } $returnValue = @{ IPAddress = @($currentIPAddressWithPrefix) AddressFamily = $AddressFamily InterfaceAlias = $InterfaceAlias KeepExistingAddress = $KeepExistingAddress } return $returnValue } <# .SYNOPSIS Sets an IP address on an interface. .PARAMETER IPAddress The desired IP address. .PARAMETER InterfaceAlias Alias of the network interface for which the IP address should be set. .PARAMETER AddressFamily IP address family. .PARAMETER KeepExistingAddress Indicates whether or not existing IP addresses on an interface will be retained. #> function Set-TargetResource { [CmdletBinding()] param ( [Parameter()] [ValidateNotNullOrEmpty()] [System.String[]] $IPAddress, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $InterfaceAlias, [Parameter(Mandatory = $true)] [ValidateSet('IPv4', 'IPv6')] [System.String] $AddressFamily = 'IPv4', [Parameter()] [System.Boolean] $KeepExistingAddress = $false ) Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($script:localizedData.ApplyingIPAddressMessage) ) -join '') # Use $AddressFamily to select the IPv4 or IPv6 destination prefix $destinationPrefix = '0.0.0.0/0' if ($AddressFamily -eq 'IPv6') { $destinationPrefix = '::/0' $prefixLength = 64 } # Get all the default routes - this has to be done in case the IP Address is being Removed $getNetRouteParameters = @{ InterfaceAlias = $InterfaceAlias AddressFamily = $AddressFamily ErrorAction = 'Stop' } $defaultRoutes = @(Get-NetRoute @getNetRouteParameters).Where( { $_.DestinationPrefix -eq $destinationPrefix } ) <# Remove any default routes on the specified interface -- it is important to do this *before* removing the IP address, particularly in the case where the IP address was auto-configured by DHCP #> if ($defaultRoutes) { foreach ($defaultRoute in $defaultRoutes) { $removeNetRouteParameters = @{ DestinationPrefix = $defaultRoute.DestinationPrefix NextHop = $defaultRoute.NextHop InterfaceIndex = $defaultRoute.InterfaceIndex AddressFamily = $defaultRoute.AddressFamily Confirm = $false ErrorAction = 'Stop' } Remove-NetRoute @removeNetRouteParameters } } # Get the current IP Address based on the parameters given. $getNetIPAddressParameters = @{ InterfaceAlias = $InterfaceAlias AddressFamily = $AddressFamily ErrorAction = 'Stop' } $currentIPs = @(Get-NetIPAddress @getNetIPAddressParameters) # Remove any IP addresses on the specified interface if ($currentIPs) { foreach ($currentIP in $currentIPs) { $removeIP = $false if ($currentIP.IPAddress -notin ($IPAddress -replace '\/\S*', '') -and -not $KeepExistingAddress) { $removeIP = $true } elseif ($currentIP.IPAddress -in ($IPAddress -replace '\/\S*', '')) { $existingIP = $IPAddress | Where-Object { $_ -match $currentIP.IPAddress } if ($existingIP -ne "$($currentIP.IPAddress)/$($currentIP.prefixLength)") { $removeIP = $true } } if ($removeIP) { $removeNetIPAddressParameters = @{ IPAddress = $currentIP.IPAddress InterfaceIndex = $currentIP.InterfaceIndex AddressFamily = $currentIP.AddressFamily prefixLength = $currentIP.prefixLength Confirm = $false ErrorAction = 'Stop' } Remove-NetIPAddress @removeNetIPAddressParameters } } } $ipAddressObject = Get-IPAddressPrefix -IPAddress $IPAddress -AddressFamily $AddressFamily foreach ($singleIP in $ipAddressObject) { # Build parameter hash table $newNetIPAddressParameters = @{ IPAddress = $singleIP.IPAddress prefixLength = $singleIP.prefixLength InterfaceAlias = $InterfaceAlias } try { # Apply the specified IP configuration New-NetIPAddress @newNetIPAddressParameters -ErrorAction Stop } catch [Microsoft.Management.Infrastructure.CimException] { $verifyNetIPAddressAdapterParam = @{ IPAddress = $singleIP.IPAddress prefixLength = $singleIP.prefixLength } <# Setting New-NetIPaddress will throw [Microsoft.Management.Infrastructure.CimException] if the IP address is already set. Need to check to make sure the IP is set on correct interface #> $verifyNetIPAddressAdapter = Get-NetIPAddress @verifyNetIPAddressAdapterParam -ErrorAction SilentlyContinue if ($verifyNetIPAddressAdapter.InterfaceAlias -eq $InterfaceAlias) { # The IP Address is already set on the correct interface Write-Verbose -Message ( @("$($MyInvocation.MyCommand): " $($script:localizedData.IPAddressMatchMessage) ) -join '' ) } else { Write-Error -Message ( @( "$($MyInvocation.MyCommand): " $($script:localizedData.IPAddressDoesNotMatchInterfaceAliasMessage) -f $InterfaceAlias,$verifyNetIPAddressAdapter.InterfaceAlias ) -join '' ) } continue } Write-Verbose -Message ( @("$($MyInvocation.MyCommand): " $($script:localizedData.IPAddressSetStateMessage) ) -join '' ) } } # Set-TargetResource <# .SYNOPSIS Tests the IP address on the interface. .PARAMETER IPAddress The desired IP address. .PARAMETER InterfaceAlias Alias of the network interface for which the IP address should be set. .PARAMETER AddressFamily IP address family. .PARAMETER KeepExistingAddress Indicates whether or not existing IP addresses on an interface will be retained. #> function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter()] [ValidateNotNullOrEmpty()] [System.String[]] $IPAddress, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $InterfaceAlias, [Parameter(Mandatory = $true)] [ValidateSet('IPv4', 'IPv6')] [System.String] $AddressFamily = 'IPv4', [Parameter()] [System.Boolean] $KeepExistingAddress = $false ) # Flag to signal whether settings are correct [System.Boolean] $desiredConfigurationMatch = $true Write-Verbose -Message ( @("$($MyInvocation.MyCommand): " $($script:localizedData.CheckingIPAddressMessage) ) -join '') Assert-ResourceProperty @PSBoundParameters <# Get the current IP Address based on the parameters given. First make sure that adapter is available #> [System.Boolean] $adapterBindingReady = $false [System.DateTime] $startTime = Get-Date while (-not $adapterBindingReady -and (((Get-Date) - $startTime).TotalSeconds) -lt 30) { $getNetIPAddressParameters = @{ InterfaceAlias = $InterfaceAlias AddressFamily = $AddressFamily ErrorAction = 'SilentlyContinue' } $currentIPs = @(Get-NetIPAddress @getNetIPAddressParameters) if ($currentIPs) { $adapterBindingReady = $true } else { Start-Sleep -Milliseconds 200 } } # while $ipAddressObject = Get-IPAddressPrefix -IPAddress $IPAddress -AddressFamily $AddressFamily # Test if the IP Address passed is present foreach ($singleIP in $ipAddressObject) { $prefixLength = $singleIP.prefixLength if ($singleIP.IPAddress -notin $currentIPs.IPAddress) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($script:localizedData.IPAddressDoesNotMatchMessage) -f $singleIP, $currentIPs.IPAddress ) -join '' ) $desiredConfigurationMatch = $false } else { Write-Verbose -Message ( @("$($MyInvocation.MyCommand): " $($script:localizedData.IPAddressMatchMessage) ) -join '') # Filter the IP addresses for the IP address to check $filterIP = $currentIPs.Where( { $_.IPAddress -eq $singleIP.IPAddress } ) # Only test the Prefix Length if the IP address is present if (-not $filterIP.prefixLength.Equals([System.Byte] $prefixLength)) { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($script:localizedData.prefixLengthDoesNotMatchMessage) -f $prefixLength, $currentIPs.prefixLength ) -join '' ) $desiredConfigurationMatch = $false } else { Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): " $($script:localizedData.prefixLengthMatchMessage) ) -join '' ) } } } return $desiredConfigurationMatch } # Test-TargetResource <# .SYNOPSIS Check the IP Address details are valid and do not conflict with Address family. Also checks the prefix length and ensures the interface exists. If any problems are detected an exception will be thrown. .PARAMETER IPAddress The desired IP address. .PARAMETER InterfaceAlias Alias of the network interface for which the IP address should be set. .PARAMETER AddressFamily IP address family. .PARAMETER KeepExistingAddress Indicates whether or not existing IP addresses on an interface will be retained. #> function Assert-ResourceProperty { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String[]] $IPAddress, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $InterfaceAlias, [Parameter()] [ValidateSet('IPv4', 'IPv6')] [System.String] $AddressFamily = 'IPv4', [Parameter()] [System.Boolean] $KeepExistingAddress = $false ) $prefixLengthArray = ($IPAddress -split '/')[1] if ($prefixLengthArray.Count -ne $IPAddress.Count) { # Return the prefix length of each IP address specified $prefixLengthArray = $IPAddress | Foreach-Object { if ($_ -match '\/\d{1,3}') { ($_ -split '/')[1] } else { if ($_.split('.')[0] -in (0..127)) { $prefixLength = 8 } elseif ($_.split('.')[0] -in (128..191)) { $prefixLength = 16 } elseif ($_.split('.')[0] -in (192..223)) { $prefixLength = 24 } if ($AddressFamily -eq 'IPv6') { $prefixLength = 64 } $prefixLength } } } if (-not (Get-NetAdapter | Where-Object -Property Name -EQ $InterfaceAlias )) { New-InvalidArgumentException ` -Message $($($script:localizedData.InterfaceNotAvailableError) -f $InterfaceAlias) ` -ArgumentName 'InterfaceAlias' } foreach ($singleIPAddress in $IPAddress) { $singleIP = ($singleIPAddress -split '/')[0] Assert-IPAddress -Address $singleIP -AddressFamily $AddressFamily } foreach ($prefixLength in $prefixLengthArray) { $prefixLength = [uint32]::Parse($prefixLength) if (( ($AddressFamily -eq 'IPv4') ` -and (($prefixLength -lt [uint32]0) -or ($prefixLength -gt [uint32]32)) ) -or ( ($AddressFamily -eq 'IPv6') ` -and (($prefixLength -lt [uint32]0) -or ($prefixLength -gt [uint32]128)) )) { New-InvalidArgumentException ` -Message $($($script:localizedData.PrefixLengthError) -f $prefixLength, $AddressFamily) ` -ArgumentName 'IPAddress' } } } # Assert-ResourceProperty Export-ModuleMember -function *-TargetResource |