Public/New-RandomPassword.ps1

function New-RandomPassword {
    <#
    .SYNOPSIS
    Generates a random password string.

    .DESCRIPTION
    Creates a random password using configurable character sets and optional exclusions.
    By default, the generated value is copied to the clipboard and written to the PassGen log.

    .PARAMETER Length
    Specifies the total length of the password.

    .PARAMETER CharacterSet
    Specifies which character sets are allowed. Uppercase tokens indicate a required category.
    Valid tokens are U, L, N, and S.

    .PARAMETER ExcludeCharacter
    Characters to exclude from the generated password.

    .PARAMETER SkipClipboard
    Prevents the generated password from being copied to the clipboard.

    .PARAMETER PassThru
    Returns the generated password to the pipeline.

    .EXAMPLE
    New-RandomPassword

    .EXAMPLE
    New-RandomPassword -Length 20 -CharacterSet LUNS -ExcludeCharacter '@','O','0'
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)]
        [ValidateRange(1, 256)]
        [int]$Length = 12,

        [Parameter(Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string[]]$CharacterSet = @('ULNS'),

        [Parameter()]
        [char[]]$ExcludeCharacter,

        [Parameter()]
        [switch]$SkipClipboard,

        [Parameter()]
        [switch]$PassThru
    )

    $tokenSets = @{
        U = [char[]]'ABCDEFGHJKLMNPQRSTUVWXYZ'
        L = [char[]]'abcdefghijkmnopqrstuvwxyz'
        N = [char[]]'23456789'
        S = [char[]]'!@#$%^&*()-+=.:;<>?_'
    }

    $requiredCharacters = New-Object System.Collections.Generic.List[char]
    $availableCharacters = New-Object System.Collections.Generic.List[char]

    $characterTokens = New-Object System.Collections.Generic.List[string]
    foreach ($value in $CharacterSet) {
        foreach ($token in [char[]]$value) {
            $tokenText = [string]$token
            if ($tokenText -notmatch '^[ULNSulns]$') {
                throw "CharacterSet contains unsupported token '$tokenText'. Valid tokens are U, L, N, and S."
            }

            if ($characterTokens -notcontains $tokenText) {
                $null = $characterTokens.Add($tokenText)
            }
        }
    }

    foreach ($token in $characterTokens) {
        $filteredCharacters = @(
            $tokenSets[$token.ToUpperInvariant()] | Where-Object { $ExcludeCharacter -cnotcontains $_ }
        )

        if (-not $filteredCharacters) {
            throw "Character set '$token' does not contain any usable characters after exclusions."
        }

        foreach ($character in $filteredCharacters) {
            $null = $availableCharacters.Add($character)
        }

        if ($token -cmatch '^[A-Z]$') {
            $null = $requiredCharacters.Add(($filteredCharacters | Get-Random))
        }
    }

    if ($requiredCharacters.Count -gt $Length) {
        throw "Length '$Length' is too short for the number of required character sets."
    }

    $characters = New-Object System.Collections.Generic.List[char]
    foreach ($character in $requiredCharacters) {
        $null = $characters.Add($character)
    }

    while ($characters.Count -lt $Length) {
        $null = $characters.Add(($availableCharacters | Get-Random))
    }

    $password = -join (Shuffle-PassGenArray -InputObject $characters.ToArray())
    Complete-PassGenResult -Value $password -DisplaySegment @(
        [pscustomobject]@{ Text = $password; Color = 'Red' }
    ) -SkipClipboard:$SkipClipboard.IsPresent -PassThru:$PassThru.IsPresent
}

Set-Alias -Name pg -Value New-RandomPassword