Skip to main content

Generates a random string of specified length. Useful for passwords and tokens.

function New-RandomString
{
    <#
    .SYNOPSIS
        Generates a random string of specified length.

    .DESCRIPTION
        This function generates a random string consisting of uppercase letters,
        lowercase letters, and numbers (0-9, A-Z, a-z). When the IncludeSymbols
        parameter is specified, it can also include common symbols (!@#$%^&*).
        It's useful for creating random passwords, IDs, tokens, or other unique identifiers.

        Compatible with PowerShell Desktop 5.1+ on Windows, macOS, and Linux.

    .PARAMETER Length
        The length of the random string to generate.
        Default is 32 characters. Must be between 1 and 10000 characters.

    .PARAMETER ExcludeAmbiguous
        Excludes potentially ambiguous characters (0, O, 1, l, I) from the generated string.
        This is useful when the string will be manually typed or displayed.

    .PARAMETER IncludeSymbols
        Includes common symbols (!@#$%^&*) in addition to alphanumeric characters.

    .PARAMETER Secure
        Uses cryptographically secure random number generation instead of Get-Random.
        Recommended for passwords and security tokens.

    .EXAMPLE
        PS> New-RandomString

        Returns a random 32-character string using alphanumeric characters.

    .EXAMPLE
        PS> New-RandomString -Length 16

        Returns a random 16-character string using alphanumeric characters.

    .EXAMPLE
        PS> New-RandomString -Length 64 -ExcludeAmbiguous

        Returns a random 64-character string without ambiguous characters (0, O, 1, l, I).

    .EXAMPLE
        PS> New-RandomString -Length 20 -IncludeSymbols -Secure

        Returns a 20-character string including symbols using secure random generation.

    .OUTPUTS
        System.String
        Returns a string of random characters based on the specified parameters.
        Character set includes alphanumeric characters and optionally symbols.

    .NOTES
        - Compatible with PowerShell Desktop 5.1+ and PowerShell Core 6+
        - Works on Windows, macOS, and Linux
        - The -Secure parameter provides cryptographically strong randomness
        - For general use cases, the default (non-secure) method is sufficient

    .LINK
        https://jonlabelle.com/snippets/view/powershell/generate-random-string-in-powershell
    #>
    [CmdletBinding()]
    [OutputType([String])]
    param (
        [Parameter(Position = 0)]
        [ValidateRange(1, 10000)]
        [int] $Length = 32,

        [Parameter()]
        [switch] $ExcludeAmbiguous,

        [Parameter()]
        [switch] $IncludeSymbols,

        [Parameter()]
        [switch] $Secure
    )

    # Build character set based on parameters
    if ($ExcludeAmbiguous)
    {
        # Exclude ambiguous characters: 0, 1, O, I, l
        $numbers = @('2', '3', '4', '5', '6', '7', '8', '9')
        $uppercaseLetters = @('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
        $lowercaseLetters = @('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z')
    }
    else
    {
        # Include all standard alphanumeric characters
        $numbers = @('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
        $uppercaseLetters = @('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
        $lowercaseLetters = @('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z')
    }

    $symbols = @('!', '@', '#', '$', '%', '^', '&', '*')

    # Build the character pool
    $characterPool = @()
    $characterPool += $numbers
    $characterPool += $uppercaseLetters
    $characterPool += $lowercaseLetters

    if ($IncludeSymbols)
    {
        $characterPool += $symbols
    }

    # Convert to array for better performance
    $characters = [char[]]$characterPool

    # Generate the random string
    $result = [System.Text.StringBuilder]::new($Length)

    if ($Secure)
    {
        # Use cryptographically secure random number generation
        try
        {
            # Use System.Security.Cryptography.RandomNumberGenerator for all modern PowerShell versions
            if ($PSVersionTable.PSVersion.Major -ge 6)
            {
                # PowerShell Core 6+ - use System.Security.Cryptography.RandomNumberGenerator
                $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
            }
            else
            {
                # PowerShell Desktop 5.1 - use RNGCryptoServiceProvider for compatibility
                $rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
            }

            $bytes = New-Object byte[] 4
            for ($i = 0; $i -lt $Length; $i++)
            {
                do
                {
                    $rng.GetBytes($bytes)
                    $randomValue = [BitConverter]::ToUInt32($bytes, 0)
                } while ($randomValue -ge ([uint32]::MaxValue - ([uint32]::MaxValue % $characters.Length)))

                $index = $randomValue % $characters.Length
                [void]$result.Append($characters[$index])
            }
        }
        catch
        {
            # If secure random generation fails, fall back to standard Get-Random
            Write-Verbose "Secure random generation failed, falling back to Get-Random: $($_.Exception.Message)"
            $result.Clear()

            for ($i = 0; $i -lt $Length; $i++)
            {
                $randomIndex = Get-Random -Minimum 0 -Maximum $characters.Length
                [void]$result.Append($characters[$randomIndex])
            }
        }
        finally
        {
            if ($rng) { $rng.Dispose() }
        }
    }
    else
    {
        # Use standard Get-Random for better performance in non-secure scenarios
        for ($i = 0; $i -lt $Length; $i++)
        {
            $randomIndex = Get-Random -Minimum 0 -Maximum $characters.Length
            [void]$result.Append($characters[$randomIndex])
        }
    }

    return $result.ToString()
}