Checking for a range in Powershell - 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.

Related

Problems with interconnected parameters in PowerShell Param block

I'm having troubles wrapping my head around the code logic needed to get my Param block functioning as I want in PowerShell 5.1. A very simplified (but usable for my request) script with the Param block is here and this works exactly as I want:
[CmdletBinding(DefaultParameterSetName = 'All')]
Param (
[Parameter(Position=0,Mandatory=$True)]
[String]$Name,
[Parameter(ParameterSetName = 'All')]
[Switch]$All,
[Parameter(ParameterSetName = 'Individual')]
[Switch]$P1,
[Parameter(ParameterSetName = 'Individual')]
[Switch]$P2
)
If ($All -Or ($P1.ToBool() + $P2.ToBool() -eq 0)) {
$All = $True
$P1 = $True
$P2 = $True
}
"`$Name is $Name"
"`$All is $All"
"`$P1 is $P1"
"`$P2 is $P2"
The auto-completion of parameters when running this script above works as intended. If I use the "-All" switch, then -P# are not available. If I use -P# switche, -All is not available. If I omit the -Name, then it prompts me to put in a name. If I use the -All switch, I want all of the individual -P# options to later be set to $True. If I use no switches at all, it prompts me for a Name then sets all options to $True.
The first problem is that when using DefaultParameterSetName = 'All' (which I had to do in order to make the script work without any switches on the command line), then the $All variable is NOT actually being set to $True when it is not present on the command line. I had to make the "If" block in order to overcome that behavior. This makes the next problem come up because the actual script I'm trying to use this in will have fifteen or more -P# switches. That will make the "If" test more complex and ugly.
Is there a better way I can do this? Maybe something in the layout of my Parameter Sets? I could even eliminate the "-All" switch entirely if there's an easier way to evaluate that none of the -P# switches are used. Is there an easier way to add up the boolean value of all Parameters named P#? I've stumbled across the $MyInvocation variable and $MyInvocation.MyCommand.Parameters seems promising but I'm not sure exactly how to process that either.
Update after answer found:
Here is my new, simplified working code sample which I arrived at thanks to all the suggestions here. The "-All" Switch was unnecessary. I decided to go with the Get-Variable method here for now due to its simplicity and scalability, but it does require a common prefix on the Switch variables. The If() block will remain the same no matter how many variables are used.
Param (
[Parameter(Position=0,Mandatory=$True)]
[String]$Name,
[Alias('Blue')]
[Switch]$OptionBlue,
[Alias('Red')]
[Switch]$OptionRed,
[Alias('Yellow')]
[Switch]$OptionYellow
)
$AllOptions = Get-Variable -Name 'Option*'
If (-Not $AllOptions.Value.Contains($True)) {
"None of the Option Switches were used, setting all Option Variables to $True"
ForEach ($Option In $AllOptions) {$Option.Value = $True}
}
"`$Name: $Name"
"`$OptionBlue: $OptionBlue"
"`$OptionRed: $OptionRed"
"`$OptionYellow: $OptionYellow"
and here's how it works:
PS C:\Scripts\PowerShell> .\Test-Params.ps1
cmdlet Test-Params.ps1 at command pipeline position 1
Supply values for the following parameters:
Name: Testing
None of the Option Switches were used, setting all Option Variables to True
$Name: Testing
$OptionBlue: True
$OptionRed: True
$OptionYellow: True
PS C:\Scripts\PowerShell> .\Test-Params.ps1 -Name Testing -OptionRed
$Name: Testing
$OptionBlue: False
$OptionRed: True
$OptionYellow: False
PS C:\Scripts\PowerShell> .\Test-Params.ps1 -Name Testing -Blue -Yellow
$Name: Testing
$OptionBlue: True
$OptionRed: False
$OptionYellow: True
PS C:\Scripts\PowerShell> .\Test-Params.ps1 -Yellow -OptionRed
cmdlet Test-Params.ps1 at command pipeline position 1
Supply values for the following parameters:
Name: Testing
$Name: Testing
$OptionBlue: False
$OptionRed: True
$OptionYellow: True
Final Update
I figured out the way to avoid any issue where the script's switch parameters might match an already existing variable in the scope. Pulling the script's parameters that match the proper prefix/suffix used in the script's parameter names using $MyInvocation and then passing those specific names to Get-Variable avoids the issue. The switches also work correctly both when they are not present, or if they are explicitly set to $False. If more switches are needed, simply add them in the Param block with the proper prefix/suffix in the name and an alias for the simpler version. I think this bit of the code is bulletproof now...
Param (
[Parameter(Position=0,Mandatory=$True)]
[String]$Name,
[Alias('Blue')]
[Switch]$OptionBlue,
[Alias('Red')]
[Switch]$OptionRed,
[Alias('Yellow')]
[Switch]$OptionYellow
)
$OptionNotAnOption = $True
$OptionSwitches = ForEach ($Option In ($MyInvocation.MyCommand.Parameters.Keys | Where {$_ -Like "Option*"})) {Get-Variable $Option}
If ($OptionSwitches.Value.IsPresent -NotContains $True) {
"All options are `$False or not present: Enabling all options"
ForEach ($Switch In $OptionSwitches) {
Get-Variable -Name ($Switch.Name) | Set-Variable -Value $True
}
}
"`$Name: $Name"
"`$OptionBlue: $OptionBlue"
"`$OptionRed: $OptionRed"
"`$OptionYellow: $OptionYellow"
"`$OptionNotAnOption: $OptionNotAnOption"
You can use the $PSBoundParameters "automatic variable" to access the parameters specified in the call to the script / function and alter the function's behaviour accordingly.
For example:
function Invoke-MyFunction
{
param
(
[string] $Name,
[switch] $P1,
[switch] $P2
)
# how many $Pn parameters were specified in the call to the function?
$count = #( $PSBoundParameters.GetEnumerator()
| where-object { $_.Key.StartsWith("P") }
).Length;
# if *none* specified then enable *all*
$all = $count -eq 0;
if( $all )
{
$P1 = $true;
$P2 = $true;
}
"`$Name is $Name"
"`$all is $all"
"`$P1 is $P1"
"`$P2 is $P2"
}
And some tests:
PS> Invoke-MyFunction
$Name is
$all is True
$P1 is True
$P2 is True
PS> Invoke-MyFunction -Name "aaa"
$Name is aaa
$all is True
$P1 is True
$P2 is True
PS> Invoke-MyFunction -Name "aaa" -P1
$Name is aaa
$all is False
$P1 is True
$P2 is False
PS> Invoke-MyFunction -Name "aaa" -P2
$Name is aaa
$all is False
$P1 is False
$P2 is True
PS> Invoke-MyFunction -Name "aaa" -P1 -P2
$Name is aaa
$all is False
$P1 is True
$P2 is True
but watch out because the way I've evaluated $count means that specifying -P1:$false or -P2:$false makes $all = $false:
PS> Invoke-MyFunction -Name "aaa" -P1:$false
$Name is aaa
$all is False
$P1 is False
$P2 is False
so you might need to refine the expression to suit whatever you want to happen in this edge case...
Update
If your parameter names don't follow a simple pattern you can do something like this instead:
$names = #( "SomeParam", "AnotherParam", "Param3" );
$count = #( $PSBoundParameters.GetEnumerator()
| where-object { $_.Key -in $names }
).Length;
Assuming those are all switches and they belong to the parameter set of 'Individual', you can set the unset parameters to $true if $All is specified like so:
if ($All.IsPresent)
{
(Get-Command -Name $MyInvocation.MyCommand.Name).Parameters.Values |
Where-Object -FilterScript { $_.ParameterSets.Keys -eq 'Individual' } |
Foreach-Object -Process {
Set-Variable -Name $_.Name -Value $true -PassThru # remove -PassThru to silence the output
}
}
Referencing the current executing command with $MyInvocation.MyCommand.Name, you can pass it to Get-Command for more detailed info. on its parameters. This will allow you to filter by ParameterSets grabbing just the ones falling under Individual. Finally, it will pass it to Set-Variable setting all the switches to $true in a dynamic sense.
Note: I typed this up on my phone so there may be some typos that should be easy to correct.

Validate and compare incoming parameters in powershell script

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?

Looking for a regex can that pull the list of all the cmdlets used inside a Powershell script

I am doing something like this:
$ScriptContent = get-content "C:\Users\admin\Desktop\Scripts\SFB.ps1"
$ScriptContent -match '(?<N>.*)-(?<V>.*)'
Using regex to identify command invocations in PowerShell is a lost cause, consider just hooking straight into the parser engine instead (see inline comments):
$scriptPath = "C:\Users\admin\Desktop\Scripts\SFB.ps1"
# Parse the script
$AST = [System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path $scriptPath).ProviderPath, [ref]$null, [ref]$null)
# Now we use the AST to find all command invocations
#AST.FindAll({
$args[0] -is [System.Management.Automation.Language.CommandAst]
}, $true) |ForEach-Object {
# The first element is always the name
$command = $_.CommandElements[0]
# if the command name is a constant string, resolve it's value
if($command -is [System.Management.Automation.Language.StringConstantExpressionAst]){
$commandName = $command.Value
$known = $true
} else {
# Otherwise, note that the name can only be known at runtime
$commandName = "'{0}'" -f $command.Extent
$known = $false
}
# output new object with the information
[pscustomobject]#{
Name = $commandName
Constant = $known
File = $scriptPath
Line = $command.Extent.StartLineNumber
}
}
If your script looks like this:
Get-Process
& "Get-Service"
$a = "Get-Item"
. $a 'C:\Windows'
You should expect the following output:
Name Constant File Line
---- -------- ---- ----
Get-Process True C:\Users\admin\Desktop\Scripts\SFB.ps1 1
Get-Service True C:\Users\admin\Desktop\Scripts\SFB.ps1 3
'$a' False C:\Users\admin\Desktop\Scripts\SFB.ps1 7

PowerShell - Password Generator - How to always include number in string?

I have the following PowerShell script that creates a random string of 15 digits, for use as an Active Directory password.
The trouble is, this works great most of the time, but on some occasions it doesn't use a number or symbol. I just get 15 letters. This is then not usable as an Active Directory password, as it must have at least one number or symbol in it.
$punc = 46..46
$digits = 48..57
$letters = 65..90 + 97..122
$YouShallNotPass = get-random -count 15 `
-input ($punc + $digits + $letters) |
% -begin { $aa = $null } `
-process {$aa += [char]$_} `
-end {$aa}
Write-Host "Password is $YouShallNotPass"
How would I amend the script to always have at least one random number or symbol in it?
Thank you.
You could invoke the Get-Random cmdlet three times, each time with a different input parameter (punc, digit and letters), concat the result strings and shuffle them using another Get-Random invoke:
(Get-Random -Count 15 -InputObject ([char[]]$yourPassword)) -join ''
However, why do you want to reinvent the wheel? Consider using the following GeneratePassword function:
[Reflection.Assembly]::LoadWithPartialName("System.Web")
[System.Web.Security.Membership]::GeneratePassword(15,2)
And to ensure, it contains at least one random number (you already specify the number of symbols):
do {
$pwd = [System.Web.Security.Membership]::GeneratePassword(15,2)
} until ($pwd -match '\d')
As suggested by jisaak, there is no 100% guaranty that the Membership.GeneratePassword Method generates a password that meets the AD complexity requirements.
That's why I reinvented the wheel:
Function Create-String([Int]$Size = 8, [Char[]]$CharSets = "ULNS", [Char[]]$Exclude) {
$Chars = #(); $TokenSet = #()
If (!$TokenSets) {$Global:TokenSets = #{
U = [Char[]]'ABCDEFGHIJKLMNOPQRSTUVWXYZ' #Upper case
L = [Char[]]'abcdefghijklmnopqrstuvwxyz' #Lower case
N = [Char[]]'0123456789' #Numerals
S = [Char[]]'!"#$%&''()*+,-./:;<=>?#[\]^_`{|}~' #Symbols
}}
$CharSets | ForEach {
$Tokens = $TokenSets."$_" | ForEach {If ($Exclude -cNotContains $_) {$_}}
If ($Tokens) {
$TokensSet += $Tokens
If ($_ -cle [Char]"Z") {$Chars += $Tokens | Get-Random} #Character sets defined in upper case are mandatory
}
}
While ($Chars.Count -lt $Size) {$Chars += $TokensSet | Get-Random}
($Chars | Sort-Object {Get-Random}) -Join "" #Mix the (mandatory) characters and output string
}; Set-Alias Create-Password Create-String -Description "Generate a random string (password)"
Usage:
The Size parameter defines the length of the password.
The CharSets parameter defines the complexity where the character U,
L, N and S stands for Uppercase, Lowercase, Numerals and Symbols.
If supplied in lowercase (u, l, n or s) the returned string
might contain any of character in the concerned character set, If
supplied in uppercase (U, L, N or S) the returned string will
contain at least one of the characters in the concerned character
set.
The Exclude parameter lets you exclude specific characters that might e.g.
lead to confusion like an alphanumeric O and a numeric 0 (zero).
Examples:
To create a password with a length of 8 characters that might contain any uppercase characters, lowercase characters and numbers:
Create-Password 8 uln
To create a password with a length of 12 characters that that contains at least one uppercase character, one lowercase character, one number and one symbol and does not contain the characters OLIoli01:
Create-Password 12 ULNS "OLIoli01"
For the latest New-Password version: use:
Install-Script -Name PowerSnippets.New-Password
Command to Generate Random passwords by using existing funciton:
[system.web.security.membership]::GeneratePassword(x,y)
x = Length of the password
y = Complexity
General Error:
Unable to find type [system.web.security.membership]. Make sure that the assembly that contains this type is loaded.
Solution:
Run the below command:
Add-Type -AssemblyName System.web;
Another solution:
function New-Password() {
param(
[int] $Length = 10,
[bool] $Upper = $true,
[bool] $Lower = $true,
[bool] $Numeric = $true,
[string] $Special
)
$upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
$lowerChars = "abcdefghijklmnopqrstuvwxyz"
$numericChars = "0123456789"
$all = ""
if ($Upper) { $all = "$all$upperChars" }
if ($Lower) { $all = "$all$lowerChars" }
if ($Numeric) { $all = "$all$numericChars" }
if ($Special -and ($special.Length -gt 0)) { $all = "$all$Special" }
$password = ""
for ($i = 0; $i -lt $Length; $i++) {
Write-Host "password: [$password]"
$password = $password + $all[$(Get-Random -Minimum 0 -Maximum $all.Length)]
}
$valid = $true
if ($Upper -and ($password.IndexOfAny($upperChars.ToCharArray()) -eq -1)) { $valid = $false }
if ($Lower -and ($password.IndexOfAny($lowerChars.ToCharArray()) -eq -1)) { $valid = $false }
if ($Numeric -and ($password.IndexOfAny($numericChars.ToCharArray()) -eq -1)) { $valid = $false }
if ($Special -and $Special.Length -gt 1 -and ($password.IndexOfAny($Special.ToCharArray()) -eq -1)) { $valid = $false }
if (-not $valid) {
$password = New-Password `
-Length $Length `
-Upper $Upper `
-Lower $Lower `
-Numeric $Numeric `
-Special $Special
}
return $password
}
Flexible enough to set length, turn on/of upper, lower, and numeric, and set the list of specials.
My take on generating passwords in PowerShell, based on what I've found here and in the Internets:
#Requires -Version 4.0
[CmdletBinding(PositionalBinding=$false)]
param (
[Parameter(
Mandatory = $false,
HelpMessage = "Minimum password length"
)]
[ValidateRange(1,[int]::MaxValue)]
[int]$MinimumLength = 24,
[Parameter(
Mandatory = $false,
HelpMessage = "Maximum password length"
)]
[ValidateRange(1,[int]::MaxValue)]
[int]$MaximumLength = 42,
[Parameter(
Mandatory = $false,
HelpMessage = "Characters which can be used in the password"
)]
[ValidateNotNullOrEmpty()]
[string]$Characters = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM##%*-_+:,.'
)
(1..(Get-Random -Minimum $MinimumLength -Maximum $MaximumLength) `
| %{ `
$Characters.GetEnumerator() | Get-Random `
}) -join ''
I preferred this over using System.Web, not to introduce dependencies, which could change with .Net / .Net Core versions.
My variation also allows random password length (in specified range), is fairly concise (apart from the parameters section, which is quite verbose, to enforce some validations and provide defaults) and allows character repetitions (as opposite to the code in the question, which never repeats the same character).
I understand, that this does not guarantee a digit in the password. This however can be addressed in different ways. E.g. as was suggested, to repeat the generation until the password matches the requirements (contains a digit). My take would be:
Generate a random password.
If it does not contain a digit (or always):
Use a random function to get 1 random digit.
Add it to the random password.
Randomize the order of the result (so the digit is not necessarily always at the end).
Assuming, that the above script would be named "Get-RandomPassword.ps1", it could look like this:
$pass = .\Get-RandomPassword.ps1
$pass += (0..9 | Get-Random)
$pass = (($pass.GetEnumerator() | Get-Random -Count $pass.Length) -join '')
Write-Output $pass
This can be generalized, to enforce using any character category:
$sets = #('abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123456789', '()-_=+[{]};:''",<.>/?`~')
$pass = .\Get-RandomPassword.ps1 -Characters ($sets -join '')
foreach ($set in $sets) {
$pass += ($set.GetEnumerator() | Get-Random)
}
$pass = (($pass.GetEnumerator() | Get-Random -Count $pass.Length) -join '')
Write-Output $pass
I wrote a secure password generator function in PowerShell, maybe this will be useful to someone.
Similar to the accepted answer, this script also uses Get-Random (twice), and also regular expression matching to ensure the output is secure.
The difference in this script is that the password length can also be randomised.
(To hard set a password length, just set the MinimumPasswordLength and MaximumPasswordLength values to the the same length.)
It also allows an easy to edit character set, and also has a regex to ensure a decent password has been generated with all of the following characteristics:
(?=.*\d) must contain at least one numerical character
(?=.*[a-z]) must contain at least one lowercase character
(?=.*[A-Z]) must contain at least one uppercase character
(?=.*\W) must contain at least one non-word character
The answer to your question about always including a number in your generated output can be solved by checking the output with a regex match (just use the parts of the regex that you need, based on the explanations above), the example here checks for uppercase, lowercase, and numerical:
$Regex = "(?=.*\d)(?=.*[a-z])(?=.*[A-Z])"
do {
$Password = ([string]($AllowedPasswordCharacters |
Get-Random -Count $PasswordLength) -replace ' ')
} until ($Password -cmatch $Regex)
$Password
Here is the full script:
Function GeneratePassword
{
cls
$MinimumPasswordLength = 12
$MaximumPasswordLength = 16
$PasswordLength = Get-Random -InputObject ($MinimumPasswordLength..$MaximumPasswordLength)
$AllowedPasswordCharacters = [char[]]'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!?##£$%^&'
$Regex = "(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W)"
do {
$Password = ([string]($AllowedPasswordCharacters |
Get-Random -Count $PasswordLength) -replace ' ')
} until ($Password -cmatch $Regex)
$Password
}
GeneratePassword
I had the same issue here is the snippet I used to create my alphanumerical password its simple all I have done is used ASCII regex replace to make it nice.
Function Password-Generator ([int]$Length)
{
# Generate passwords just call password-generator(lenght of password)
$Assembly = Add-Type -AssemblyName System.Web
$RandomComplexPassword = [System.Web.Security.Membership]::GeneratePassword($Length,2)
$AlphaNumericalPassword = $RandomComplexPassword -replace '[^\x30-\x39\x41-\x5A\x61-\x7A]+'
Write-Output $AlphaNumericalPassword
}
I've created this. You can choose how many Pwd to create
$howoften = Read-Host "How many would you like to create: "
$i = 0
do{
(-join(1..42 | ForEach {((65..90)+(97..122)+(".") | % {[char]$_})+(0..9)+(".") | Get-Random}))
$i++
} until ($i -match $howoften)
To change the length of the pwd simply edit the "42" in line 4
(-join(1..**42** | ForEach ...

Use 2 arrays in one loop

I want to use 2 arrays in one loop, but I am failing each time to find out how?
$hosts = "1.1.1.1,2.2.2.2,3.3.3.3"
$vmotionIPs = "1.2.3.4,5.6.7.8,7.8.9.0"
foreach ($host in $hosts) ($vmotionIP in $vmotionIPs)
New-VMHostNetworkAdapter -VMHost $host-VirtualSwitch myvSwitch `
-PortGroup VMotion -IP $vmotionIP -SubnetMask 255.255.255.0 `
-VMotionEnabled $true
I know the above syntax is wrong but I just hope it conveys my goal here.
The most straightforward way is to use a hashtable:
$hosts = #{
"1.1.1.1" = "1.2.3.4" # Here 1.1.1.1 is the name and 1.2.3.4 is the value
"2.2.2.2" = "5.6.7.8"
"3.3.3.3" = "7.8.9.0"
}
# Now we can iterate the hashtable using GetEnumerator() method.
foreach ($hostaddr in $hosts.GetEnumerator()) { # $host is a reserved name
New-VMHostNetworkAdapter -VMHost $hostaddr.Name -VirtualSwitch myvSwitch `
-PortGroup VMotion -IP $$hostaddr.Value -SubnetMask 255.255.255.0 `
-VMotionEnabled $true
}
First, your arrays aren't arrays. They're just strings. To be arrays you'll need to specify them as:
$hosts = "1.1.1.1","2.2.2.2","3.3.3.3";
$vmotionIPs = "1.2.3.4","5.6.7.8","7.8.9.0";
Second, $host is a reserved variable. You should avoid using that.
Third, I'm assuming you want the first host to use the first vmotionIP, the second host to use the second vmotionIP, etc.
So, the standard way to do this is to do this:
$hosts = "1.1.1.1","2.2.2.2","3.3.3.3";
$vmotionIPs = "1.2.3.4","5.6.7.8","7.8.9.0";
for ($i = 0; $i -lt $hosts.Count; $i++) {
New-VMHostNetworkAdapter -VMHost $hosts[$i] `
-VirtualSwitch myvSwitch `
-PortGroup VMotion `
-IP $vmotionIPs[$i] `
-SubnetMask 255.255.255.0 `
-VMotionEnabled $true;
}
Or you can use the hashtable method #AlexanderObersht describes. This method changes the least about your code, however.
Thank you for your information. What you suggested worked for me some other script but i ended up achieving this using the following.
first i generated a series of ip addresses like this
$fixed = $host1.Split('.')[0..2]
$last = [int]($host.Split('.')[3])
$max = Read-Host "Maximum number of hosts that you want to configure?"
$max_hosts = $max - 1
$hosts =
$last..($last + $max_hosts) | %{
[string]::Join('.',$fixed) + "." + $_
}
and then i did
$vMotion1_ip1 = Read-Host "the 1st vmotion ip of the 1st host?"
$fixed = $vMotion1_ip1.Split('.')[0..2]
$last = [int]($vMotion1_ip1.Split('.')[3])
$max_hosts = $max - 1
$vMotions =
$last..($last + $max_hosts) | %{
[string]::Join('.',$fixed) + "." + $_
}
$first = [string]::Join('.',$fixed) + "." + $_
foreach ($vmhost in $vMotions) {write-host "$vmhost has the following network ("$first$(($last++))", "255.255.255.0")"}
not exactly like this but something along this way.
Thank you all for your answers. I ended up using the do while instead. This allows us to loop through as many as arrays as we want at the same time or include multiple arrays in one loop.
$hosts = #("1.1.1.1","2.2.2.2","3.3.3.3")
$vmotionIPs = #("1.2.3.4","5.6.7.8","7.8.9.0")
[int]$n = 0
do
{
$vmhost = $hosts[$n]
$vmotionIP = $vmotionIPs[$n]
New-VMHostNetworkAdapter -VMHost $vmhost-VirtualSwitch myvSwitch -PortGroup VMotion -IP $vmotionIP -SubnetMask 255.255.255.0 -VMotionEnabled $true
$n++
} while ($n -lt $hosts.count)