PSVersion: 7.4
OS: Ubuntu 24.04 LTS
TL;DR: Get-Random Function Example
Playing with Randoms can have its perks. This article goes over implementing a random object generator into a PowerShell Function.
How is a Raven like a Writing Desk? Link to heading
Oxford defines Randomness as the quality or state of lacking a pattern or principle of organization. PowerShell has it’s own builtin cmdlet to assist in implementing randomness within a script. A great example of implementing randomness is a password generator!
Let’s start with the information on Get-Help Get-Random:
NAME
Get-Random
SYNOPSIS
Gets a random number, or selects objects randomly from a collection.
DESCRIPTION
The `Get-Random` cmdlet gets a randomly selected number. If you submit a
collection of objects to `Get-Random`, it gets one or more randomly
selected objects from the collection.
Without parameters or input, a `Get-Random` command returns a randomly
selected 32-bit unsigned integer between 0 (zero) and `[int32]::MaxValue`.
You can use the parameters of `Get-Random` to specify the minimum and
maximum values, the number of objects returned from a collection, or a
seed number.
> [!CAUTION] > `Get-Random` doesn't ensure cryptographically secure
randomness. The seed value is used for the > current command and for all
subsequent `Get-Random` commands in the current session until you use >
SetSeed again or close the session. You can't reset the seed to its
default value. > > Deliberately setting the seed results in non-random,
repeatable behavior. It should only be used > when trying to reproduce
behavior, such as when debugging or analyzing a script that includes >
`Get-Random` commands. Be aware that the seed value could be set by other
code in the same > session, such as an imported module. > > PowerShell 7.4
includes `Get-SecureRandom`, which ensures cryptographically secure
randomness.
Did you catch the Caution paragraph?
Get-Random doesn't ensure cryptographically secure randomness.
At this point, a new question emerges; Do I need cryptographically secure randomness and instead utilize Get-SecureRandom for my script? That will depend on your use case. Get-Random by default uses the system tick count (in milliseconds) of the elapsed since the system started as a seed number from 0 to [Int32]::MaxValue (Roughly 2 Billion). If you have a script that runs on a schedule, then the chances of generating that same seed number increases which would mean you could create duplicate values.
Get-SecureRandom on the other hand, with PowerShell 7.4, uses the RandomNumberGenerator Class to ensure an exclusive integer and replaces the previously used “System.Security.Cryptography.RNGCryptoServiceProvider” .NET class.
Matt Graeber wrote a post back in 2014 that covered the effectiveness of Get-Random in comparison to using System.Security.Cryptography.RNGCryptoServiceProvider .NET class.
Please visit Matt’s post for more information on those differences:
https://powershellmagazine.com/2014/07/28/testing-the-effectiveness-of-get-random/
For now, let’s use Get-SecureRandom and implement it into a simple XKCD passphrase generator. For this Passphrase generator, we’ll need a short function and a wordlist that you can find easily enough online. For this example, we’re using a wordlist that’s separating words by newline or `n.
Supercalifragilisticexpialidocious Link to heading
Once we have our wordlist, let’s start on the function. In this example, I’m setting parameters for the passphrase requirements:
Function Get-Passphrase {
Param(
[int]$words = 4,
[string]$delimiter = " ",
[switch]$FirstLetterUpper
)
The $words parameter denotes the number of words we want with a default of 4. $delimiter will be our separator between words, and $FirstLetterUpper capitalizes the first letter of each word.
$password = $null
$wordlist = get-content "Path\To\WordList.txt"
switch($words) {
{$_ -ge 6 } { throw "Word parameter cannot be greater or equal 6." }
5 { $range = (3,4) }
4 { $range = (4,5) }
3 { $range = (5,6) }
2 { $range = (7,8) }
{$_ -le 1 } { throw "Word parameter cannot be less or equal 1." }
}
$list = $wordlist -split "`n" | ForEach-Object {
New-Object PSObject -Property @{
Value = $_.ToLower()
Length = $_.length
}
} | Where-Object {
($_.Length -eq ($range[0] + 1)) -or ($_.Length -eq ($range[1] + 1))
}
Here we’re setting the $password to $null for now and getting the $wordlist content from the saved file path. For some extra sillies, we’ll use a switch for $words so that the number of $words set will also determine the length of characters of those words. For example, a -words 2 password will be 8 or 9 characters in length per word and a -words 5 passphrase will be 4 to 5 characters per word. This helps to ensure that the total number of characters for the passphrase stays within a normal password length.
1..$words | ForEach-Object {
$part = (Get-SecureRandom $list).Value.Trim()
if($FirstLetterUpper) {
$password += ((Get-Culture).TextInfo).ToTitleCase($part)
} else {
$password += $part
}
if($_ -lt $words){
$password += $delimiter
}
}
return $password
The cmdlet now sets a range between 1 and the set $words count. Then for each, we use Get-SecureRandom to select a random word from the wordlist. if($FirstLetterUpper) parameter is used, the first letter of each word is capitalized using Get-Culture. You can also swap this out for $part.ToUpper().SubString(0,1) + $part.ToLower().SubString(1). After, we add a delimiter and then call the $password to finish.
Function Example Link to heading
Function Get-Passphrase {
Param(
[int]$words = 4,
[string]$delimiter = " ",
[switch]$FirstLetterUpper
)
$password = $Null
$wordlist = get-content "Path\To\WordList.txt"
switch($words) {
{$_ -ge 6 } { throw "Word parameter cannot be greater or equal 6." }
5 { $range = (3,4) }
4 { $range = (4,5) }
3 { $range = (5,6) }
2 { $range = (7,8) }
{$_ -le 1 } { throw "Word parameter cannot be less or equal 1." }
}
$list = $wordlist -split "`n" | ForEach-Object {
New-Object PSObject -Property @{
Value = $_.ToLower()
Length=$_.length
}
} | Where-Object {
($_.Length -eq ($range[0] + 1)) -or ($_.Length -eq ($range[1] + 1))
}
1..$words | ForEach-Object {
$part = (Get-SecureRandom $list).Value.Trim()
if($FirstLetterUpper) {
$password += ((Get-Culture).TextInfo).ToTitleCase($part)
} else {
$password += $part
}
if($_ -lt $words){
$password += $delimiter
}
}
return $password
}
Example Call and Output Link to heading
PS /home/fordablescripts> get-passphrase -words 5 -delimiter "-" -firstletterupper
Fold-Jawed-Style-Plane-Shove
PowerShell can be a great tool to create random generating scripts and with PowerShell 7.4, Get-SecureRandom takes that security a step further. Of course, I recommend implementing an MFA service for truly secure access.
Get-Scripting