SimpleIP.psm1
function Convert-IPv4Address { <# .SYNOPSIS Convert IP address between formats .DESCRIPTION Convert IP address between formats .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 #> [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-ValidIPv4 -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 formats .DESCRIPTION Convert IP subnet mask between formats 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 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-ValidIPv4 -Mask)) {throw} } catch { throw "$Mask is not a valid subnet mask" } if (Test-ValidIPv4 -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 .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 -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 : 00000000000010100000000000001011000000000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000 Cidr : a:b:c::/64 Prefix : 64 PrefixIntArray : {65535, 65535, 65535, 65535...} PrefixHexArray : {ffff, ffff, ffff, ffff...} PrefixHexArrayExpanded : {ffff, ffff, ffff, ffff...} PrefixBinary : 11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000 #> [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]) { 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 = $prefixIntArray = $prefixHexArray = $prefixHexArrayExpanded = $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) { $ipReturn = $cidr = '{0}/{1}' -f $ipCompact, $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 $_ } } $r = [PSCustomObject] @{ IP = $ipReturn IPCompact = $ipCompact IPExpanded = $ipExpanded IPIntArray = $ipIntArray IPHexArray = $ipHexArray IPHexArrayExpanded = $ipHexArrayExpanded IPBinary = $ipBinary Cidr = $cidr Prefix = $Prefix PrefixIntArray = $prefixIntArray PrefixHexArray = $prefixHexArray PrefixHexArrayExpanded = $prefixHexArrayExpanded 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 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 -Broadcast -WithMask 127.255.255.255 255.0.0.0 .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 MaskQuadDot : 255.255.255.128 MaskLength : 25 #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-ValidIPv4 -Ip $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $Ip, [Parameter()] [ValidateScript({ (Test-ValidIPv4 -Ip $_ -Mask -AllowLength) -or $(throw "$_ is not a valid IPv4 mask") })] [System.String] $Mask = '', [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 = 'SubnetWithMaskLength')] [Parameter(ParameterSetName = 'BroadcastWithMaskLength')] [Parameter(ParameterSetName = 'FirstWithMaskLength')] [Parameter(ParameterSetName = 'LastWithMaskLength')] [Parameter(ParameterSetName = 'AllWithMaskLength')] [System.Management.Automation.SwitchParameter] $WithMaskLength, [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 = '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-ValidIPv4 -Ip $Ip) {throw "No mask defined for $Ip"} } else { $Mask = $Mask | Convert-IPv4Mask -Length } if (-not (Test-ValidIPv4 -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 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 -QuadDot 255.255.252.0 #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-ValidIPv4 -Ip $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $Ip, #[Parameter()] #[ValidateScript({ (Test-ValidIPv4 -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 #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [ValidateScript({ (Test-ValidIPv4 -Ip $_ -AllowMask) -or $(throw "$_ is not a valid IPv4 address") })] [System.String] $Ip, [Parameter()] [ValidateScript({ (Test-ValidIPv4 -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 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 Info Return object with different info .EXAMPLE Get-IPv6Address -Ip 7:6:5::77:88/64 -Subnet 7:6:5::/64 .EXAMPLE Get-IPv6Address -Ip 7:6:5::77:88/64 -Info IP : 7:6:5::77:88/64 Subnet : 7:6:5::/64 FirstIP4Real : 7:6:5::/64 FirstIP : 7:6:5::1/64 LastIP : 7:6:5::ffff:ffff:ffff:fffe/64 LastIP4Real : 7:6:5::ffff:ffff:ffff:ffff/64 #> [OutputType([System.String])] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=0)] [System.String] $Ip, [Parameter()] [Nullable[System.Byte]] $Prefix, [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 = 'SubnetWithPrefix')] #[Parameter(ParameterSetName = 'BroadcastWithPrefix')] #[Parameter(ParameterSetName = 'FirstWithPrefix')] #[Parameter(ParameterSetName = 'LastWithPrefix')] #[Parameter(ParameterSetName = 'AllWithPrefix')] [System.Management.Automation.SwitchParameter] $WithPrefix, [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[]] $broadcastInt = (0..7).ForEach({ [uint16] ((([uint32]$ipInt[$_]) -bor (-bnot ([uint32]$prefixInt[$_]))) -band [uint16]::MaxValue)}) [uint16[]] $firstInt = $subnetInt.Clone() [uint16[]] $lastInt = $broadcastInt.Clone() if ($Prefix -ne 128) { ++$firstInt[7] --$lastInt[7] } if ($Info) { [PSCustomObject] @{ IP = Convert-IPv6Address -Ip $ipInt -Prefix $Prefix Subnet = Convert-IPv6Address -Ip $subnetInt -Prefix $Prefix FirstIP4Real = Convert-IPv6Address -Ip $subnetInt -Prefix $Prefix FirstIP = Convert-IPv6Address -Ip $firstInt -Prefix $Prefix LastIP = Convert-IPv6Address -Ip $lastInt -Prefix $Prefix LastIP4Real = Convert-IPv6Address -Ip $broadcastInt -Prefix $Prefix } } 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} } else { throw 'Unknown error' } $outputScript = if ($IpOnly) { {(Convert-IPv6Address -Ip $_ -Prefix $Prefix -Info).IPCompact} } #elseif ($PrefixOnly) { {} } #elseif ($PrefixWithSlashOnly) { {} } 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-ValidIPv4 { <# .SYNOPSIS Test if a string contains a valid IP address .DESCRIPTION Test if a string contains a valid IP address 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-ValidIPv4 -Ip 127.0.0.1 True .EXAMPLE Test-ValidIPv4 -Ip 127.0.0.256 False .EXAMPLE Test-ValidIPv4 -Ip 127.0.0.1/32 False .EXAMPLE Test-ValidIPv4 -Ip 127.0.0.1/32 -AllowMask True .EXAMPLE Test-ValidIPv4 -Ip "127.0.0.1 255.255.255.255" -AllowMask True .EXAMPLE Test-ValidIPv4 -Ip 127.0.0.1 -RequireMask False .EXAMPLE Test-ValidIPv4 -Ip 255.255.0.0 -Mask True .EXAMPLE Test-ValidIPv4 -Ip 255.0.255.0 -Mask False .EXAMPLE Test-ValidIPv4 -Ip 32 -Mask False .EXAMPLE Test-ValidIPv4 -Ip 32 -Mask -AllowLength True .EXAMPLE Test-ValidIPv4 -Ip /32 -Mask -AllowLength 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' } } 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-ValidIP -Value Test-ValidIPv4 Export-ModuleMember -Function Get-IPv4Mask Export-ModuleMember -Function Get-IPv4Subnet Export-ModuleMember -Function Test-ValidIPv4 Export-ModuleMember -Function Get-IPv4Address Export-ModuleMember -Function Get-IPv6Address Export-ModuleMember -Function Convert-IPv6Address Export-ModuleMember -Function Convert-IPv4Address Export-ModuleMember -Function Get-IPv6Subnet Export-ModuleMember -Function Convert-IPv4Mask Export-ModuleMember -Alias Convert-IPMask Export-ModuleMember -Alias Get-IPAddress Export-ModuleMember -Alias Test-ValidIP Export-ModuleMember -Alias Get-Mask Export-ModuleMember -Alias Convert-IP Export-ModuleMember -Alias Convert-IPAddress Export-ModuleMember -Alias Get-Subnet |