Passwords.psm1

<#
.SYNOPSIS
    This function generates a new password.
.DESCRIPTION
    This function generates a new password.
    This function is superior to [System.Web.Security.Membership]::GeneratePassword() as it is able
    to guarantee a minimum number of lowercase characters, uppercase characters and digits.
#>

function New-Password {
    [CmdletBinding(PositionalBinding=$false)]
    param (
        # The number of characters in the password.
        [Parameter(Mandatory=$true)]
        [Int]$numberOfCharacters,

        # The minimum number of lowercase characters in the password.
        # Set to 0 to indicate no lowercase characters should be present in the password.
        [Parameter(Mandatory=$false)]
        [ValidateScript({ $_ -gt 0 })]
        [Int]$minNumberLowercase = 1,

        # The minimum number of uppercase characters in the password.
        # Set to 0 to indicate no uppercase characters should be present in the password.
        [Parameter(Mandatory=$false)]
        [ValidateScript({ $_ -gt 0 })]
        [Int]$minNumberUppercase = 1,

        # The minimum number of numerical digits in the password.
        # Set to 0 to indicate no numerical digits should be present in the password.
        [Parameter(Mandatory=$false)]
        [ValidateScript({ $_ -gt 0 })]
        [Int]$minNumberDigits = 1,

        # The minimum number of special characters in the password.
        # Set to 0 to indicate no special characters should be present in the password.
        [Parameter(Mandatory=$false)]
        [ValidateScript({ $_ -gt 0 })]
        [Int]$minNumberSpecialCharacters = 1
    )

    # Verify inputs
    if ($minNumberLowercase + $minNumberUppercase + $minNumberDigits + $minNumberSpecialCharacters -gt $numberOfCharacters) {
        Write-Error "Sum of minimum number of each category of characters is greater than the total number of characters."
        return ""
    }

    # Store password characters as list
    $password = @()

    # Generate list of lowercase characters (97 to 122 in ASCII)
    $lowercaseCharacters = 97 .. 122 | ForEach-Object -Process {
        "$([Char]$_)"
    }

    # Add uppercase characters to password
    for ($i = 0; $i -lt $minNumberLowercase; ++$i) {
        $password += Get-Random -InputObject $lowercaseCharacters -Count 1
    }

    # Generate list of uppercase characters (65 to 90 in ASCII)
    $upperCaseCharacters = 65 .. 90 | ForEach-Object -Process {
        "$([Char]$_)"
    }

    # Add uppercase characters to password
    for ($i = 0; $i -lt $minNumberUppercase; ++$i) {
        $password += Get-Random -InputObject $upperCaseCharacters -Count 1
    }

    # Generate list of digits (48 to 57 in ASCII)
    $digits = 48 .. 57 | ForEach-Object -Process {
        "$([Char]$_)"
    }

    # Add digits to password
    for ($i = 0; $i -lt $minNumberDigits; ++$i) {
        $password += Get-Random -InputObject $digits -Count 1
    }

    # Generate list of special characters (33 to 47, 58 to 64, 91 to 96 in ASCII)
    $specialCharacters = ((33 .. 47) + (58 .. 64) + (91 .. 96)) | ForEach-Object -Process {
        "$([Char]$_)"
    }

    # Add digits to password
    for ($i = 0; $i -lt $minNumberSpecialCharacters; ++$i) {
        $password += Get-Random -InputObject $specialCharacters -Count 1
    }

    # Generate character set containing remaining characters
    $allCharacters = @()
    if ($minNumberLowercase -gt 0) {
        $allCharacters += $lowercaseCharacters
    }
    if ($minNumberUppercase -gt 0) {
        $allCharacters += $upperCaseCharacters
    }
    if ($minNumberDigits -gt 0) {
        $allCharacters += $digits
    }
    if ($minNumberSpecialCharacters -gt 0) {
        $allCharacters += $specialCharacters
    }

    # Fill up password with remaining characters
    $numRemainingCharacters = $numberOfCharacters - $minNumberLowercase - $minNumberUppercase - $minNumberDigits - $minNumberSpecialCharacters
    for ($i = 0; $i -lt $numRemainingCharacters; ++$i) {
        $password += Get-Random -InputObject $allCharacters -Count 1
    }

    # Randomly permute password
    $password = $password | Sort-Object { Get-Random }

    # Join password into a string and return
    $password -join ""
}