SimpleIP.psm1
# Override Write-Verbose in this module so calling function is added to the message function script:Write-Verbose { [CmdletBinding()] param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [String] $Message ) begin {} process { try { $PSBoundParameters['Message'] = $((Get-PSCallStack)[1].Command) + ': ' + $PSBoundParameters['Message'] } catch {} Microsoft.PowerShell.Utility\Write-Verbose @PSBoundParameters } end {} } function Convert-IPv4Address { <# .SYNOPSIS Convert IP address between different formats .DESCRIPTION Convert IP address between different formats - Quad dot (without mask) eg. "192.168.1.2" - Integer (uint32) eg. 3232235778 - Binary (32 long string) eg. "11000000101010000000000100000010" .PARAMETER IP Input IP is either: - Quad dot (without mask) eg. "192.168.1.2" - Integer (uint32) eg. 3232235778 - Binary (32 long string) eg. "11000000101010000000000100000010" .PARAMETER QuadDot Output in quad dot format, eg. "192.168.1.2" This is default output .PARAMETER Integer Output integer (uint32), eg 3232235778 .PARAMETER Binary Output in binary (32 long string), eg. "11000000101010000000000100000010" .EXAMPLE Convert-IPv4Address -IP 192.168.1.2 -Integer 3232235778 .EXAMPLE Convert-IPv4Address -IP 192.168.1.2 -Binary 11000000101010000000000100000010 .EXAMPLE Convert-IPv4Address -IP 11000000101010000000000100000010 -QuadDot 192.168.1.2 .EXAMPLE 3232235778 | Convert-IPv4Address 192.168.1.2 .EXAMPLE Convert-IPv4Address 192.168.1.2 -Integer 3232235778 #> [OutputType([System.String], ParameterSetName = 'QuadDot')] [OutputType([System.UInt32], ParameterSetName = 'Integer')] [OutputType([System.String], ParameterSetName = 'Binary')] [CmdletBinding(DefaultParameterSetName = 'QuadDot')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $IP, [Parameter(ParameterSetName = 'QuadDot')] [System.Management.Automation.SwitchParameter] $QuadDot, [Parameter(Mandatory = $true, ParameterSetName = 'Integer')] [System.Management.Automation.SwitchParameter] $Integer, [Parameter(Mandatory = $true, ParameterSetName = 'Binary')] [System.Management.Automation.SwitchParameter] $Binary ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' [System.UInt32] $i = 0 if (Test-IPv4Address -IP $IP) { $IP -split '\.' | ForEach-Object -Process { $i = $i * 256 + $_ } } elseif ($IP -match '^[01]{32}$') { $i = [System.Convert]::ToUInt32($IP, 2) } else { try { $i = $IP } catch { throw "$IP is not a valid IPv4 address" } } # Return if ($Integer) { $i } else { $b = [System.Convert]::ToString(([System.UInt32] $i), 2).PadLeft(32, '0') if ($Binary) { $b } else { (0..3 | ForEach-Object -Process {[System.Convert]::ToByte($b[(8*$_)..(8*$_+7)] -join '', 2)}) -join '.' } } } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Convert-IPv4Mask { <# .SYNOPSIS Convert IP subnet mask between different formats .DESCRIPTION Convert IP subnet mask between different formats - Quad dot eg. "255.255.128.0" - Mask length (0-32) eg. "17" - Mask length with slash eg. "/17" - Integer (uint32) eg. "4294934528" - Binary (32 long string) eg. "11111111111111111000000000000000" If input is in quad dot format ("255.0.0.0"), output defaults to mask length with a leading slash ("/8") - else output defaults to quad dot format Output can be forced to be in specific format with switches .PARAMETER Mask Input subnet mask is either: - Quad dot eg. "255.255.128.0" - Mask length (0-32) eg. "17" - Mask length with slash eg. "/17" - Integer (uint32) eg. "4294934528" - Binary (32 long string) eg. "11111111111111111000000000000000" .PARAMETER QuadDot Output in quad dot format, eg. "255.255.128.0" .PARAMETER Length Output is in mask length, eg "17" .PARAMETER LengthWithSlash Output is in mask length with leading slash, eg "/17" .PARAMETER Integer Output integer (uint32), eg 4294934528 .PARAMETER Binary Output in binary (32 long string), eg. "11111111111111111000000000000000" .EXAMPLE Convert-IPv4Mask -Mask 255.255.128.0 /17 .EXAMPLE Convert-IPv4Mask -Mask /17 -Length 17 .EXAMPLE Convert-IPv4Mask -Mask /17 255.255.128.0 .EXAMPLE Convert-IPv4Mask -Mask 17 255.255.128.0 .EXAMPLE Convert-IPv4Mask -Mask 17 -Binary 11111111111111111000000000000000 #> [OutputType([System.String], ParameterSetName = 'Default')] [OutputType([System.String], ParameterSetName = 'QuadDot')] [OutputType([System.Byte] , ParameterSetName = 'Length')] [OutputType([System.String], ParameterSetName = 'LengthWithSlash')] [OutputType([System.UInt32], ParameterSetName = 'Integer')] [OutputType([System.String], ParameterSetName = 'Binary')] [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $Mask, [Parameter(Mandatory = $true, ParameterSetName = 'QuadDot')] [System.Management.Automation.SwitchParameter] $QuadDot, [Parameter(Mandatory = $true, ParameterSetName = 'Length')] [System.Management.Automation.SwitchParameter] $Length, [Parameter(Mandatory = $true, ParameterSetName = 'LengthWithSlash')] [System.Management.Automation.SwitchParameter] $LengthWithSlash, [Parameter(Mandatory = $true, ParameterSetName = 'Integer')] [System.Management.Automation.SwitchParameter] $Integer, [Parameter(Mandatory = $true, ParameterSetName = 'Binary')] [System.Management.Automation.SwitchParameter] $Binary ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $quadDotInput = $false [System.UInt32] $i = 0 if ($Mask -match '^/?([0-9]|[12][0-9]|3[0-2])$') { $i = [System.Convert]::ToUInt32(('1' * $Matches[1] + '0' * (32 - $Matches[1])), 2) } else { try { if (-not ($Mask | Convert-IPv4Address | Test-IPv4Address -Mask)) {throw} } catch { throw "$Mask is not a valid subnet mask" } if (Test-IPv4Address -IP $Mask) {$quadDotInput = $true} $i = Convert-IPv4Address -IP $Mask -Integer } if (($output = $PSCmdlet.ParameterSetName) -eq 'Default') { $output = if ($quadDotInput) {'LengthWithSlash'} else {'QuadDot'} } # Return switch ($output) { 'QuadDot' {$i | Convert-IPv4Address} 'Length' {[System.Byte] [regex]::Matches(($i | Convert-IPv4Address -Binary), '1').Count} 'LengthWithSlash' {'/' + [regex]::Matches(($i | Convert-IPv4Address -Binary), '1').Count} 'Integer' {$i} 'Binary' {$i | Convert-IPv4Address -Binary} } } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Convert-IPv6Address { <# .SYNOPSIS Convert IPv6 address between formats .DESCRIPTION Convert IPv6 address between formats Also compress/compact IPv6 address. (IPv6 addresses can be hard to compare ("0::1" -eq "::1"), but they are run throug this command, they can be compared) Output defaults to compacted IPv6 address (eg. "::1") .PARAMETER IP Input IP is either - Standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64") - [uint16[]] array with 8 elements - Binary (string containinging 128 "0" or "1" - spaces are allowed) .PARAMETER Prefix If prefix is not set in IP address, it must be set with this parameter .PARAMETER Info Output object with IP and prefix in different formats .EXAMPLE Convert-IPv6Address 00ab:00:0:000:00:fff::1 ab::fff:0:1 .EXAMPLE Convert-IPv6Address 00ab:00:0:000:00:fff::1/64 ab::fff:0:1/64 .EXAMPLE Convert-IPv6Address -IP a:b:c::/64 -Info IP : a:b:c::/64 IPCompact : a:b:c:: IPExpanded : 000a:000b:000c:0000:0000:0000:0000:0000 IPIntArray : {10, 11, 12, 0...} IPHexArray : {a, b, c, 0...} IPHexArrayExpanded : {000a, 000b, 000c, 0000...} IPBinary : 0000000000001010 0000000000001011 0000000000001100 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 Cidr : a:b:c::/64 CidrExpanded : 000a:000b:000c:0000:0000:0000:0000:0000/64 Prefix : 64 PrefixIntArray : {65535, 65535, 65535, 65535...} PrefixHexArray : {ffff, ffff, ffff, ffff...} PrefixHexArrayExpanded : {ffff, ffff, ffff, ffff...} PrefixHexString : ffff:ffff:ffff:ffff:0:0:0:0 PrefixHexStringExpanded : ffff:ffff:ffff:ffff:0000:0000:0000:0000 PrefixBinary : 1111111111111111 1111111111111111 1111111111111111 1111111111111111 0000000000000000 0000000000000000 0000000000000000 0000000000000000 #> [OutputType([System.String], ParameterSetName = 'Default')] [OutputType([PSCustomObject], ParameterSetName = 'Info')] [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.Object] $IP, [Parameter()] [Nullable[System.Byte]] $Prefix, [Parameter(Mandatory = $true, ParameterSetName = 'Info')] [System.Management.Automation.SwitchParameter] $Info ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' if ($Prefix -ne $null -and $Prefix -gt 128) {throw 'Prefix is higher than 128'} [System.UInt16[]] $ipIntArray = @() if ($IP -is [System.String] -and ($nospaceIP = $IP -replace ' ') -and $nospaceIP -match '^[01]{128}$') { $ipIntArray = (0..7).ForEach({ [System.Convert]::ToUInt16($nospaceIP.Substring(($_*16), 16), 2) }) } elseif ($IP -is [System.String]) { if (-not ($IP -match '^([0-9a-f:]+)(/(([1-9]?[0-9])|(1[01][0-9])|(12[0-8])))?$')) {throw "Error parsing IPv6 address $IP"} $ipOnly = $Matches[1] if ($Prefix -eq $null) { $Prefix = $Matches[3] } elseif ($Matches[3] -ne $null -and $Prefix -ne $Matches[3]) { "Prefix set to /$($Matches[3]) in -IP but /$Prefix in -Prefix for $IP. Using /$Prefix" | Write-Warning } try { if ( $ipOnly -match '[^0-9a-f:]' -or # Something else than hex or colon $ipOnly -match '::.+::' -or # two double colon $ipOnly -match ':::' -or # tripple colon $ipOnly -match '^:[^:]' -or # start with single colon $ipOnly -match '[^:]:$' # end with single colon ) {throw} $ipOnly = $ipOnly -replace '::',':*:' $ipSplit = $ipOnly -split ':'| Where-Object -FilterScript {$_ -ne ''} if ($ipSplit.Count -gt 8) {throw} $ipIntArray = $ipSplit | ForEach-Object -Process { if ($_ -eq '*') { [System.UInt16[]] (@(0) * (9 - $ipSplit.Count)) } else { [System.Convert]::ToUInt16($_, 16) } } if ($ipIntArray.Count -ne 8) {throw} } catch { throw "Error parsing IPv6 address $IP" } } elseif ($IP -is [array] -and $IP.Count -eq 8) { try { $ipIntArray = $IP } catch { throw 'Input IP is in unknown format' } } else { throw 'Input IP is in unknown format' } $cidr = $cidrExpanded = $prefixIntArray = $prefixHexArray = $prefixHexArrayExpanded = $prefixHexString = $prefixHexStringExpanded = $prefixBinary = $null $ipHexArrayExpanded = $ipIntArray | ForEach-Object -Process { '{0:x4}' -f $_ } $ipHexArray = $ipIntArray | ForEach-Object -Process { '{0:x}' -f $_ } $ipExpanded = $ipHexArrayExpanded -join ':' $ipCompact = $ipHexArray -join ':' foreach ($i in (7..0)) { if ($ipCompact -ne ($ipCompact = $ipCompact -replace "(^|:)0(:0){$i}(:|`$)",'::')) {break} } $ipReturn = $ipCompact $ipBinary = ($ipIntArray | ForEach-Object -Process {[System.Convert]::ToString(([System.UInt16] $_), 2).PadLeft(16, '0')}) -join ' ' if ($Prefix -ne $null) { $ipReturn = $cidr = '{0}/{1}' -f $ipCompact, $Prefix $cidrExpanded = '{0}/{1}' -f $ipExpanded, $Prefix $prefixBinary = '1' * $Prefix + '0' * (128 - $Prefix) $prefixIntArray = (0..7).ForEach({ [System.Convert]::ToUInt16($prefixBinary.Substring(($_*16), 16), 2) }) $prefixHexArrayExpanded = $prefixIntArray | ForEach-Object -Process { '{0:x4}' -f $_ } $prefixHexArray = $prefixIntArray | ForEach-Object -Process { '{0:x}' -f $_ } $prefixHexString = $prefixHexArray -join ':' $prefixHexStringExpanded = $prefixHexArrayExpanded -join ':' $prefixBinary = ($prefixIntArray | ForEach-Object -Process {[System.Convert]::ToString(([System.UInt16] $_), 2).PadLeft(16, '0')}) -join ' ' } $r = [PSCustomObject] @{ IP = $ipReturn IPCompact = $ipCompact IPExpanded = $ipExpanded IPIntArray = $ipIntArray IPHexArray = $ipHexArray IPHexArrayExpanded = $ipHexArrayExpanded IPBinary = $ipBinary Cidr = $cidr CidrExpanded = $cidrExpanded Prefix = $Prefix PrefixIntArray = $prefixIntArray PrefixHexArray = $prefixHexArray PrefixHexArrayExpanded = $prefixHexArrayExpanded PrefixHexString = $prefixHexString PrefixHexStringExpanded = $prefixHexStringExpanded PrefixBinary = $prefixBinary } # Return if ($Info) { $r } else { $r.IP } } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Get-IPv4Address { <# .SYNOPSIS Get IP subnet, mask, broadcast for an IP address .DESCRIPTION Get IP subnet, mask, broadcast for an IP address .PARAMETER IP Input IP in quad dot format with subnet mask, either: - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0" - IP + mask length, eg. "127.0.0.1/8" If input is IP without subnet mask (eg. "127.0.0.1") then -Mask parameter must be set .PARAMETER Mask If input IP is in format without subnet mask, this parameter must be set to either - Quad dot format, eg. "255.255.255.0" - Mask length (0-32), eg. "24" - Mask length with slash, eg. "/24" .PARAMETER SameIP Return same IP as input IP (why? maybe in a different format back) If input is "10.11.12.13/24", then "10.11.12.13/24" is returned .PARAMETER Subnet Return subnet If input is "10.11.12.13/24", then "10.11.12.0/24" is returned .PARAMETER Broadcast Return broadcast If input is "10.11.12.13/24", then "10.11.12.255/24" is returned .PARAMETER First Return first usable IP in subnet If input is "10.11.12.13/24", then "10.11.12.1/24" is returned .PARAMETER Last Return last usable IP in subnet If input is "10.11.12.13/24", then "10.11.12.254/24" is returned .PARAMETER All Return all usable IPs in subnet If input is "10.11.12.13/24", then an array with IP addresses from "10.11.12.1/24" to "10.11.12.254/24" is returned .PARAMETER Pool Treat subnet and broadcast adddresses as usable First IP will be same as subnet and last IP will be the same as broadcast .PARAMETER WithMaskLength Return in "127.0.0.1/8" format This is default output .PARAMETER WithMask Return in "127.0.0.1 255.0.0.0" format .PARAMETER IPOnly Return in "127.0.0.1" format .PARAMETER MaskQuadDotOnly Only return subnet mask in "255.0.0.0" format .PARAMETER MaskLengthOnly Only return subnet mask in "8" format .PARAMETER MaskLengthWithSlashOnly Only return subnet mask in "/8" format .PARAMETER Info Return object with different info .EXAMPLE Get-IPv4Address -IP 127.0.0.1/8 -Subnet 127.0.0.0/24 .EXAMPLE Get-IPv4Address -IP 127.0.0.1/8 -Subnet -WithMask 127.0.0.0 255.0.0.0 .EXAMPLE Get-IPv4Address -IP 127.0.0.1/8 -Broadcast -WithMask 127.255.255.255 255.0.0.0 .EXAMPLE Get-IPv4Address -IP 127.0.0.1/8 -Broadcast -IPOnly 127.255.255.255 .EXAMPLE Get-IPv4Address -IP 10.100.200.201 -Mask /30 -All -WithMask 10.100.200.201 255.255.255.252 10.100.200.202 255.255.255.252 .EXAMPLE Get-IPv4Address -IP 192.168.0.150/255.255.255.128 -Info IP : 192.168.0.150 Subnet : 192.168.0.128 FirstIP : 192.168.0.129 LastIP : 192.168.0.254 Broadcast : 192.168.0.255 Integer : @{IP=3232235670; Subnet=3232235648; FirstIP=3232235... Binary : @{IP=11000000101010000000000010010110; Subnet=11000... MaskQuadDot : 255.255.255.128 MaskLength : 25 #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $IP, [Parameter()] [ValidateScript({ (Test-IPv4Address -IP $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })] [System.String] $Mask = '', [Parameter(ParameterSetName = 'SameIPWithMaskLength')] [Parameter(ParameterSetName = 'SameIPWithMask')] [Parameter(ParameterSetName = 'SameIPIPOnly')] [System.Management.Automation.SwitchParameter] $SameIP, [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMaskLength')] [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIPOnly')] [System.Management.Automation.SwitchParameter] $Subnet, [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithMaskLength')] [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIPOnly')] [System.Management.Automation.SwitchParameter] $Broadcast, [Parameter(Mandatory = $true, ParameterSetName = 'FirstWithMaskLength')] [Parameter(Mandatory = $true, ParameterSetName = 'FirstWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'FirstIPOnly')] [System.Management.Automation.SwitchParameter] $First, [Parameter(Mandatory = $true, ParameterSetName = 'LastWithMaskLength')] [Parameter(Mandatory = $true, ParameterSetName = 'LastWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'LastIPOnly')] [System.Management.Automation.SwitchParameter] $Last, [Parameter(Mandatory = $true, ParameterSetName = 'AllWithMaskLength')] [Parameter(Mandatory = $true, ParameterSetName = 'AllWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'AllIPOnly')] [System.Management.Automation.SwitchParameter] $All, [Parameter()] [System.Management.Automation.SwitchParameter] $Pool, [Parameter(ParameterSetName = 'SameIPWithMaskLength')] [Parameter(ParameterSetName = 'SubnetWithMaskLength')] [Parameter(ParameterSetName = 'BroadcastWithMaskLength')] [Parameter(ParameterSetName = 'FirstWithMaskLength')] [Parameter(ParameterSetName = 'LastWithMaskLength')] [Parameter(ParameterSetName = 'AllWithMaskLength')] [System.Management.Automation.SwitchParameter] $WithMaskLength, [Parameter(Mandatory = $true, ParameterSetName = 'SameIPWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'FirstWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'LastWithMask')] [Parameter(Mandatory = $true, ParameterSetName = 'AllWithMask')] [System.Management.Automation.SwitchParameter] $WithMask, [Parameter(Mandatory = $true, ParameterSetName = 'SameIPIPOnly')] [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIPOnly')] [Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIPOnly')] [Parameter(Mandatory = $true, ParameterSetName = 'FirstIPOnly')] [Parameter(Mandatory = $true, ParameterSetName = 'LastIPOnly')] [Parameter(Mandatory = $true, ParameterSetName = 'AllIPOnly')] [System.Management.Automation.SwitchParameter] $IPOnly, [Parameter(Mandatory = $true, ParameterSetName = 'MaskQuadDotOnly')] [System.Management.Automation.SwitchParameter] $MaskQuadDotOnly, [Parameter(Mandatory = $true, ParameterSetName = 'MaskLengthOnly')] [System.Management.Automation.SwitchParameter] $MaskLengthOnly, [Parameter(Mandatory = $true, ParameterSetName = 'MaskLengthWithSlashOnly')] [System.Management.Automation.SwitchParameter] $MaskLengthWithSlashOnly, [Parameter(Mandatory = $true, ParameterSetName = 'Info')] [System.Management.Automation.SwitchParameter] $Info ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' if ($Mask -eq '') { if (Test-IPv4Address -IP $IP) {throw "No mask defined for $IP"} } else { $Mask = $Mask | Convert-IPv4Mask -Length } if (-not (Test-IPv4Address -IP $IP)) { ($IP, $m) = $IP -split '[/ ]' $m = $m | Convert-IPv4Mask -Length if ($Mask -eq '') { $Mask = $m } elseif ($Mask -ne $m) { "Mask set to /$m in -IP but /$Mask in -Mask for $IP. Using /$Mask" | Write-Warning } } [System.String] $maskQuadDot = $Mask | Convert-IPv4Mask -QuadDot [System.UInt32] $maskInt = $Mask | Convert-IPv4Mask -Integer [System.UInt32] $ipInt = $IP | Convert-IPv4Address -Integer [System.UInt32] $subnetInt = $ipInt -band $maskInt [System.UInt32] $broadcastInt = $ipInt -bor (-bnot $maskInt) [System.UInt32] $firstInt = $subnetInt [System.UInt32] $lastInt = $broadcastInt if (-not ($Pool -or $Mask -eq 31 -or $Mask -eq 32)) { ++$firstInt --$lastInt } if ($Info) { [PSCustomObject] @{ IP = $ipInt | Convert-IPv4Address Subnet = $subnetInt | Convert-IPv4Address FirstIP = $firstInt | Convert-IPv4Address LastIP = $lastInt | Convert-IPv4Address Broadcast = $broadcastInt | Convert-IPv4Address Integer = [PSCustomObject] @{ IP = $ipInt Subnet = $subnetInt FirstIP = $firstInt LastIP = $lastInt Broadcast = $broadcastInt } Binary = [PSCustomObject] @{ IP = $ipInt | Convert-IPv4Address -Binary Subnet = $subnetInt | Convert-IPv4Address -Binary FirstIP = $firstInt | Convert-IPv4Address -Binary LastIP = $lastInt | Convert-IPv4Address -Binary Broadcast = $broadcastInt | Convert-IPv4Address -Binary } MaskQuadDot = $maskQuadDot MaskLength = $Mask } } else { $createScript = if ($Subnet) { {$subnetInt} } elseif ($Broadcast) { {$broadcastInt} } elseif ($First) { {$firstInt} } elseif ($Last) { {$lastInt} } elseif ($All) { {for ([uint32] $i = $firstInt; $i -le $lastInt; $i++) {$i}} } else { {$ipInt} } $outputScript = if ($WithMask) { {'{0} {1}' -f $_, $maskQuadDot} } elseif ($IPOnly) { {$_} } elseif ($MaskQuadDotOnly) { {$maskQuadDot} } elseif ($MaskLengthOnly) { {$Mask} } elseif ($MaskLengthWithSlashOnly) { {'/{0}' -f $Mask} } else { {'{0}/{1}' -f $_, $Mask} } $createScript.Invoke() | Convert-IPv4Address | ForEach-Object -Process $outputScript } } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Get-IPv4Mask { <# .SYNOPSIS Get IP subnet mask for an IP address .DESCRIPTION Get IP subnet mask for an IP address .PARAMETER IP Input IP in quad dot format with subnet mask, either: - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0" - IP + mask length, eg. "127.0.0.1/8" .PARAMETER QuadDot Return subnet mask in "255.0.0.0" format .PARAMETER Length Return subnet mask in "8" format .PARAMETER LengthWithSlash Return subnet mask in "/8" format .EXAMPLE Get-IPv4Mask 9.8.7.6/22 255.255.252.0 .EXAMPLE Get-IPv4Mask 9.8.7.6/22 -LengthWithSlash /22 .EXAMPLE Get-IPv4Mask 9.8.7.6/255.255.252.0 -Length 22 #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $IP, #[Parameter()] #[ValidateScript({ (Test-IPv4Address -IP $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })] #[System.String] #$Mask = '', [Parameter(ParameterSetName = 'QuadDot')] [System.Management.Automation.SwitchParameter] $QuadDot, [Parameter(Mandatory = $true, ParameterSetName = 'Length')] [System.Management.Automation.SwitchParameter] $Length, [Parameter(Mandatory = $true, ParameterSetName = 'LengthWithSlash')] [System.Management.Automation.SwitchParameter] $LengthWithSlash ) process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" $params = @{ IP = $IP } #if ($Mask -ne '') {$params['Mask'] = $Mask} if ($Length) {$params['MaskLengthOnly'] = $true} elseif ($LengthWithSlash) {$params['MaskLengthWithSlashOnly'] = $true} else {$params['MaskQuadDotOnly'] = $true} Get-IPv4Address @params Write-Verbose -Message 'Process end' } } function Get-IPv4Subnet { <# .SYNOPSIS Get IP subnet for an IP address .DESCRIPTION Get IP subnet for an IP address .PARAMETER IP Input IP in quad dot format with subnet mask, either: - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0" - IP + mask length, eg. "127.0.0.1/8" If input is IP without subnet mask (eg. "127.0.0.1") then -Mask parameter must be set .PARAMETER Mask If input IP is in format without subnet mask, this parameter must be set to either - Quad dot format, eg. "255.255.255.0" - Mask length (0-32), eg. "24" - Mask length with slash, eg. "/24" .PARAMETER WithMaskLength Return in "127.0.0.0/8" format This is default output .PARAMETER WithMask Return in "127.0.0.0 255.0.0.0" format .PARAMETER IPOnly Return in "127.0.0.1" format .EXAMPLE Get-IPv4Subnet 127.0.0.1/8 127.0.0.0/8 .EXAMPLE Get-IPv4Subnet -IP 10.20.30.40/28 -WithMask 10.20.30.32 255.255.255.240 .EXAMPLE Get-IPv4Subnet -IP '10.20.30.40 255.255.255.240' -IPOnly 10.20.30.32 #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $IP, [Parameter()] [ValidateScript({ (Test-IPv4Address -IP $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })] [System.String] $Mask = '', [Parameter(ParameterSetName = 'SubnetWithMaskLength')] [System.Management.Automation.SwitchParameter] $WithMaskLength, [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithMask')] [System.Management.Automation.SwitchParameter] $WithMask, [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIPOnly')] [System.Management.Automation.SwitchParameter] $IPOnly ) process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" Get-IPv4Address -Subnet @PSBoundParameters Write-Verbose -Message 'Process end' } } function Get-IPv6Address { <# .SYNOPSIS Get subnet, prefix, ... for an IPv6 address .DESCRIPTION Get subnet, prefix, ... for an IPv6 address .PARAMETER IP Input IP is standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64") .PARAMETER Prefix If prefix is not set in IP address, it must be set with this parameter .PARAMETER SameIP Return same IP as input IP (why? maybe in a different format back) If input is "7:6:5::77:88/56", then "7:6:5::77:88/56" is returned .PARAMETER Subnet Return subnet If input is "7:6:5::77:88/56", then "7:6:5::/56" is returned .PARAMETER WithPrefix Return in "7:6:5::/64" format This is default output .PARAMETER IPOnly Return in "7:6:5::" format .PARAMETER PrefixOnly Only return prefix in "64" format .PARAMETER PrefixWithSlashOnly Only return prefix in "/64" format .PARAMETER Info Return object with different info .EXAMPLE Get-IPv6Address -IP 007:6:5::77:88/64 -Subnet 7:6:5::/64 .EXAMPLE Get-IPv6Address -IP 007:6:5::77:88/64 -Subnet -IPOnly 7:6:5:: .EXAMPLE Get-IPv6Address -IP 007:6:5::77:88/64 -IPOnly 7:6:5::77:88 .EXAMPLE Get-IPv6Address -IP 007:6:5::77:88/64 -Info IP : 7:6:5::77:88/64 Subnet : 7:6:5::/64 FirstIP : 7:6:5::/64 SecondIP : 7:6:5::1/64 PenultimateIP : 7:6:5::ffff:ffff:ffff:fffe/64 LastIP : 7:6:5::ffff:ffff:ffff:ffff/64 Objects : @{IP=; Subnet=; FirstIP=; SecondIP=; PenultimateIP=; LastIP=} #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $IP, [Parameter()] [Nullable[System.Byte]] $Prefix, [Parameter(ParameterSetName = 'SubnetWithPrefix')] [Parameter(ParameterSetName = 'SameIPIPOnly')] [System.Management.Automation.SwitchParameter] $SameIP, [Parameter(Mandatory = $true, ParameterSetName = 'SubnetWithPrefix')] [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIPOnly')] [System.Management.Automation.SwitchParameter] $Subnet, #[Parameter(Mandatory = $true, ParameterSetName = 'BroadcastWithPrefix')] #[Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIPOnly')] #[System.Management.Automation.SwitchParameter] #$Broadcast, #[Parameter(Mandatory = $true, ParameterSetName = 'FirstWithPrefix')] #[Parameter(Mandatory = $true, ParameterSetName = 'FirstIPOnly')] #[System.Management.Automation.SwitchParameter] #$First, #[Parameter(Mandatory = $true, ParameterSetName = 'LastWithPrefix')] #[Parameter(Mandatory = $true, ParameterSetName = 'LastIPOnly')] #[System.Management.Automation.SwitchParameter] #$Last, #[Parameter(Mandatory = $true, ParameterSetName = 'AllWithPrefix')] #[Parameter(Mandatory = $true, ParameterSetName = 'AllIPOnly')] #[System.Management.Automation.SwitchParameter] #$All, #[Parameter()] #[System.Management.Automation.SwitchParameter] #$Pool, [Parameter(ParameterSetName = 'SameIPWithPrefix')] [Parameter(ParameterSetName = 'SubnetWithPrefix')] #[Parameter(ParameterSetName = 'BroadcastWithPrefix')] #[Parameter(ParameterSetName = 'FirstWithPrefix')] #[Parameter(ParameterSetName = 'LastWithPrefix')] #[Parameter(ParameterSetName = 'AllWithPrefix')] [System.Management.Automation.SwitchParameter] $WithPrefix, [Parameter(Mandatory = $true, ParameterSetName = 'SameIPIPOnly')] [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIPOnly')] #[Parameter(Mandatory = $true, ParameterSetName = 'BroadcastIPOnly')] #[Parameter(Mandatory = $true, ParameterSetName = 'FirstIPOnly')] #[Parameter(Mandatory = $true, ParameterSetName = 'LastIPOnly')] #[Parameter(Mandatory = $true, ParameterSetName = 'AllIPOnly')] [System.Management.Automation.SwitchParameter] $IPOnly, [Parameter(Mandatory = $true, ParameterSetName = 'PrefixOnly')] [System.Management.Automation.SwitchParameter] $PrefixOnly, [Parameter(Mandatory = $true, ParameterSetName = 'PrefixWithSlashOnly')] [System.Management.Automation.SwitchParameter] $PrefixWithSlashOnly, [Parameter(Mandatory = $true, ParameterSetName = 'Info')] [System.Management.Automation.SwitchParameter] $Info ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $prefixParam = if ($Prefix -eq $null) { @{} } else { @{Prefix = $Prefix} } $ipInfo = Convert-IPv6Address -Info -IP $IP @prefixParam if ($ipInfo.Prefix -eq $null) {throw "No prefix defined for $IP"} $Prefix = $ipInfo.Prefix # Yeah yeah, I know that there's now broadcast IP in IPv6 and that every IP can be used! [uint16[]] $ipInt = $ipInfo.IPIntArray [uint16[]] $prefixInt = $ipInfo.PrefixIntArray # Why the f**k does binary operators not work properly with uint16 # For IPv4 it's just "$ip -band $mask" and "$ip -bor (-bnot $mask)" [uint16[]] $subnetInt = (0..7).ForEach({ [uint16] (([uint32]$ipInt[$_]) -band ([uint32]$prefixInt[$_])) }) [uint16[]] $lastInt = (0..7).ForEach({ [uint16] ((([uint32]$ipInt[$_]) -bor (-bnot ([uint32]$prefixInt[$_]))) -band [uint16]::MaxValue)}) [uint16[]] $firstInt = $subnetInt.Clone() [uint16[]] $secondInt = $firstInt.Clone() [uint16[]] $penultimateInt = $lastInt.Clone() if ($Prefix -ne 128) { # FIXXXME - should we do something else if it's /128? Set to $null? ++$secondInt[7] --$penultimateInt[7] } if ($Info) { $objects = [PSCustomObject] @{ IP = Convert-IPv6Address -IP $ipInt -Prefix $Prefix -Info Subnet = Convert-IPv6Address -IP $subnetInt -Prefix $Prefix -Info FirstIP = Convert-IPv6Address -IP $firstInt -Prefix $Prefix -Info SecondIP = Convert-IPv6Address -IP $secondInt -Prefix $Prefix -Info PenultimateIP = Convert-IPv6Address -IP $penultimateInt -Prefix $Prefix -Info LastIP = Convert-IPv6Address -IP $lastInt -Prefix $Prefix -Info } [PSCustomObject] @{ IP = $objects.IP.IP Subnet = $objects.Subnet.IP FirstIP = $objects.FirstIP.IP SecondIP = $objects.SecondIP.IP PenultimateIP = $objects.PenultimateIP.IP LastIP = $objects.LastIP.IP Prefix = $Prefix Objects = $objects } } else { $createScript = if ($Subnet) { {,@($subnetInt)} } #elseif ($Broadcast) { {$broadcastInt} } #elseif ($First) { {$firstInt} } #elseif ($Last) { {$lastInt} } #elseif ($All) { {for ([uint32] $i = $firstInt; $i -le $lastInt; $i++) {$i}} } else { {,@($ipInt)} } $outputScript = if ($IPOnly) { {(Convert-IPv6Address -IP $_ -Prefix $Prefix -Info).IPCompact} } elseif ($PrefixOnly) { {'{0}' -f $Prefix} } elseif ($PrefixWithSlashOnly) { {'/{0}' -f $Prefix} } else { {Convert-IPv6Address -IP $_ -Prefix $Prefix} } $createScript.Invoke() | ForEach-Object -Process $outputScript } } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Get-IPv6Subnet { <# .SYNOPSIS Get IP subnet for an IPv6 address .DESCRIPTION Get IP subnet for an IPv6 address .PARAMETER IP Input IP is standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64") .PARAMETER Prefix If prefix is not set in IP address, it must be set with this parameter .PARAMETER WithPrefix Return in "7:6:5::/64" format This is default output .PARAMETER IPOnly Return in "7:6:5::" format .EXAMPLE Get-IPv6Subnet -IP 7:6:5::77:88/64 7:6:5::/64 .EXAMPLE Get-IPv6Subnet -IP 7:6:5::77:88/64 -IPOnly 7:6:5:: #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $IP, [Parameter()] [Nullable[System.Byte]] $Prefix, [Parameter(ParameterSetName = 'SubnetWithPrefix')] [System.Management.Automation.SwitchParameter] $WithPrefix, [Parameter(Mandatory = $true, ParameterSetName = 'SubnetIPOnly')] [System.Management.Automation.SwitchParameter] $IPOnly ) process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" Get-IPv6Address -Subnet @PSBoundParameters Write-Verbose -Message 'Process end' } } function Test-IPv4Address { <# .SYNOPSIS Test if a string contains a valid IP address (IPv4) .DESCRIPTION Test if a string contains a valid IP address (IPv4) Uses regex to test Returns [bool] .PARAMETER IP IP address (or subnet mask) to test is valid or not .PARAMETER IPOnly Only return True if input is valid IPv4 in quad dot format (without subnet mask) Eg. "127.0.0.1" This is default .PARAMETER Mask Only return True if input is valid IPv4 subnet mask in quad dot format Eg. "255.255.255.0" .PARAMETER AllowLength Used along with -Mask Only return true if input is subnet mask in: - Quad dot format, eg. "255.255.255.0" - Mask length (0-32), eg. "24" - Mask length with slash, eg. "/24" .PARAMETER AllowMask Return true if input is either - IP in quad dot, eg. "127.0.0.1" - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0" - IP + mask length, eg. "127.0.0.1/8" .PARAMETER RequireMask Return true if input is IP with mask, either - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0" - IP + mask length, eg. "127.0.0.1/8" .EXAMPLE Test-IPv4Address -IP 127.0.0.1 True .EXAMPLE Test-IPv4Address -IP 127.0.0.256 False .EXAMPLE Test-IPv4Address -IP 127.0.0.1/32 False .EXAMPLE Test-IPv4Address -AllowMask -IP 127.0.0.1/32 True .EXAMPLE Test-IPv4Address -AllowMask -IP "127.0.0.1 255.255.255.255" True .EXAMPLE Test-IPv4Address -AllowMask -IP "127.0.0.1" True .EXAMPLE Test-IPv4Address -RequireMask -IP 127.0.0.1/32 True .EXAMPLE Test-IPv4Address -RequireMask -IP 127.0.0.1 False .EXAMPLE Test-IPv4Address -Mask -IP 255.255.0.0 True .EXAMPLE Test-IPv4Address -Mask -IP 255.0.255.0 False .EXAMPLE Test-IPv4Address -Mask -IP 32 False .EXAMPLE Test-IPv4Address -Mask -AllowLength -IP 255.0.255.0 False .EXAMPLE Test-IPv4Address -Mask -AllowLength -IP 255.255.0.0 True .EXAMPLE Test-IPv4Address -Mask -AllowLength -IP 32 True .EXAMPLE Test-IPv4Address -Mask -AllowLength -IP /32 True #> [OutputType([System.Boolean])] [CmdletBinding(DefaultParameterSetName = 'IPOnly')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $IP, [Parameter(ParameterSetName = 'IPOnly')] [System.Management.Automation.SwitchParameter] $IPOnly, [Parameter(Mandatory = $true, ParameterSetName = 'Mask')] [System.Management.Automation.SwitchParameter] $Mask, [Parameter(ParameterSetName = 'Mask')] [System.Management.Automation.SwitchParameter] $AllowLength, [Parameter(Mandatory = $true, ParameterSetName = 'AllowMask')] [System.Management.Automation.SwitchParameter] $AllowMask, [Parameter(Mandatory = $true, ParameterSetName = 'RequireMask')] [System.Management.Automation.SwitchParameter] $RequireMask ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $i = '(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' $s = '(((255\.){3}(255|254|252|248|240|224|192|128|0+))|((255\.){2}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){2})|((255|254|252|248|240|224|192|128|0+)(\.0+){3}))' $b = '[0-9]|[12][0-9]|3[0-2]' $matchIP = "^($i)$" $matchMask = "^($s)$" $matchMaskAndLength = "^(($s)|(/?($b)))$" $matchAll = "^($i)[/ ](($s)|($b))$" ( ($PSCmdlet.ParameterSetName -eq 'IPOnly' -and $IP -match $matchIP ) -or ($PSCmdlet.ParameterSetName -eq 'Mask' -and $IP -match $matchMask -and -not $AllowLength) -or ($PSCmdlet.ParameterSetName -eq 'Mask' -and $IP -match $matchMaskAndLength -and $AllowLength) -or ($PSCmdlet.ParameterSetName -eq 'AllowMask' -and $IP -match $matchIP ) -or ($PSCmdlet.ParameterSetName -eq 'AllowMask' -and $IP -match $matchAll ) -or ($PSCmdlet.ParameterSetName -eq 'RequireMask' -and $IP -match $matchAll ) ) } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv4AddressInSameNet { <# .SYNOPSIS Test if two IP addresses are in the same subnet .DESCRIPTION Test if two IP addresses are in the same subnet .PARAMETER IP Input IP in quad dot format with subnet mask, either: - IP + mask in quad dot, eg. "127.0.0.1 255.0.0.0" - IP + mask length, eg. "127.0.0.1/8" If input is IP without subnet mask (eg. "127.0.0.1") then -Mask parameter must be set .PARAMETER IP2 Same format as -IP .PARAMETER Mask If input IP is in format without subnet mask, this parameter must be set to either - Quad dot format, eg. "255.255.255.0" - Mask length (0-32), eg. "24" - Mask length with slash, eg. "/24" .PARAMETER AllowMaskMismatch Return true if hosts with the two IP addresses can communicate with each other directly (not routed), even if there's a mismatch in subnet mask between the two. .EXAMPLE Test-IPv4AddressInSameNet -IP 10.30.50.60 -IP2 10.30.50.61/24 True .EXAMPLE Test-IPv4AddressInSameNet -IP 10.30.50.60/24 -IP2 10.30.50.61/255.255.255.0 True .EXAMPLE Test-IPv4AddressInSameNet -IP 10.30.50.60/24 -IP2 10.30.50.61/29 False .EXAMPLE Test-IPv4AddressInSameNet -IP 10.30.50.60/24 -IP2 10.30.50.61/29 -AllowMaskMismatch True #> [OutputType([System.Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $IP, [Parameter(Mandatory = $true, Position=1)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $IP2, [Parameter()] [ValidateScript({ (Test-IPv4Address -IP $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })] [System.String] $Mask = '', [Parameter()] [System.Management.Automation.SwitchParameter] $AllowMaskMismatch ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $null = $PSBoundParameters.Remove('AllowMaskMismatch') $null = $PSBoundParameters.Remove('IP') $null = $PSBoundParameters.Remove('IP2') if ( $Mask -or ((Test-IPv4Address -IP $IP -RequireMask) -and (Test-IPv4Address -IP $IP2 -RequireMask)) ) { $info1 = Get-IPv4Address -Info -IP $IP @PSBoundParameters $info2 = Get-IPv4Address -Info -IP $IP2 @PSBoundParameters } elseif (Test-IPv4Address -IP $IP -RequireMask) { $info1 = Get-IPv4Address -Info -IP $IP @PSBoundParameters $null = $PSBoundParameters.Remove('Mask') $info2 = Get-IPv4Address -Info -IP $IP2 -Mask $info1.MaskLength @PSBoundParameters } elseif (Test-IPv4Address -IP $IP2 -RequireMask) { $info2 = Get-IPv4Address -Info -IP $IP2 @PSBoundParameters $null = $PSBoundParameters.Remove('Mask') $info1 = Get-IPv4Address -Info -IP $IP -Mask $info2.MaskLength @PSBoundParameters } else { throw "No mask defined for either IP ($IP) or IP2 ($IP2), and -Mask parameter is not set" } if ($info1.IP -eq $info2.IP) { "-IP and -IP2 is the same ($IP)" | Write-Warning } # Return ($info1.Subnet -eq $info2.Subnet -and $info1.MaskLength -eq $info2.MaskLength) -or ( $AllowMaskMismatch -and $info1.Integer.IP -ge $info2.Integer.FirstIP -and $info1.Integer.IP -le $info2.Integer.LastIP -and $info2.Integer.IP -ge $info1.Integer.FirstIP -and $info2.Integer.IP -le $info1.Integer.LastIP ) } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv4AddressInSubnet { <# .SYNOPSIS Test if IP address is in a subnet .DESCRIPTION Test if IP address is in a subnet .PARAMETER Subnet Input IP in quad dot format with subnet mask, either: - IP + mask in quad dot, eg. "127.0.0.0 255.0.0.0" - IP + mask length, eg. "127.0.0.0/8" If input is IP without subnet mask (eg. "127.0.0.0") then -Mask parameter must be set .PARAMETER IP Same format as -Subnet .PARAMETER Mask If subnet is in format without subnet mask, this parameter must be set to either - Quad dot format, eg. "255.255.255.0" - Mask length (0-32), eg. "24" - Mask length with slash, eg. "/24" .PARAMETER AllowMaskMismatch Return true if IP is in subnet, even if the subnet mask is wrong. .EXAMPLE Test-IPv4AddressInSubnet -Subnet 10.30.50.0/24 -IP 10.30.50.70 True .EXAMPLE Test-IPv4AddressInSubnet -Subnet 10.30.50.0/24 -IP 10.30.50.70/24 True .EXAMPLE Test-IPv4AddressInSubnet -Subnet 10.30.50.0/24 -IP 10.30.50.70/29 False .EXAMPLE Test-IPv4AddressInSubnet -Subnet 10.30.50.0/24 -IP 10.30.50.70/29 -AllowMaskMismatch True .EXAMPLE Test-IPv4AddressInSubnet -Subnet 10.30.50.0/24 -IP 10.30.50.70/23 -AllowMaskMismatch True .EXAMPLE Test-IPv4AddressInSubnet -Subnet 10.30.50.0/24 -IP 10.30.51.70/23 -AllowMaskMismatch False #> [OutputType([System.Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $Subnet, [Parameter(Mandatory = $true, Position=1)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $IP, [Parameter()] [ValidateScript({ (Test-IPv4Address -IP $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })] [System.String] $Mask = '', [Parameter()] [System.Management.Automation.SwitchParameter] $AllowMaskMismatch ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $null = $PSBoundParameters.Remove('AllowMaskMismatch') $null = $PSBoundParameters.Remove('Subnet') $null = $PSBoundParameters.Remove('IP') if (-not (Test-IPv4Subnet -Subnet $Subnet @PSBoundParameters)) { throw "$Subnet (mask $Mask) is not a subnet" } elseif ( $Mask -or ((Test-IPv4Address -IP $Subnet -RequireMask) -and (Test-IPv4Address -IP $IP -RequireMask)) ) { $info1 = Get-IPAddress -Info -IP $Subnet @PSBoundParameters $info2 = Get-IPAddress -Info -IP $IP @PSBoundParameters } elseif (Test-IPv4Address -IP $Subnet -RequireMask) { $info1 = Get-IPAddress -Info -IP $Subnet @PSBoundParameters $null = $PSBoundParameters.Remove('Mask') $info2 = Get-IPAddress -Info -IP $IP -Mask $info1.MaskLength @PSBoundParameters } else { throw "No mask defined for Subnet ($Subnet), and -Mask parameter is not set" } if ($info1.IP -eq $info2.IP) { "-Subnet and -IP is the same ($IP)" | Write-Warning } # Return ($info1.Subnet -eq $info2.Subnet -and $info1.MaskLength -eq $info2.MaskLength) -or ( $AllowMaskMismatch -and $info2.Integer.IP -ge $info1.Integer.FirstIP -and $info2.Integer.IP -le $info1.Integer.LastIP ) } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv4AddressIsPrivate { <# .SYNOPSIS Test if IP address is in a private segment .DESCRIPTION Test if IP address is in a private segment .PARAMETER IP Input IP in quad dot format with or without subnet mask .PARAMETER xxx xxx .EXAMPLE xxx #> [OutputType([System.Boolean])] [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $true, Position=1)] [ValidateScript({ (Test-IPv4Address -IP $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $IP, [Parameter(Mandatory = $true, ParameterSetName = 'Rfc1918')] [System.Management.Automation.SwitchParameter] $Rfc1918, [Parameter(Mandatory = $true, ParameterSetName = 'Rfc6598')] [System.Management.Automation.SwitchParameter] $Rfc6598 ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $subnets = @() if ($PSCmdlet.ParameterSetName -in 'Default','Rfc1918') { $subnets += '10.0.0.0/8','172.16.0.0/12','192.168.0.0/16' } if ($PSCmdlet.ParameterSetName -in 'Default','Rfc6598') { $subnets += '100.64.0.0/10' } $return = $false foreach ($subnet in $subnets) { if (Test-IPv4AddressInSubnet -Subnet $subnet -IP $IP -AllowMaskMismatch -WarningAction SilentlyContinue) { $return = $true break } } # Return $return } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv4Subnet { <# .SYNOPSIS Test if IP address is a valid subnet address .DESCRIPTION Test if IP address is a valid subnet address .PARAMETER Subnet Input IP in quad dot format with subnet mask, either: - IP + mask in quad dot, eg. "127.0.0.0 255.0.0.0" - IP + mask length, eg. "127.0.0.0/8" If input is IP without subnet mask (eg. "127.0.0.0") then -Mask parameter must be set .PARAMETER Mask If input IP is in format without subnet mask, this parameter must be set to either - Quad dot format, eg. "255.255.255.0" - Mask length (0-32), eg. "24" - Mask length with slash, eg. "/24" .EXAMPLE Test-IPv4Subnet -Subnet 10.20.30.0/24 True .EXAMPLE Test-IPv4Subnet -Subnet 10.20.30.0/255.255.0.0 False #> [OutputType([System.Boolean])] [CmdletBinding(DefaultParameterSetName = 'IPOnly')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $Subnet, [Parameter()] [System.String] $Mask = '' ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $null = $PSBoundParameters.Remove('Subnet') $info = Get-IPv4Address -Info -IP $Subnet @PSBoundParameters # Return $info.IP -eq $info.Subnet } catch { # Never throw, just return false $false } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv6Address { <# .SYNOPSIS Test if a string contains a valid IP address (IPv6) .DESCRIPTION Test if a string contains a valid IP address (IPv6) Returns [bool] .PARAMETER IP IP address to test is valid or not .PARAMETER IPOnly Only return True if input is valid IPv6 address (without prefix) Eg. "a:b::c" This is default .PARAMETER AllowPrefix Return true if input valid IPv6 address with or without prefix Eg. "a:b::c" or "a:b::c/64" .PARAMETER RequirePrefix Return true if input valid IPv6 address with prefix Eg. "a:b::c/64" .EXAMPLE Test-IPv6Address -IP a:b::c True .EXAMPLE Test-IPv6Address -IP a:b::c/64 False .EXAMPLE Test-IPv6Address -IP a:b::x False .EXAMPLE Test-IPv6Address -IP a:b::c/64 False .EXAMPLE Test-IPv6Address -AllowPrefix -IP a:b::c/64 True .EXAMPLE Test-IPv6Address -AllowPrefix -IP a:b::c True .EXAMPLE Test-IPv6Address -AllowPrefix -IP a:b::x/64 False .EXAMPLE Test-IPv6Address -RequirePrefix -IP a:b::c/64 True .EXAMPLE Test-IPv6Address -RequirePrefix -IP a:b::c False #> [OutputType([System.Boolean])] [CmdletBinding(DefaultParameterSetName = 'IPOnly')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $IP, [Parameter(ParameterSetName = 'IPOnly')] [System.Management.Automation.SwitchParameter] $IPOnly, [Parameter(Mandatory = $true, ParameterSetName = 'AllowPrefix')] [System.Management.Automation.SwitchParameter] $AllowPrefix, [Parameter(Mandatory = $true, ParameterSetName = 'RequirePrefix')] [System.Management.Automation.SwitchParameter] $RequirePrefix ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' # RegEx for IPv6 is just %&(%&�&(=)%&/%&� # We just try to parse it instead - and don't do RegEx match as we do with IPv4! # And yes - there's probably lot's of weird IPv6 syntaxes that doesn't work with this! $ipObject = Convert-IPv6Address -IP $IP -Info # Return ($PSCmdlet.ParameterSetName -eq 'IPOnly' -and $ipObject.Prefix -eq $null) -or ($PSCmdlet.ParameterSetName -eq 'RequirePrefix' -and $ipObject.Prefix -ne $null) -or ($PSCmdlet.ParameterSetName -eq 'AllowPrefix') } catch { # Never throw, just return false $false } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv6AddressInSameNet { <# .SYNOPSIS Test if two IP addresses are in the same subnet (IPv6) .DESCRIPTION Test if two IP addresses are in the same subnet (IPv6) .PARAMETER IP Input IP is standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64") .PARAMETER IP2 Same format as -IP .PARAMETER Prefix If prefix is not set in IP address, it must be set with this parameter .PARAMETER AllowPrefixMismatch Return true if hosts with the two IP addresses can communicate with each other directly (not routed), even if there's a mismatch in prefix between the two. .EXAMPLE Test-IPv6AddressInSameNet a:2::/31 a:3::/31 True .EXAMPLE Test-IPv6AddressInSameNet a:2::/32 a:3::/32 False .EXAMPLE Test-IPv6AddressInSameNet a:2::/31 a:3::/30 False .EXAMPLE Test-IPv6AddressInSameNet a:2::/31 a:3::/32 -AllowPrefixMismatch False .EXAMPLE Test-IPv6AddressInSameNet a:2::/31 a:3::/30 -AllowPrefixMismatch True #> [OutputType([System.Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-IPv6Address -IP $_ -AllowPrefix) -or $(throw "$_ is not a valid IPv6 address") })] [System.String] $IP, [Parameter(Mandatory = $true, Position=1)] [ValidateScript({ (Test-IPv6Address -IP $_ -AllowPrefix) -or $(throw "$_ is not a valid IPv6 address") })] [System.String] $IP2, [Parameter()] [Nullable[System.Byte]] $Prefix, [Parameter()] [System.Management.Automation.SwitchParameter] $AllowPrefixMismatch ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $null = $PSBoundParameters.Remove('AllowPrefixMismatch') $null = $PSBoundParameters.Remove('IP') $null = $PSBoundParameters.Remove('IP2') if ( $Prefix -or ((Test-IPv6Address -IP $IP -RequirePrefix) -and (Test-IPv6Address -IP $IP2 -RequirePrefix)) ) { $info1 = Get-IPv6Address -Info -IP $IP @PSBoundParameters $info2 = Get-IPv6Address -Info -IP $IP2 @PSBoundParameters } elseif (Test-IPv6Address -IP $IP -RequirePrefix) { $info1 = Get-IPv6Address -Info -IP $IP @PSBoundParameters $null = $PSBoundParameters.Remove('Prefix') $info2 = Get-IPv6Address -Info -IP $IP2 -Prefix $info1.Prefix @PSBoundParameters } elseif (Test-IPv6Address -IP $IP2 -RequirePrefix) { $info2 = Get-IPv6Address -Info -IP $IP2 @PSBoundParameters $null = $PSBoundParameters.Remove('Prefix') $info1 = Get-IPv6Address -Info -IP $IP -Prefix $info2.Prefix @PSBoundParameters } else { throw "No prefix defined for either IP ($IP) or IP2 ($IP2), and -Prefix parameter is not set" } if ($info1.IP -eq $info2.IP) { "-IP and -IP2 is the same ($IP)" | Write-Warning } # Return ($info1.Subnet -eq $info2.Subnet -and $info1.Prefix -eq $info2.Prefix) -or ( $AllowPrefixMismatch -and $info1.Objects.IP.IPBinary -ge $info2.Objects.FirstIP.IPBinary -and $info1.Objects.IP.IPBinary -le $info2.Objects.LastIP.IPBinary -and $info2.Objects.IP.IPBinary -ge $info1.Objects.FirstIP.IPBinary -and $info2.Objects.IP.IPBinary -le $info1.Objects.LastIP.IPBinary ) } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv6AddressInSubnet { <# .SYNOPSIS Test if two IP addresses are in the same subnet (IPv6) .DESCRIPTION Test if two IP addresses are in the same subnet (IPv6) .PARAMETER Subnet Input IP is standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64") .PARAMETER IP Same format as -Subnet .PARAMETER Prefix If prefix is not set in subnet address, it must be set with this parameter .PARAMETER AllowPrefixMismatch Return true if hosts with the two IP addresses can communicate with each other directly (not routed), even if there's a mismatch in prefix between the two. .EXAMPLE Test-IPv6AddressInSubnet -Subnet a:2::/31 -IP a:3::/31 True .EXAMPLE Test-IPv6AddressInSubnet -Subnet a:2::/32 -IP a:3::/32 False .EXAMPLE Test-IPv6AddressInSubnet -Subnet a:2::/31 -IP a:3::/30 False .EXAMPLE Test-IPv6AddressInSubnet -Subnet a:2::/32 -IP a:3::/31 -AllowPrefixMismatch False .EXAMPLE Test-IPv6AddressInSubnet -Subnet a:2::/31 -IP a:3::/32 -AllowPrefixMismatch True #> [OutputType([System.Boolean])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-IPv6Address -IP $_ -AllowPrefix) -or $(throw "$_ is not a valid IPv6 address") })] [System.String] $Subnet, [Parameter(Mandatory = $true, Position=1)] [ValidateScript({ (Test-IPv6Address -IP $_ -AllowPrefix) -or $(throw "$_ is not a valid IPv6 address") })] [System.String] $IP, [Parameter()] [Nullable[System.Byte]] $Prefix, [Parameter()] [System.Management.Automation.SwitchParameter] $AllowPrefixMismatch ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $null = $PSBoundParameters.Remove('AllowPrefixMismatch') $null = $PSBoundParameters.Remove('Subnet') $null = $PSBoundParameters.Remove('IP') if (-not (Test-IPv6Subnet -Subnet $Subnet @PSBoundParameters)) { throw "$Subnet (prefix $Prefix) is not a subnet" } elseif ( $Prefix -or ((Test-IPv6Address -IP $Subnet -RequirePrefix) -and (Test-IPv6Address -IP $IP -RequirePrefix)) ) { $info1 = Get-IPv6Address -Info -IP $Subnet @PSBoundParameters $info2 = Get-IPv6Address -Info -IP $IP @PSBoundParameters } elseif (Test-IPv6Address -IP $Subnet -RequirePrefix) { $info1 = Get-IPv6Address -Info -IP $Subnet @PSBoundParameters $null = $PSBoundParameters.Remove('Prefix') $info2 = Get-IPv6Address -Info -IP $IP -Prefix $info1.Prefix @PSBoundParameters } else { throw "No prefix defined for Subnet ($Subnet), and -Prefix parameter is not set" } if ($info1.IP -eq $info2.IP) { "-Subnet and -IP is the same ($IP)" | Write-Warning } # Return ($info1.Subnet -eq $info2.Subnet -and $info1.Prefix -eq $info2.Prefix) -or ( $AllowPrefixMismatch -and $info2.Objects.IP.IPBinary -ge $info1.Objects.FirstIP.IPBinary -and $info2.Objects.IP.IPBinary -le $info1.Objects.LastIP.IPBinary ) } catch { # If error was encountered inside this function then stop doing more # But still respect the ErrorAction that comes when calling this function # And also return the line number where the original error occured $msg = $_.ToString() + "`r`n" + $_.InvocationInfo.PositionMessage.ToString() Write-Verbose -Message "Encountered an error: $msg" Write-Error -ErrorAction $origErrorActionPreference -Exception $_.Exception -Message $msg } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } function Test-IPv6Subnet { <# .SYNOPSIS Test if IP address is a valid subnet address (IPv6) .DESCRIPTION Test if IP address is a valid subnet address (IPv6) .PARAMETER Subnet Input IP is standard IPv6 format with out prefix (eg. "a:b:00c::" or "a:b:00c::0/64") .PARAMETER Prefix If prefix is not set in subnet address, it must be set with this parameter .EXAMPLE Test-IPv6Subnet -Subnet a:0:0:b::/64 True .EXAMPLE Test-IPv6Subnet -Subnet a:0:0:0:b::/64 False #> [OutputType([System.Boolean])] [CmdletBinding(DefaultParameterSetName = 'IPOnly')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $Subnet, [Parameter()] [System.String] $Prefix = '' ) begin { Write-Verbose -Message "Begin (ErrorActionPreference: $ErrorActionPreference)" $origErrorActionPreference = $ErrorActionPreference $verbose = $PSBoundParameters.ContainsKey('Verbose') -or ($VerbosePreference -ne 'SilentlyContinue') } process { Write-Verbose -Message "Process begin (ErrorActionPreference: $ErrorActionPreference)" try { # Make sure that we don't continue on error, and that we catches the error $ErrorActionPreference = 'Stop' $null = $PSBoundParameters.Remove('Subnet') $info = Get-IPv6Address -Info -IP $Subnet @PSBoundParameters # Return $info.IP -eq $info.Subnet } catch { # Never throw, just return false $false } finally { $ErrorActionPreference = $origErrorActionPreference } Write-Verbose -Message 'Process end' } end { Write-Verbose -Message 'End' } } Set-Alias -Name Convert-IP -Value Convert-IPv4Address Set-Alias -Name Convert-IPAddress -Value Convert-IPv4Address Set-Alias -Name Convert-IPMask -Value Convert-IPv4Mask Set-Alias -Name Get-IPAddress -Value Get-IPv4Address Set-Alias -Name Get-Mask -Value Get-IPv4Mask Set-Alias -Name Get-Subnet -Value Get-IPv4Subnet Set-Alias -Name Test-IPAddress -Value Test-IPv4Address Export-ModuleMember -Function Get-IPv4Mask Export-ModuleMember -Function Get-IPv4Subnet Export-ModuleMember -Function Test-IPv4Address Export-ModuleMember -Function Test-IPv6Subnet Export-ModuleMember -Function Convert-IPv4Mask Export-ModuleMember -Function Test-IPv6Address Export-ModuleMember -Function Convert-IPv4Address Export-ModuleMember -Function Test-IPv6AddressInSameNet Export-ModuleMember -Function Test-IPv4Subnet Export-ModuleMember -Function Test-IPv6AddressInSubnet Export-ModuleMember -Function Convert-IPv6Address Export-ModuleMember -Function Get-IPv6Address Export-ModuleMember -Function Test-IPv4AddressInSubnet Export-ModuleMember -Function Test-IPv4AddressIsPrivate Export-ModuleMember -Function Test-IPv4AddressInSameNet Export-ModuleMember -Function Get-IPv6Subnet Export-ModuleMember -Function Get-IPv4Address Export-ModuleMember -Alias Convert-IPMask Export-ModuleMember -Alias Get-IPAddress Export-ModuleMember -Alias Get-Mask Export-ModuleMember -Alias Convert-IP Export-ModuleMember -Alias Convert-IPAddress Export-ModuleMember -Alias Get-Subnet Export-ModuleMember -Alias Test-IPAddress |