DSCResources/DSC_ClusterIPAddress/DSC_ClusterIPAddress.psm1
$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' Import-Module -Name $script:resourceHelperModulePath $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' <# .SYNOPSIS Returns the current state of the failover cluster IP address. .PARAMETER IPAddress IP address to check the state of. .PARAMETER AddressMask Address mask of the IP address. #> function Get-TargetResource { [CmdletBinding()] [OutputType([Hashtable])] param ( [Parameter(Mandatory = $true)] [System.String] $IPAddress, [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask Write-Verbose -Message ($script:localizedData.GetTargetResourceMessage -f $IPAddress, $AddressMask) $result = @{ IPAddress = $null AddressMask = $null Ensure = 'Absent' } $ipResources = Get-ClusterResource | Where-Object {$_.ResourceType -eq 'IP Address'} foreach ( $ipResource in $ipResources ) { $ipResourceDetails = Get-ClusterIPResourceParameters -IPAddressResourceName $ipResource.name if ( $ipResourceDetails.Address -eq $IPAddress ) { Write-Verbose -Message ($script:localizedData.FoundIPResource -f $IPAddress) $result.IPAddress = $ipResourceDetails.Address $result.AddressMask = $ipResourceDetails.AddressMask $result.Ensure = 'Present' } } $result } <# .SYNOPSIS Sets the state of the failover cluster IP address. .PARAMETER IPAddress IP address to either add or remove from the Failover Cluster. .PARAMETER AddressMask Address mask of the IP address to either add or remove from the Failover Cluster. .PARAMETER Ensure Ensure whether the IP address is added or removed from the Failover Cluster #> function Set-TargetResource { param ( [Parameter()] [System.String] [ValidateSet('Present', 'Absent')] $Ensure = 'Present', # IPAddress to add to Cluster [Parameter(Mandatory = $true)] [System.String] $IPAddress, # SubnetMask of IPAddress [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask Write-Verbose -Message ($script:localizedData.SetTargetResourceMessage -f $IPAddress, $AddressMask, $Ensure) if ($Ensure -eq 'Present') { # We've gotten here because the IPAddress given is not in the DependencyExpression for the cluster # We need to Check if the network is added to the cluster. If not, we fail. If it is, we can append the IPAddress if ( -not $(Test-ClusterNetwork -IPAddress $IPAddress -AddressMask $AddressMask) ) { New-InvalidArgumentException ` -Message ($script:localizedData.NonExistantClusterNetwork -f $IPAddress,$AddressMask) ` -ArgumentName 'IPAddress' } else { $params = @{ IPAddress = $IPAddress AddressMask = $AddressMask ErrorAction = 'Stop' } Add-ClusterIPAddressDependency @params } } else { Remove-ClusterIPAddressDependency -IPAddress $IPAddress -AddressMask $AddressMask } } <# .SYNOPSIS Tests the current state of the failover cluster IP address. .PARAMETER IPAddress IP address to check the state of. .PARAMETER AddressMask Address mask of the IP address. .PARAMETER Ensure Ensure whether the IP address is added or removed from the Failover Cluster #> function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter()] [System.String] [ValidateSet('Present', 'Absent')] $Ensure = 'Present', # IPAddress to add to Cluster [Parameter(Mandatory = $true)] [System.String] $IPAddress, # SubnetMask of IPAddress [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask Write-Verbose -Message ($script:localizedData.TestTargetResourceMessage -f $IPAddress, $AddressMask, $Ensure) $ipResource = Get-TargetResource -IPAddress $IPAddress -AddressMask $AddressMask $result = $false if ($Ensure -eq 'Present') { if (-not ([System.String]::IsNullOrEmpty($ipResource.IPAddress))) { if ($ipResource.AddressMask -eq $AddressMask) { $result = $true } } } else { <# $ipResource will always have some contents, but if IPAddress is null or empty, the resource does not exist. #> if ([System.String]::IsNullOrEmpty($ipResource.IPAddress)) { $result = $true } } $result } <# .Synopsis Given an IP Address and a Subnet Mask, returns the IP Addresses subnet. .DESCRIPTION Returns an IPAddress object of the subnet mask of the given IPAddress and Subnet. .PARAMETER IPAddress IP address to add to the Cluster's DependencyExpression .PARAMETER AddressMask The subnet mask of the IPAddress .EXAMPLE Get-Subnet -IPAddress 10.235.32.129 -AddressMask 255.255.255.128 #> function Get-Subnet { [CmdletBinding()] [OutputType([System.String])] param ( # IPAddress to add to Cluster [Parameter(Mandatory = $true)] [System.String] $IPAddress, # SubnetMask of IPAddress [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask $subnet = ([IPAddress](([IPAddress]$Ipaddress).Address -band ([IPAddress]$AddressMask).Address)).IPAddressToString Write-Verbose -Message ($script:localizedData.FoundSubnetfromIPAddressandAddressMask -f $IPAddress, $AddressMask, $subnet) return $subnet } <# .Synopsis Adds an IPAddress as a Dependency to a Windows Cluster .DESCRIPTION Adds an IP Address resource to a Windows Cluster's Dependecy Expression .PARAMETER IPAddress IP address to add to the Cluster's DependencyExpression .PARAMETER AddressMask The subnet mask of the IPAddress .PARAMETER ClusterName Name of the cluster to add IP Address resource to .EXAMPLE # Using the default ParameterSet of both IP Address and Subnet Add-ClusterIPAddressDependency -IPAddress 10.235.32.137 -AddressMask 255.255.255.128 -Verbose #> function Add-ClusterIPAddressDependency { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $IPAddress, [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask #* Get Windows Cluster resource $clusterObj = Get-ClusterObject $ipResourceName = Add-ClusterIPResource -IPAddress $IPAddress -OwnerGroup $clusterObj.ownerGroup $ipResource = Get-ClusterResource -Name $ipResourceName Add-ClusterIPParameter -IPAddressResourceName $ipResource.Name -IPAddress $IPAddress -AddressMask $AddressMask $ipResources = Get-ClusterIPResource -OwnerGroup $clusterObj.ownerGroup $dependencyExpression = New-ClusterIPDependencyExpression -ClusterResource $ipResources.Name #Set cluster resources $params = @{ Resource = $($clusterObj.Name) Dependency = $dependencyExpression ErrorAction = 'Stop' } Write-Verbose -Message ($script:localizedData.SetDependencyExpression -f $dependencyExpression) Set-ClusterResourceDependency @params } <# .Synopsis Removes an IPAddress as a Dependency to a Windows Cluster .DESCRIPTION Removes an IP Address resource to a Windows Cluster's Dependecy Expression .PARAMETER IPAddress IP address to remove to the Cluster's DependencyExpression .PARAMETER AddressMask The subnet mask of the IPAddress .EXAMPLE Remove-ClusterIPAddressDependency -IPAddress 10.235.32.137 -AddressMask 255.255.255.128 -Verbose #> function Remove-ClusterIPAddressDependency { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $IPAddress, [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask #* Get Windows Cluster resource $clusterObj = Get-ClusterObject $ipResource = Get-ClusterIPResourceFromIPAddress -IPAddress $IPAddress Remove-ClusterResource -InputObject $ipResource -Force # Write new dependency expression $ipResources = Get-ClusterIPResource -OwnerGroup $clusterObj.OwnerGroup if ( $ipResources.Count -ge 1 ) { $dependencyExpression = New-ClusterIPDependencyExpression -ClusterResource $ipResources.Name $params = @{ Resource = $($clusterObj.Name) Dependency = $dependencyExpression ErrorAction = 'Stop' } Write-Verbose -Message ($script:localizedData.SetDependencyExpression -f $dependencyExpression) Set-ClusterResourceDependency @params } } <# .Synopsis Checks whether the ClusterNetwork for a given IPAddress has been added to a Cluster .DESCRIPTION Given an IPAddress and AddressMask this cmdlet will check if the correct ClusterNetwork has been added to the cluster. .PARAMETER IPAddress IP address to check whether it's subnet is a cluster network already .PARAMETER AddressMask The subnet mask of the IPAddress .EXAMPLE Test-ClusterNetwork -IPAddress 10.245.10.32 -AddressMask 255.255.255.0 #> function Test-ClusterNetwork { [CmdletBinding()] [OutputType([System.Boolean])] param ( # IPAddress to add to Cluster [Parameter(Mandatory = $true)] [System.String] $IPAddress, # SubnetMask of IPAddress [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask $clusterNetworks = Get-ClusterNetworkList Write-Verbose -Message ($script:localizedData.GetSubnetfromIPAddressandAddressMask -f $IPAddress, $AddressMask) $subnet = $(Get-Subnet -IPAddress $IPAddress -AddressMask $AddressMask -ErrorAction Stop) foreach ( $network in $clusterNetworks ) { if (( $network.Address -eq $subnet ) -and ( $network.AddressMask -eq $AddressMask )) { Write-Verbose -Message ($script:localizedData.NetworkAlreadyInCluster -f $($network.address), $IPAddress, $subnet) return $True } } return $false } <# .SYNOPSIS Returns a list of PSCustomObjects representing the network and subnet mask of all networks in the cluster. #> function Get-ClusterNetworkList { [CmdletBinding()] param ( ) Write-Verbose -Message ($script:localizedData.GetClusterNetworks) $networks = New-Object -TypeName "System.Collections.Generic.List[PSCustomObject]" foreach ( $network in Get-ClusterNetwork ) { $networks.Add([PSCustomObject]@{ Address = $network.Address AddressMask = $network.AddressMask }) Write-Verbose -Message ($script:localizedData.FoundClusterNetwork -f $($network.Address), $($network.AddressMask)) } return $networks } <# .Synopsis Adds an IP Address Resource to a given Cluster Group and returns an IPAddress Resource .PARAMETER IPAddress IP address to check whether it's subnet is a cluster network already .PARAMETER OwnerGroup OwnerGroup of the cluster to add the IP resource to #> function Add-ClusterIPResource { [CmdletBinding()] [OutputType([System.String])] param ( # IPAddress to add to Cluster [Parameter(Mandatory = $true)] [System.String] $IPAddress, # Owner Group of the cluster [Parameter(Mandatory = $true)] [System.String] $OwnerGroup ) Test-IPAddress -IPAddress $IPAddress #* Create new IPAddress resource and add the IPAddress parameters to it Write-Verbose -Message ($script:localizedData.CreateNewIPResource -f $IPAddress, $OwnerGroup) $resourceName = "IP Address $IPAddress" $params = @{ Name = $resourceName ResourceType = 'IP Address' Group = $OwnerGroup ErrorAction = 'Stop' } $resource = Add-ClusterResource @params return $resourceName } <# .Synopsis Gets all IP Resources added to the cluster .PARAMETER OwnerGroup OwnerGroup of the cluster to get the IP resources from #> function Get-ClusterIPResource { [CmdletBinding()] [OutputType([System.String])] param ( # Owner Group of the cluster [Parameter(Mandatory = $true)] [System.String] $OwnerGroup ) $ipResources = Get-ClusterResource | Where-Object { ( $_.OwnerGroup -eq $OwnerGroup ) -and ( $_.ResourceType -eq 'IP Address' ) } return $ipResources } <# .Synopsis Gets the IP resource information of a Given Cluster IP address Resource .PARAMETER IPAddressResource IP cddress resource to get to information from #> function Get-ClusterIPResourceParameters { [CmdletBinding()] param ( # IPAddress to add to Cluster [Parameter(Mandatory = $true)] [System.String] $IPAddressResourceName ) $ipObj = Get-ClusterResource -Name $IPAddressResourceName $address = (Get-ClusterParameter -InputObject $ipObj -Name Address).Value $addressMask = (Get-ClusterParameter -InputObject $ipObj -Name SubnetMask).Value $network = (Get-ClusterParameter -InputObject $ipObj -Name Network).Value Write-Verbose -Message ($script:localizedData.FoundIPAddressResource -f $address, $addressMask, $network) @{ Address = $address AddressMask = $addressMask Network = $network } } <# .Synopsis Adds an IP address resource to cluster parameter .PARAMETER IPAddressResource IP cddress resource to add to the cluster parameter .PARAMETER IPAddress IP address to add to the cluster parameter .PARAMETER AddressMask Address mask of the IP address #> function Add-ClusterIPParameter { [CmdletBinding()] param ( # IPAddress to add to Cluster [Parameter(Mandatory = $true)] [System.String] $IPAddressResourceName, [Parameter(Mandatory = $true)] [System.String] $IPAddress, [Parameter(Mandatory = $true)] [System.String] $AddressMask ) Test-IPAddress -IPAddress $IPAddress Test-IPAddress -IPAddress $AddressMask $ipAddressResource = Get-ClusterResource -Name $IPAddressResourceName $parameter1 = New-Object Microsoft.FailoverClusters.PowerShell.ClusterParameter $iPAddressResource,Address,$IPAddress $parameter2 = New-Object Microsoft.FailoverClusters.PowerShell.ClusterParameter $iPAddressResource,SubnetMask,$AddressMask $parameterList = $parameter1,$parameter2 Write-Verbose -Message ($script:localizedData.AddIPAddressResource -f $IPAddress,$AddressMask) $parameterList | Set-ClusterParameter -ErrorAction Stop } <# .Synopsis Validates a given IP address .PARAMETER IPAddress IP address to validate #> function Test-IPAddress { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $IPAddress ) $null = [System.Net.IPAddress]::Parse($IPAddress) } <# .Synopsis Creates a new cluster IP Dependency .PARAMETER ClusterResource Cluster resources to create IP Dependency from #> function New-ClusterIPDependencyExpression { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [System.String[]] $ClusterResource ) if ($ClusterResource.count -eq 1) { $dependencyExpression = "[$ClusterResource]" } else { $dependencyExpression = '' $clusterResourceCount = $ClusterResource.count - 1 $i = 0 while ( $i -le $clusterResourceCount ) { if ( $i -eq $clusterResourceCount ) { $dependencyExpression += "[$($ClusterResource[$i])]" } else { $dependencyExpression += "[$($ClusterResource[$i])] or " } $i++ } } Write-Verbose -Message ($script:localizedData.NewDependencyExpression -f $dependencyExpression) return $dependencyExpression } <# .Synopsis Returns an object representing the cluster #> function Get-ClusterObject { [CmdletBinding()] param ( ) $cluster = Get-ClusterResource | Where-Object { $_.name -eq 'Cluster Name'} return $cluster } <# .Synopsis Gets a Cluster Resource from a given IP address. .Parameter IPAddress IP address of the cluster resource object to find. #> function Get-ClusterIPResourceFromIPAddress { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.String] $IPAddress ) $result = $null Test-IPAddress -IPAddress $IPAddress $clusterObj = Get-ClusterObject $ipResources = Get-ClusterIPResource -OwnerGroup $clusterObj.ownerGroup foreach ( $ipResource in $ipResources ) { $resource = Get-ClusterIPResourceParameters -IPAddressResourceName $ipResource.name if ($resource.Address -eq $IPAddress) { $result = Get-ClusterResource -Name $ipResource.name } } return $result } |