PowerShell random string generator
Generate one here, or copy a script below. Each one was run on Windows PowerShell 5.1, PowerShell 7.6, or both.
More options
The short answer
On PowerShell 7.4 or later, call [System.Security.Cryptography.RandomNumberGenerator]::GetString($chars, 24). It reads the operating system cryptographic generator and picks each character uniformly; a chi-square test over 620,000 characters from it on PowerShell 7.6 came out at 50.4 on 61 degrees of freedom, which is what uniform output looks like.
On Windows PowerShell 5.1, which runs on .NET Framework 4.8 and has neither GetString nor GetInt32, pick each character separately with Get-Random -Maximum. Without -SetSeed, Get-Random draws from the system cryptographic generator in 5.1 as well as in 7.
| Command | Windows PowerShell 5.1 | PowerShell 7 |
|---|---|---|
| Get-Random, no -SetSeed | RNGCryptoServiceProvider | RandomNumberGenerator |
| Get-Random -SetSeed n | System.Random, repeatable | System.Random, repeatable |
| Get-SecureRandom | Not available | From 7.4, cannot be seeded |
| RandomNumberGenerator::GetString | Not available | From 7.4, on .NET 8 |
| Membership::GeneratePassword | Available | Not available |
Get-Random is secure. -Count is the trap.
A lot of advice says Get-Random is not cryptographic. That was checked for this page rather than repeated, and it is not true. In the PowerShell source, Get-Random without -SetSeed builds its generator with RandomNumberGenerator.Create(), and inspecting Windows PowerShell 5.1 shows the same internal class holding an RNGCryptoServiceProvider. Only -SetSeed swaps it for System.Random, which is why Get-Random -SetSeed 7 -Maximum 1000 returns 922 every single time.
Get-Random maps a random 31-bit integer onto the requested range by floating-point multiplication rather than by rejection sampling, so it is not perfectly uniform. The unevenness is far too small to matter: across a 62-character alphabet the most likely character is favoured over the least likely by about 4 parts in 100 million.
The real problem is the most common one-liner. Piping an alphabet into Get-Random -Count 24 picks 24 different items, without replacement. A character can never appear twice, so 24 characters from 62 carry 135.5 bits instead of 142.9. Ask for more characters than the alphabet holds and it quietly returns fewer: -Count 100 over 62 characters returned 62 on both 5.1 and 7.6.
A password built with Get-Random -Count is not broken, but it is weaker than its length suggests and can never be longer than its alphabet. Pick one character per call instead, as the examples below do.
Mistakes that pass code review
Every one of these runs cleanly and prints something that looks like a password.
- -join ((65..90) + (97..122) | Get-Random -Count 16 | ForEach-Object { [char]$_ }). No character repeats, and the result can never exceed 52 characters.
- Adding -SetSeed with a timestamp to make results more random. It does the opposite: it replaces the cryptographic generator with System.Random, and anyone who guesses the seed can reproduce the output.
- Get-Random -Maximum 61 for a 62-character alphabet. -Maximum is exclusive, so the last character is never chosen. Use -Maximum $chars.Length.
- [System.Web.Security.Membership]::GeneratePassword(20, 0) in a script that will run on PowerShell 7. System.Web exists only on .NET Framework, so the type is missing there. The second argument is a minimum number of punctuation characters, not a maximum, so symbols appear even when it is 0.
- [guid]::NewGuid().ToString() as a password. It is 36 characters of hex digits and hyphens with a fixed version digit, so it is long without being efficient, and it fails most password rules that require letters in both cases.
Scripts that run on both 5.1 and 7
If a script has to run on Windows PowerShell 5.1 and PowerShell 7, pick characters with Get-Random -Maximum, or fill a byte array from RandomNumberGenerator.Create() for tokens. Both approaches are in the examples below, and both were run on each version.
Once 7.4 is your minimum, Get-SecureRandom is the clearest choice for scripts other people will read. It takes the same parameters as Get-Random except -SetSeed, so nobody can seed it by accident.
Doing this in your own code
PowerShell 7.4+ - GetString
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
# GetString draws from the OS CSPRNG and picks every character uniformly.
1..3 | ForEach-Object {
[System.Security.Cryptography.RandomNumberGenerator]::GetString($chars, 24)
} Windows PowerShell 5.1 and PowerShell 7 - Get-Random per character
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
function New-RandomString([int]$Length = 24) {
# One draw per character, so characters can repeat. -Maximum is exclusive.
-join (1..$Length | ForEach-Object { $chars[(Get-Random -Maximum $chars.Length)] })
}
1..3 | ForEach-Object { New-RandomString 24 } Never add -SetSeed here. It replaces the cryptographic generator with System.Random.
Windows PowerShell 5.1 and PowerShell 7 - hex and URL-safe tokens
$bytes = New-Object byte[] 32
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
# 64 hex characters, 256 bits
-join ($bytes | ForEach-Object { $_.ToString('x2') })
# 43 URL-safe base64 characters, 256 bits
[Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_') Windows PowerShell 5.1 and PowerShell 7 - password with every character type
$classes = @('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz', '0123456789', '!#$%*+-=?@^_')
$alphabet = -join $classes
function New-Password([int]$Length = 20) {
if ($Length -lt $classes.Count) { throw 'Length is shorter than the number of required classes.' }
# Whole-string rejection: generate, check every class, try again if one is missing.
do {
$candidate = -join (1..$Length | ForEach-Object { $alphabet[(Get-Random -Maximum $alphabet.Length)] })
$valid = $true
foreach ($class in $classes) {
if ($candidate.IndexOfAny($class.ToCharArray()) -lt 0) { $valid = $false }
}
} while (-not $valid)
$candidate
}
1..5 | ForEach-Object { New-Password 20 } Questions
How do I generate a random string in PowerShell?
On PowerShell 7.4 and later, [System.Security.Cryptography.RandomNumberGenerator]::GetString($chars, 24). On Windows PowerShell 5.1, -join (1..24 | ForEach-Object { $chars[(Get-Random -Maximum $chars.Length)] }). Both use the system cryptographic generator.
Is Get-Random cryptographically secure?
Without -SetSeed, yes, in both Windows PowerShell 5.1 and PowerShell 7: it draws from the system cryptographic generator. With -SetSeed it switches to System.Random and becomes repeatable and predictable. Its mapping onto a range is very slightly uneven, by about 4 parts in 100 million for 62 values, which is not a practical concern.
What is the difference between Get-Random and Get-SecureRandom?
Get-SecureRandom, added in PowerShell 7.4, shares its implementation with Get-Random but has no -SetSeed parameter, so it always uses the cryptographic generator. Get-Random is equally secure when you do not seed it. Get-SecureRandom makes that intent explicit and impossible to undo by accident.
Why does my PowerShell password never repeat a character?
Because it was built with Get-Random -Count, which selects items without replacement. That lowers the entropy and caps the length at the size of the alphabet. Call Get-Random once per character instead.
How do I create a random password in Windows PowerShell 5.1?
Loop over the length and append $chars[(Get-Random -Maximum $chars.Length)] each time, then check that every required character class is present and regenerate if one is missing. The password example below does exactly that and was run on 5.1.
How do I make a random hex or base64 token in PowerShell?
Create a byte array, fill it with [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes), then format each byte with ToString("x2") for hex, or pass the array to [Convert]::ToBase64String and replace plus and slash for a URL-safe token. That works on 5.1 and 7.