PowerShell Password Generator: Create Secure Random Passwords

PowerShell Password Generator: Create Secure Random Passwords

PowerShell Tips Editor 2 min read
PowerShell Password Generator: Create Secure Random Passwords

Every new user account, every service account reset, and every temporary password in your onboarding script needs a password that is both random and strong. Relying on simple Get-Random with a small character set is insufficient for production use — it is not cryptographically secure. This post builds a PowerShell generate random password solution using .NET’s cryptographic random number generator, enforces complexity rules, creates passphrases, converts output to SecureString, and handles bulk generation for mass provisioning.

Simple Get-Random Character Approach

The basic approach uses Get-Random to select characters from a set. This is acceptable for low-security scenarios like temporary Wi-Fi passwords, but not for AD accounts:

$charset = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%'
$length  = 12

$password = -join (1..$length | ForEach-Object { Get-Random -InputObject $charset.ToCharArray() })
Write-Host "Generated: $password"
Generated: mK8!vQ2wXp#L

The limitation: Get-Random uses a pseudo-random number generator seeded from the system clock. In tight loops or adversarial scenarios, the output is predictable.

Cryptographically Secure with RNGCryptoServiceProvider

For production use, use System.Security.Cryptography.RandomNumberGenerator (.NET 6+) or the legacy RNGCryptoServiceProvider to ensure each byte comes from a cryptographically secure source:

function New-CryptoPassword {
    param(
        [int]$Length = 16,
        [string]$Charset = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%^&*'
    )

    $rng   = [System.Security.Cryptography.RandomNumberGenerator]::Create()
    $bytes = [byte[]]::new($Length)
    $rng.GetBytes($bytes)

    $chars = $Charset.ToCharArray()
    $password = -join ($bytes | ForEach-Object { $chars[$_ % $chars.Length] })
    $rng.Dispose()
    $password
}

$pwd = New-CryptoPassword -Length 16
Write-Host "Secure password: $pwd"

Ensure Complexity Rules Met

After generating the random string, verify it satisfies the complexity requirements. If it does not, regenerate until it does:

function New-ComplexPassword {
    param([int]$Length = 14)
    do {
        $pwd = New-CryptoPassword -Length $Length
        $hasUpper   = $pwd -cmatch '[A-Z]'
        $hasLower   = $pwd -cmatch '[a-z]'
        $hasDigit   = $pwd -match '\d'
        $hasSpecial = $pwd -match '[!@#$%^&*]'
        $complex = $hasUpper -and $hasLower -and $hasDigit -and $hasSpecial
    } while (-not $complex)
    $pwd
}

Write-Host (New-ComplexPassword -Length 14)

Generate Passphrase from Word List

Passphrases — sequences of random words — are both more memorable and higher entropy than character-based passwords. Combine three or four random words with a separator:

function New-Passphrase {
    param(
        [int]$WordCount    = 4,
        [string]$Separator = '-'
    )
    # Load word list from file or use an inline sample
    $wordList = @(
        'Cloud','Pepper','Monkey','River','Delta','Copper','Forest','Spark',
        'Tiger','Flame','Silver','Maple','Bridge','Storm','Gravel','Turbo'
    )
    $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
    $buf = [byte[]]::new(4)

    $words = 1..$WordCount | ForEach-Object {
        $rng.GetBytes($buf)
        $idx = [BitConverter]::ToUInt32($buf, 0) % $wordList.Count
        $wordList[$idx]
    }
    $rng.Dispose()
    ($words -join $Separator) + (Get-Random -Minimum 10 -Maximum 99)
}

Write-Host (New-Passphrase)
Forest-Tiger-Maple-Storm47

Convert to SecureString

AD cmdlets and credential objects require SecureString rather than plain-text strings. Convert your generated password immediately after creation:

$plainText    = New-ComplexPassword -Length 16
$secureString = ConvertTo-SecureString $plainText -AsPlainText -Force

# Use directly with Set-ADAccountPassword
Set-ADAccountPassword -Identity "jsmith" -NewPassword $secureString -Reset

# Or create a credential object
$cred = New-Object System.Management.Automation.PSCredential("jsmith", $secureString)

# Clear the plain-text variable from memory when done
Clear-Variable plainText

Bulk Password Generation

Generate a unique password for each user in a CSV file and output the mapping as a separate credentials file:

$users = Import-Csv "C:\HR\NewHires.csv"
$credsReport = $users | ForEach-Object {
    $pwd = New-ComplexPassword -Length 14
    Set-ADAccountPassword -Identity $_.SamAccountName `
        -NewPassword (ConvertTo-SecureString $pwd -AsPlainText -Force) -Reset
    [PSCustomObject]@{
        Username = $_.SamAccountName
        TempPassword = $pwd
        CreatedAt = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    }
}

# Export to an encrypted file rather than plain CSV
$credsReport | Export-Csv "C:\HR\TempPasswords_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
Write-Host "Generated $($credsReport.Count) passwords"

Common Errors and Fixes

  • Get-Random is not cryptographically secure — use RNG for production. Get-Random uses a seeded pseudo-random generator. The output is statistically random but predictable if an attacker knows the seed time. For passwords, authentication tokens, and anything security-sensitive, always use RandomNumberGenerator.Create() instead.
  • Character set too small reduces entropy. A 12-character password from only lowercase letters has 26^12 ≈ 95 billion combinations. Adding uppercase, digits, and symbols increases this to 94^12 ≈ 475 trillion combinations. Use a full character set and a minimum length of 14 characters for passwords that must resist brute-force attacks.

Related Cmdlets / See Also

Wrapping Up

Use RandomNumberGenerator.Create() for cryptographically secure passwords, enforce complexity rules with a do-while retry loop, convert to SecureString immediately before passing to AD cmdlets, and clear plain-text password variables from memory as soon as they are no longer needed. For human-memorable secrets, passphrases beat character soup every time.

Send-Item -To