Validate and compare incoming parameters in powershell script - powershell

I'm just learning the basics of powershell and have a task - create pwsh script which accepts 3 incoming parameters (all are mandatory):
first parameter, value address_1, it's IP address with the format x.x.x.x
second parameter, value address_2, it's IP address with the format x.x.x.x
third parameter, value mask, value in the format x.x.x.x or xx (255.0.0.0 or 8)
This script should check address_1 and address_2 belong to the same network or not. Results in output console, yes or no. As I mentioned before incoming parameters not allow to accept not valid arguments, it should show error.
Can someone explain, how I can do that. I will be very grateful for your help.

As per my comment. This stuff already exists for years now, thus no need to try and write this from scratch, unless it's a homework assignment, or you are pushing yourself to learn to do it.
Search is your friend.
'powershell ipv4 address range to cidr'
The first hit in the results...
https://www.kittell.net/code/powershell-ipv4-range
...and the author's examples:
# IPv4 Range
function New-IPRange ($start, $end)
{
# created by Dr. Tobias Weltner, MVP PowerShell
$ip1 = ([System.Net.IPAddress]$start).GetAddressBytes()
[Array]::Reverse($ip1)
$ip1 = ([System.Net.IPAddress]($ip1 -join '.')).Address
$ip2 = ([System.Net.IPAddress]$end).GetAddressBytes()
[Array]::Reverse($ip2)
$ip2 = ([System.Net.IPAddress]($ip2 -join '.')).Address
for ($x=$ip1; $x -le $ip2; $x++)
{
$ip = ([System.Net.IPAddress]$x).GetAddressBytes()
[Array]::Reverse($ip)
$ip -join '.'
}
}
# IPv4 Range - Example
New-IPRange 192.168.10.10 192.168.10.20
# broadcast IPv4 address from a CIDR range
function Get-Broadcast ($addressAndCidr)
{
$addressAndCidr = $addressAndCidr.Split("/")
$addressInBin = (New-IPv4toBin $addressAndCidr[0]).ToCharArray()
for($i=0;$i -lt $addressInBin.length;$i++)
{
if($i -ge $addressAndCidr[1])
{
$addressInBin[$i] = "1"
}
}
[string[]]$addressInInt32 = #()
for ($i = 0;$i -lt $addressInBin.length;$i++)
{
$partAddressInBin += $addressInBin[$i]
if(($i+1)%8 -eq 0)
{
$partAddressInBin = $partAddressInBin -join ""
$addressInInt32 += [Convert]::ToInt32($partAddressInBin -join "",2)
$partAddressInBin = ""
}
}
$addressInInt32 = $addressInInt32 -join "."
return $addressInInt32
}
# IPv4 Broadcast - Example
Get-Broadcast 192.168.10.10/27
# detect if a specified IPv4 address is in the range
function Test-IPinIPRange ($Address,$Lower,$Mask)
{
[Char[]]$a = (New-IPv4toBin $Lower).ToCharArray()
if($mask -like "*.*")
{
[Char[]]$b = (New-IPv4toBin $Mask).ToCharArray()
}
else
{
[Int[]]$array = (1..32)
for($i=0;$i -lt $array.length;$i++)
{
if($array[$i] -gt $mask){$array[$i]="0"}else{$array[$i]="1"}
}
[string]$mask = $array -join ""
[Char[]]$b = $mask.ToCharArray()
}
[Char[]]$c = (New-IPv4toBin $Address).ToCharArray()
$res = $true
for($i=0;$i -le $a.length;$i++)
{
if($a[$i] -ne $c[$i] -and $b[$i] -ne "0")
{
$res = $false
}
}
return $res
}
# IPv4 In Range - Example
Write-Output "`r`nTest If IP In Range - 192.168.23.128/25"
Test-IPinIPRange "192.168.23.200" "192.168.23.12" "255.255.255.128"
Write-Output "`r`nTest If IP In Range - 192.168.23.127/24"
Test-IPinIPRange "192.168.23.127" "192.168.23.12" "24"
# convert an IPv4 address to a Bin
function New-IPv4toBin ($ipv4)
{
$BinNum = $ipv4 -split '\.' | ForEach-Object {[System.Convert]::ToString($_,2).PadLeft(8,'0')}
return $binNum -join ""
}
# IPv4 To Bin - Example
Write-Output "`r`nIP To Bin"
New-IPv4toBin 192.168.10.10
# convert a Bin to an IPv4 address
function New-IPv4fromBin($addressInBin)
{
[string[]]$addressInInt32 = #()
$addressInBin = $addressInBin.ToCharArray()
for ($i = 0;$i -lt $addressInBin.length;$i++)
{
$partAddressInBin += $addressInBin[$i]
if(($i+1)%8 -eq 0)
{
$partAddressInBin = $partAddressInBin -join ""
$addressInInt32 += [Convert]::ToInt32($partAddressInBin -join "",2)
$partAddressInBin = ""
}
}
$addressInInt32 = $addressInInt32 -join "."
return $addressInInt32
}
# IPv4 From Bin - Example
Write-Output "`r`nIP From Bin - 192.168.23.250"
New-IPv4fromBin "11000000101010000001011111111010"
Write-Output "`r`nIP From Bin - 192.168.10.10"
New-IPv4fromBin "11000000101010000000101000001010"
# CIDR To IPv4 Range - Example
Write-Output "`r`nIP CIDR to Range"
New-IPRange "192.168.23.120" (Get-Broadcast "192.168.23.120/25")
You of course can refactor the above with the Validate code already provided to you by the others.

Some of the documentation can seem over-whelming on frist read, so here's a working framework to study and get you started. The [ValidatePattern()] and [ValidateScript()] attributes validate IPv4 address format and valid value range and errors will be thrown if the conditions they specify aren't met.
Perform you domain comparision in the Process block and branch conditionally on the result. I leave that to you.
Function AddressTest
{
Param(
[Parameter(Mandatory,Position = 0)]
[ValidatePattern('^(\d{1,3}\.){3}\d{1,3}$')]
[ValidateScript({[Int[]](($_.Split('.')) -le 255).Count -eq 4})]
[String]$address_1,
[Parameter(Mandatory,Position = 1)]
[ValidatePattern('^(\d{1,3}\.){3}\d{1,3}$')]
[ValidateScript({[Int[]](($_.Split('.')) -le 255).Count -eq 4})]
[String]$address_2,
[Parameter(Mandatory,Position = 2)]
[ValidatePattern('^((\d{1,3}\.){3})?\d{1,3}$')]
[ValidateScript({(($_ -match '^\d+$') -and ([Int]$_ -le 255)) -or (($_.Split('.') -le 255).Count -eq 4)})]
[String]$Mask
)
Process
{
echo $address_1
echo $address_2
echo $mask
}
}
Read the documentation at the links others provided in the commnents while picking apart the code to understand how it works.

This is an example of how to use regex validation patterns to test if the two ip address and netmask are valid.
param (
[Parameter(Mandatory, Position=0)][string] $ip1,
[Parameter(Mandatory, Position=1)] [string] $ip2,
[Parameter(Mandatory, Position=2)] [string] $mask
)
# you can use [Parameter(Mandatory, Position=0)][IPAddress] $ip1 as input instead of string
# ipaddress param can accept partial ip's like 192.168 and will convert it to 192.0.0.168
# string with test would probably be better
function IsValidIPv4 ($ip) {
return ($ip -match '^(?:(?: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]?)$' -and [bool]($ip -as [ipaddress]))
}
# Validate IP's as actual IPv4
if (isValidIPv4 $ip1){
write-host "$($ip1) IS a valid IPv4 Address"
} else {
write-host "$($ip1) is not a valid IPv4 Address" -ForegroundColor Red
}
if (isValidIPv4 $ip2){
write-host "$($ip2) IS a valid IPv4 Address"
} else {
write-host "$($ip2) is not a valid IPv4 Address" -ForegroundColor Red
}
if (isValidIPv4 $mask){
write-host "$($mask) IS a valid IPv4 Address"
} else {
write-host "$($mask) is not a valid netmask" -ForegroundColor Red
}
Then check with the netmask that ip1 and ip2 are in the same network
Note :
As pointed out in my comments above
you can use [Parameter(Mandatory, Position=0)][IPAddress] $ip1 as input instead of string
ipaddress param can accept partial ip's like 192.168 and will convert it to 192.0.0.168 so this will cause incorrect validation - DON'T USE IT

I find a answer, script, with strange validation pattern:
param (
[parameter(Mandatory = $true, Position = 0)]
[Net.IPAddress]
$ip1,
[parameter(Mandatory = $true, Position = 1)]
[Net.IPAddress]
$ip2,
[parameter(Mandatory = $true, Position = 2)]
[alias("SubnetMask")]
[Net.IPAddress]
$mask
)
if (($ip1.address -band $mask.address) -eq ($ip2.address -band $mask.address)) { $true } else { $false }
Working correct:
./script.ps1 -ip1 192.168.228.110 -ip2 192.168.228.190 -mask 255.255.255.128
But when I use network prefix it always give a true.
./script.ps1 -ip1 192.168.228.110 -ip2 192.168.228.190 -mask 30
How I can modify script, to working with prefix?

Related

Powershell IP address range

I need to help with my code which is write in Powershell. Program should generate IP addresses in range. For example from 10.4.254.250 to 10.4.255.255.
When I have the same subnet (from 10.4.255.x to 10.4.255.x), all is correct. Problem starts when I have different subnet (from 10.4.254.250 to 10.4.255.255).
Output is invalid. Try it please. Thank you, for your help.
Correct output should be, that ip address which is 10.4.255.X starts from 1. Now starts from 250 to 255.
I need to get all ip addresses from variable $from to variable $to. When IP address in the same subnet $from = "10.4.255.1" $to = "10.4.255.1" all is correct. Problem starts, when different subnet $from = "10.4.254.250" $to = "10.4.255.255"
Look at my code bellow:
$from = "10.4.254.250"
$to = "10.4.255.255"
$Ip_Adresa_Od = $from -split "\."
$Ip_Adresa_Do = $to -split "\."
foreach ($Ip_Adresa_A in $Ip_Adresa_Od[0]..$Ip_Adresa_Do[0])
{
foreach ($Ip_Adresa_B in $Ip_Adresa_Od[1]..$Ip_Adresa_Do[1])
{
foreach ($Ip_Adresa_C in $Ip_Adresa_Od[2]..$Ip_Adresa_Do[2])
{
foreach ($Ip_Adresa_D in $Ip_Adresa_Od[3]..$Ip_Adresa_Do[3])
{
$Ip_Adresa_Pocitace = "$Ip_Adresa_A.$Ip_Adresa_B.$Ip_Adresa_C.$Ip_Adresa_D"
$Ip_Adresa_Pocitace
}
}
}
}
Wrong output is:
10.4.254.250
10.4.254.251
10.4.254.252
10.4.254.253
10.4.254.254
10.4.254.255
10.4.255.250
10.4.255.251
10.4.255.252
10.4.255.253
10.4.255.254
10.4.255.255
Working with IP addresses and ranges is complicated, and something I try to avoid if a program/software I am using does it already. Here are some functions that I wrote a while back that convert the addresses to decimal values, that are easier to manipulate. There are probably better, more precise solutions than this, but it will also return a range based off an address with a Subnet address or CIDR mask too. It should also cover the case #vonPryz mentioned where the addresses are across .24 CIDR ranges.
function Find-IPRange {
<#
.SYNOPSIS
Determines all the IP address in a given range or subnet.
.DESCRIPTION
This function can evaluate a set of addresses based of the following three options:
Range - What IP addresses are between this and that address
Mask - What are the IP addresses given a particular IP address and mask, i.e. 24, 25.
Subnet - What are the IP addresses given a particular IP address and subnet address, i.e 255.255.0.0, 255.255.255.192
You have to specify an IP address to use the subnet and mask options. For the range you have to specify two addresses.
.PARAMETER Start
Start address of an IP range
.PARAMETER End
End address of an IP range
.PARAMETER IP
Any valid ip address
.PARAMETER Subnet
A valid Subnet IP address i.e. 255.255.255.0, 255.255.0.0
.PARAMETER Mask
A valid net mask from 0 to 32
.EXAMPLE
Find-IPRange -IP 192.168.0.4 -mask 30
.EXAMPLE
Find-IPRange -Start 192.168.1.250 -End 192.168.2.5
.EXAMPLE
Find-IPRange -IP 10.100.100.10 -Subnet 255.255.255.240
#>
[CmdletBinding(DefaultParameterSetName = "Range")]
Param (
[Parameter(Mandatory = $true, ParameterSetName = "Range")]
[System.Net.IPAddress]
$Start,
[Parameter(Mandatory = $true, ParameterSetName = "Range")]
[System.Net.IPAddress]
$End,
[Parameter(Mandatory = $true, ParameterSetName = "Mask")]
[Parameter(Mandatory = $true, ParameterSetName = "Subnet")]
[System.Net.IPAddress]
$IP,
[Parameter(Mandatory = $true, ParameterSetName = "Subnet")]
[System.Net.IPAddress]
$Subnet,
[Parameter(Mandatory = $true, ParameterSetName = "Mask")]
[ValidateRange(0, 32)]
[System.Int32]
$Mask,
[Parameter(ParameterSetName = "Mask")]
[Parameter(ParameterSetName = "Subnet")]
[System.Management.Automation.SwitchParameter]
$ReturnRange
)
Begin {
# If the user specifies a mask, then convert it to a subnet ip address
if ($Mask) {
$Binary = ("1" * $Mask) + ("0" * (32 - $Mask))
$Decimal = [System.Convert]::ToInt64($Binary, 2)
[System.Net.IPAddress]$Subnet = ConvertFrom-IntToIP -Decimal $Decimal
}
}
Process {
# If we're looking at a subnet, we need to establish the start address and the broadcast address for it. We're using bitwise operators to do this.
if ($PSCmdlet.ParameterSetName -ne "Range") {
# Compare bits where both are a match using the bitwise AND operator
[System.Net.IPAddress]$SubnetAddr = $Subnet.Address -band $IP.Address
# Flip the subnet mask i.e. 0.0.0.255 for 255.255.255.0 by using the bitwise XOR operator and then compare against a bitwise OR operator
[System.Net.IPAddress]$Broadcast = ([System.Net.IPAddress]'255.255.255.255').Address -bxor $Subnet.Address -bor $SubnetAddr.Address
# Return the start and end of a subnet only if requested
if ($ReturnRange) { return $SubnetAddr, $Broadcast }
# Convert the start and end of the ranges to integers
$RangeStart = ConvertFrom-IPToInt -ip $SubnetAddr.IPAddressToString
$RangeEnd = ConvertFrom-IPToInt -ip $Broadcast.IPAddressToString
}
else {
$RangeStart = ConvertFrom-IPToInt -ip $Start.IPAddressToString
$RangeEnd = ConvertFrom-IPToInt -ip $End.IPAddressToString
}
# Loop through the points between the start and end of the ranges and convert them back to IP addresses
for ($Addr = $RangeStart; $Addr -le $RangeEnd; $Addr ++) { ConvertFrom-IntToIP -Decimal $Addr }
}
End {
}
}
function ConvertFrom-IPToInt {
<#
.SYNOPSIS
Converts an IP address to an Int64 value.
.DESCRIPTION
Converts an IP address to an Int64 value.
.PARAMETER IP
A valid IP address to be converted to an integer
.EXAMPLE
ConvertFrom-IPToInt -IP 192.168.0.1
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[System.Net.IPAddress]
$IP
)
Begin {
}
Process {
# Split the IP address in to octets
$Octets = $IP -split "\."
# Multiply the octets based on the maximum number of addresses each octet provides.
[System.Int64]$Decimal = ([System.Int32]$Octets[0] * [System.Math]::Pow(256, 3)) +
([System.Int32]$Octets[1] * [System.Math]::Pow(256, 2)) +
([System.Int32]$Octets[2] * 256) +
([System.Int32]$Octets[3])
}
End {
# Return the int64 value
$Decimal
}
}
function ConvertFrom-IntToIP {
<#
.SYNOPSIS
Converts an Int64 value to an IP address.
.DESCRIPTION
Converts an Int64 value to an IP address.
.PARAMETER Decimal
A decimal value for the IP Address to be converted
.EXAMPLE
ConvertFrom-IntToIP -Decimal 3232235521
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[System.Int64]
$Decimal
)
Begin {
# Initialise an array for the octets
$Octets = #()
}
Process {
# Work out first octet by dividing by the total number of addresses.
$Octets += [System.String]([System.Math]::Truncate($Decimal / [System.Math]::Pow(256, 3)))
# Work out second octet by the modulus of the first octets total number of addresses divided by the total number of address available for a class B subnet.
$Octets += [System.String]([System.Math]::Truncate(($Decimal % [System.Math]::Pow(256, 3)) / [System.Math]::Pow(256, 2)))
# Work out third octet by the modulus of the second octets total number of addresses divided by the total number of address available for a class C subnet.
$Octets += [System.String]([System.Math]::Truncate(($Decimal % [System.Math]::Pow(256, 2)) / 256))
# Work out fourth octet by the modulus of the third octets total number of addresses.
$Octets += [System.String]([System.Math]::Truncate($Decimal % 256))
# Join the strings to form the IP address
[System.Net.IPAddress]$IP = $Octets -join "."
}
End {
# Return the ip address object
$IP.IPAddressToString
}
}
DISCLAIMER: I am not a network engineer so please feel free to suggest any changes to how the addresses are converted to ints and back. This function also hasn't been through any unit testing, so there may be cases that exist where it does not work.
Example Output:
Find-IPRange -Start 10.4.254.250 -End 10.4.255.255
10.4.254.250
10.4.254.251
10.4.254.252
10.4.254.253
10.4.254.254
10.4.254.255
10.4.255.0
10.4.255.1
10.4.255.2
...truncated
10.4.255.249
10.4.255.250
10.4.255.251
10.4.255.252
10.4.255.253
10.4.255.254
10.4.255.255
Other uses:
Find-IPRange -IP 192.168.0.4 -Mask 28
192.168.0.0
192.168.0.1
192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.5
192.168.0.6
192.168.0.7
192.168.0.8
192.168.0.9
192.168.0.10
192.168.0.11
192.168.0.12
192.168.0.13
192.168.0.14
192.168.0.15
Find-IPRange -IP 192.168.0.4 -Subnet 255.255.255.252
192.168.0.4
192.168.0.5
192.168.0.6
192.168.0.7
You have to convert your IP address to an integer and then in each iteration of a for loop convert the integer to a byte array:
$from = "10.4.254.250"
$to = "10.4.255.255"
$Ip_Adresa_Od = $from -split "\."
$Ip_Adresa_Do = $to -split "\."
#change endianness
[array]::Reverse($Ip_Adresa_Od)
[array]::Reverse($Ip_Adresa_Do)
#convert octets to integer
$start=[bitconverter]::ToUInt32([byte[]]$Ip_Adresa_Od,0)
$end=[bitconverter]::ToUInt32([byte[]]$Ip_Adresa_Do,0)
for ($ip=$start; $ip -lt $end; $ip++)
{
#convert integer back to byte array
$get_ip=[bitconverter]::getbytes($ip)
#change endianness
[array]::Reverse($get_ip)
$new_ip=$get_ip -join "."
$new_ip
}
I hope I understand your question. I believe you would like to restart the counter on the 4th octet back to 1 once the 3rd octet iterates from 254 to 255? There's probably a better way to do this but for now hopefully this works. I've added an if statement that resets the range once the final 10.4.254.255 ip is reached. This will allow your loop to include the 10.4.255.x range starting from 1 in the 4th octet until 255 is reached. The while loop condition will be set to false once the final 10.4.255.255 IP is reached and exit.
I hope this helps and provides the desired result.
$from = "10.4.254.250"
$to = "10.4.254.255"
$Ip_Adresa_Od = $from -split "\."
$Ip_Adresa_Do = $to -split "\."
$run = "true";
while($run -eq "true")
{
if($Ip_Adresa_Pocitace -eq "10.4.254.255")
{
$from = "10.4.255.1"
$to = "10.4.255.255"
$Ip_Adresa_Od = $from -split "\."
$Ip_Adresa_Do = $to -split "\."
}
foreach ($Ip_Adresa_C in $Ip_Adresa_Od[2]..$Ip_Adresa_Do[2])
{
foreach ($Ip_Adresa_D in $Ip_Adresa_Od[3]..$Ip_Adresa_Do[3])
{
$Ip_Adresa_Pocitace = "10.4.$Ip_Adresa_C.$Ip_Adresa_D"
$Ip_Adresa_Pocitace
if($Ip_Adresa_Pocitace -eq "10.4.255.255")
{
$run = "false";
}
}
}
}
Results:
10.4.254.250
10.4.254.251
10.4.254.252
10.4.254.253
10.4.254.254
10.4.254.255
10.4.255.1
10.4.255.2
10.4.255.3
...
10.4.255.249
10.4.255.250
10.4.255.251
10.4.255.252
10.4.255.253
10.4.255.254
10.4.255.255
Solution w/ new parameters.
# Orininal Parameters
# $from = "10.4.254.250"
# $to = "10.4.254.255"
$from = "10.4.253.250"
$to = "10.4.253.255"
$Ip_Adresa_Od = $from -split "\."
$Ip_Adresa_Do = $to -split "\."
$run = "true";
while($run -eq "true")
{
if($Ip_Adresa_Pocitace -eq "10.4.253.255")
{
# Orininal Parameters
# $from = "10.4.255.1"
# $to = "10.4.255.255"
$from = "10.4.254.1"
$to = "10.4.254.255"
$end = $to
$Ip_Adresa_Od = $from -split "\."
$Ip_Adresa_Do = $to -split "\."
}
foreach ($Ip_Adresa_C in $Ip_Adresa_Od[2]..$Ip_Adresa_Do[2])
{
foreach ($Ip_Adresa_D in $Ip_Adresa_Od[3]..$Ip_Adresa_Do[3])
{
$Ip_Adresa_Pocitace = "10.4.$Ip_Adresa_C.$Ip_Adresa_D"
$Ip_Adresa_Pocitace
if($Ip_Adresa_Pocitace -eq $end)
{
$run = "false";
}
}
}
}
Results:
10.4.253.250
10.4.253.251
10.4.253.252
10.4.253.253
10.4.253.254
10.4.253.255
10.4.254.1
10.4.254.2
10.4.254.3
...
10.4.254.253
10.4.254.254
10.4.254.255

Powershell - only if statement executes despite input

I'm trying to start my Powershell portfolio with and easy script that calculates Network ID or BroadcastID depending on input from the user. That being said, I can only get my if statement to run. I am unsure what I am missing here. I have tried searching this as I know I cannot be the only one to have this issue, but I am unable to find it. Any education on my error would be appreciated as it seems like a basic flaw.
Thanks!
#prompt for IP and subnet mask
$ip = Read-Host -Prompt "Enter your IP address"
$mask = Read-Host - Prompt "Enter your subnet mask"
[String]$UserDecision = Read-Host -Prompt "Enter N if you would like to calculate your Network ID or B if you would like to calculate your Broadcast address."
$splitmask=$mask.split(".")
$wildcard="$(255 - $splitmask[0]).$(255 - $splitmask[1]).$(255 - $splitmask[2]).$(255 - $splitmask[3])"
# ip and mask variable to ip addresses
$ip = [ipaddress] $ip
$mask = [ipaddress] $mask
#determine networkID
function CalculateNetID {
$networkID = [ipaddress] ($ip.Address -band $mask.Address)
#print NetworkID to console
echo "The Network id is $($networkID.IPAddressToString)"
}
function CalculateBroadcastID {
$networkID = [ipaddress] ($ip.Address -band $mask.Address)
#convert wildcard to IP addresses
$wildcard= [ipaddress] $wildcard
$broadcast = [ipaddress] $($wildcard.Address -bor $NetworkID.Address)
#print broadcast to console
echo "The Broadcast id is $broadcast"
}
if ($UserDecision -eq "N" -or "n"){
CalculateNetID
}
elseif($UserDecision -eq "B" -or "b"){
CalculateBroadcastID
}
else{
echo "Please retry and enter the character associated with the ID you would like to calculate"
}
Here is your problem:
$false -or 'n' # => True
As you can see, this is what's happening in your first if statement, the correct syntax for your condition should be:
if($UserDecision -eq "N" -or $UserDecision -eq "n") {
Same thing applies for the elseif.
Note that, PowerShell comparison operators are not case-sensitive by default, so the -or conditions could be just removed:
if($UserDecision -eq "n") {
You might also want to consider using a switch statement instead of chained if, elseif, else:
switch($UserDecision) {
N { CalculateNetID }
B { CalculateBroadcastID }
Default {
"Please retry and enter the character associated with the ID you would like to calculate"
}
}

powershell: if loop, while the first position of arraylist in in process

I have an arraylist, in which I am going to save some values. I am doing a foreach loop and if a value is going to be saved at the first position of the ArrayList, I want to output a "First position" line and otherwise, nothing should be done.
The if block that I wrote below, doesn't work.
<# Don't consider this code from here
[System.Collections.ArrayList]$alist = #()
$al ="keeranpc01,www.google.ch,192.168.0.25"
$al = $al.Split(",")
foreach($h in $al){
# Abstand vor dem Hostnamen löschen
if($h.contains(' ')) {
$h = $h -replace '\s', ''
}
$alist.Add($h)
}
to here
#>
#### Start here
[System.Collections.ArrayList]$rear = #()
foreach($ha in $alist) {
$PingConnetion = Test-Connection $ha -Count 1 -ErrorAction SilentlyContinue
$pcre = $ha
$pire = if($PingConnetion.ResponseTime -ne $null) {Write-output 'Erreichbar'}else{Write-Output 'Nicht Erreichbar'}
$zure = $PingConnetion.ResponseTime
$zeit = Get-Date -Format HH:mm:ss
if($alist[$_] -eq $alist[0]) {Write-Host 'First position'}
[void]$rear.Add([PSCustomObject]#{Zeit = $zeit; Host = $pcre; IPv4 = $PingConnetion.IPV4Address.IPAddressToString; Ping = $pire; Zugriffszeit = $zure; })
}
how should I write the if statement so that it is possible? I expect if-statement to work, Only when the first position of ArrayList is in process
thx
What you are tying to do does work except you are tying to compare element zero of your alist to a ordinal position of your alist which is invalid. You would need to compare the following:
if($ha -eq $alist[0]) {Write-Host 'First position'}
Below is a worked example that might be clearer.
$input = 1..10
foreach($x in $input){
if($input[0] -eq $x){
write-host "First Position"
}
$x
}

How can I increase the maximum number of characters read by Read-Host?

I need to get a very long string input (around 9,000 characters), but Read-Host will truncate after around 8,000 characters. How can I extend this limit?
The following are possible workarounds.
Workaround 1 has the advantage that it will work with PowerShell background jobs that require keyboard input. Note that if you are trying to paste clipboard content containing new lines, Read-HostLine will only read the first line, but Read-Host has this same behavior.
Workaround 1:
<#
.SYNOPSIS
Read a line of input from the host.
.DESCRIPTION
Read a line of input from the host.
.EXAMPLE
$s = Read-HostLine -prompt "Enter something"
.NOTES
Read-Host has a limitation of 1022 characters.
This approach is safe to use with background jobs that require input.
If pasting content with embedded newlines, only the first line will be read.
A downside to the ReadKey approach is that it is not possible to easily edit the input string before pressing Enter as with Read-Host.
#>
function Read-HostLine ($prompt = $null) {
if ($prompt) {
"${prompt}: " | Write-Host
}
$str = ""
while ($true) {
$key = $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown");
# Paste the clipboard on CTRL-V
if (($key.VirtualKeyCode -eq 0x56) -and # 0x56 is V
(([int]$key.ControlKeyState -band [System.Management.Automation.Host.ControlKeyStates]::LeftCtrlPressed) -or
([int]$key.ControlKeyState -band [System.Management.Automation.Host.ControlKeyStates]::RightCtrlPressed))) {
$clipboard = Get-Clipboard
$str += $clipboard
Write-Host $clipboard -NoNewline
continue
}
elseif ($key.VirtualKeyCode -eq 0x08) { # 0x08 is Backspace
if ($str.Length -gt 0) {
$str = $str.Substring(0, $str.Length - 1)
Write-Host "`b `b" -NoNewline
}
}
elseif ($key.VirtualKeyCode -eq 13) { # 13 is Enter
Write-Host
break
}
elseif ($key.Character -ne 0) {
$str += $key.Character
Write-Host $key.Character -NoNewline
}
}
return $str
}
Workaround 2:
$maxLength = 65536
[System.Console]::SetIn([System.IO.StreamReader]::new([System.Console]::OpenStandardInput($maxLength), [System.Console]::InputEncoding, $false, $maxLength))
$s = [System.Console]::ReadLine()
Workaround 3:
function Read-Line($maxLength = 65536) {
$str = ""
$inputStream = [System.Console]::OpenStandardInput($maxLength);
$bytes = [byte[]]::new($maxLength);
while ($true) {
$len = $inputStream.Read($bytes, 0, $maxLength);
$str += [string]::new($bytes, 0, $len)
if ($str.EndsWith("`r`n")) {
$str = $str.Substring(0, $str.Length - 2)
return $str
}
}
}
$s = Read-Line
More discussion here:
Console.ReadLine() max length?
Why does Console.Readline() have a limit on the length of text it allows?
https://github.com/PowerShell/PowerShell/issues/16555

Checking for a range in Powershell

I'm trying to write a script that will get an IP address of a computer and check to see whether it falls in a specific range of IPs. So for example, if the IP of the machine is 192.168.0.5, the script will check to see if it falls between the range 192.168.0.10 to 192.168.0.20. So far, my script is only able to get IPs of remote machines, but I just can't figure out how I would check if the IP is in a specific range. I'd appreciate any suggestions. Thank you.
It might be easiest to let .NET do the work - there's an IPAddress class that can parse them to their numeric values for comparison. Here's a function that you can drop into your profile (or add to a module, or however you prefer to add PS functions):
function IsIpAddressInRange {
param(
[string] $ipAddress,
[string] $fromAddress,
[string] $toAddress
)
$ip = [system.net.ipaddress]::Parse($ipAddress).GetAddressBytes()
[array]::Reverse($ip)
$ip = [system.BitConverter]::ToUInt32($ip, 0)
$from = [system.net.ipaddress]::Parse($fromAddress).GetAddressBytes()
[array]::Reverse($from)
$from = [system.BitConverter]::ToUInt32($from, 0)
$to = [system.net.ipaddress]::Parse($toAddress).GetAddressBytes()
[array]::Reverse($to)
$to = [system.BitConverter]::ToUInt32($to, 0)
$from -le $ip -and $ip -le $to
}
Usage looks like:
PS> IsIpAddressInRange "192.168.0.5" "192.168.0.10" "192.168.0.20"
False
PS> IsIpAddressInRange "192.168.0.15" "192.168.0.10" "192.168.0.20"
True
i write a little function to do this:
function Test-IpAddressInRange {
[CmdletBinding()]
param (
[Parameter(Position = 0, Mandatory = $true)][ipaddress]$from,
[Parameter(Position = 1, Mandatory = $true)][ipaddress]$to,
[Parameter(Position = 2, Mandatory = $true)][ipaddress]$target
)
$f=$from.GetAddressBytes()|%{"{0:000}" -f $_} | & {$ofs='-';"$input"}
$t=$to.GetAddressBytes()|%{"{0:000}" -f $_} | & {$ofs='-';"$input"}
$tg=$target.GetAddressBytes()|%{"{0:000}" -f $_} | & {$ofs='-';"$input"}
return ($f -le $tg) -and ($t -ge $tg)
}
test result:
PS C:\> Test-IpAddressInRange "192.168.0.1" "192.168.0.100" "192.168.0.1"
True
PS C:\> Test-IpAddressInRange "192.168.0.1" "192.168.0.100" "192.168.0.100"
True
PS C:\> Test-IpAddressInRange "192.168.90.1" "192.168.100.100" "192.168.101.101"
False
PS C:\>
It's easy if mask is 255.255.255.0
in this case you can do something like this:
$a = [ipaddress]"192.168.0.5"
10..20 -contains $a.IPAddressToString.split('.')[3]
true
For different submask you have to check each ip's octect.
If you're into the whole brevity thing, here is a functional hybrid of AndyHerb's comment and E.Z. Hart's answer:
function IsIpAddressInRange {
param(
[System.Version] $IPAddress,
[System.Version] $FromAddress,
[System.Version] $ToAddress
)
$FromAddress -le $IPAddress -and $IPAddress -le $ToAddress
}
Examples:
IsIpAddressInRange "192.168.1.50" "192.168.1.30" "192.168.1.100"
True
IsIpAddressInRange "192.168.25.75" "192.168.25.0" "192.168.25.255"
True
IsIpAddressInRange "192.168.36.240" "192.168.36.0" "192.168.36.100"
False
IsIpAddressInRange "192.168.36.240" "192.168.33.0" "192.168.37.0"
True
For seeing if an IP is in range 192.168.1.10 to 192.168.1.20, for example, you can use regex and the -match operator
$ip -match "192\.168\.1\.0?(1\d)|20"
The 0? is to allow a leading 0.
Similarly, for any range, you can use a regex.
For very simple range, use string split on . and operate on the components.
Came across this one when googling, rather high hit. Just wanted to say that at least in powershell 4 and 5 it's a lot easier:
$range = "10.10.140.0-10.11.15.0"
$ipStart,$ipEnd = $range.Split("-")
$ipCheck = "10.10.250.255"
($ipCheck -ge $ipStart) -AND ($ipCheck -le $ipEnd)
This is not working, if you give an IP 192.168.25.75 within range 192.168.25.0-192.168.25.255.