Src/Public/New-Passphrase.ps1
<#
.NOTES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Module: PSPassPhrase Function: New-Passphrase Author: Martin Cooper (@mc1903) Date: 20-09-2025 GitHub Repo: https://github.com/mc1903/PSpassPhrase Version: 3.0.2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .SYNOPSIS Generates one or more passphrases composed of random words, numbers, and optional special characters. .DESCRIPTION The New-Passphrase function generates passphrases by selecting a specified number of random words from a word list. These words can be separated by spaces, hyphens, or underscores. The passphrase may optionally include a random number and a special character at the end. The function allows customisation of the word length, capitalisation, and special characters used. .PARAMETER passPhraseCount Specifies the number of passphrases to generate. Default is 1. .PARAMETER wordsPerPassPhrase Specifies the number of words to use in each passphrase. Default is 3. .PARAMETER minWordLength Specifies the minimum length of words to be included in the passphrase. Default is 5. .PARAMETER maxWordLength Specifies the maximum length of words to be included in the passphrase. Default is 8. .PARAMETER firstCapitalOnly If specified, only the first letter of the passphrase will be capitalised. Otherwise, the first letter of each word in the passphrase will be capitalised. .PARAMETER delimiterChar Specifies the character to use as the delimiter between each word in the passphrase. Valid values are: 'none', 'space', 'hyphen', or 'underscore'. - 'none' separates words with a space (e.g. "AlphaBetaGamma"). - 'space' separates words with a space (e.g. "Alpha Beta Gamma"). - 'hyphen' separates words with a hyphen (e.g. "Alpha-Beta-Gamma"). - 'underscore' separates words with an underscore (e.g. "Alpha_Beta_Gamma"). The delimiter character will also be inserted before the random number at the end. The default is 'none'. .PARAMETER randomNumberLength Specifies the length of the random number to append to each passphrase. Default is 3. .PARAMETER noLastSpecialChar If specified, no special character will be appended to the end of the passphrase. .PARAMETER specialCharacterList Specifies the list of special characters to choose from when appending a special character to the passphrase. Default is a list containing "!", "$", "*", and "&". .PARAMETER wordListPath Specifies the path to the word list file to use for generating passphrases. If not specified, a default list will be used. .PARAMETER SaveDefaults If specified, saves the current parameter values as the new defaults for future executions. The defaults are stored in a configuration file in your profile folder and will be used for any parameters not explicitly provided in future invocations. .EXAMPLE # Generate a single passphrase with the default settings New-Passphrase .EXAMPLE # Generate 3 passphrases, each composed of 4 words, with a minimum word length of 6 and maximum word length of 10 New-Passphrase -passPhraseCount 3 -wordsPerPassPhrase 4 -minWordLength 6 -maxWordLength 10 .EXAMPLE # Generate a passphrase with words separated by hyphens, a 5-digit random number, and no special character at the end New-Passphrase -delimiterChar hyphen -randomNumberLength 5 -noLastSpecialChar .EXAMPLE # Save your preferred passphrase configuration as the new defaults New-Passphrase -wordsPerPassPhrase 5 -delimiterChar hyphen -randomNumberLength 6 -firstCapitalOnly -specialCharacterList '@','#','$' -SaveDefaults # Subsequent uses of 'New-Passphrase' with no or partial parameters will use these defaults. #> Function New-Passphrase { [CmdletBinding()] param ( [int]$passPhraseCount = 1, [int]$wordsPerPassPhrase = 3, [int]$minWordLength = 5, [int]$maxWordLength = 8, [ValidateSet('none','space','hyphen','underscore')] [string]$delimiterChar = 'none', [switch]$firstCapitalOnly, [int]$randomNumberLength = 3, [switch]$noLastSpecialChar, [string[]]$specialCharacterList = @("!", "$", "*", "&"), [string]$wordListPath, [switch]$SaveDefaults ) $configPath = "$HOME\PSPassPhrase-Defaults.conf" $configFirstCapitalOnly = $null $configNoLastSpecialChar = $null $paramCount = $PSBoundParameters.Count if ('SaveDefaults' -in $PSBoundParameters.Keys) { $paramCount = $paramCount - 1 } $noParams = ($paramCount -eq 0) if ($noParams -and (Test-Path $configPath)) { $lines = Get-Content $configPath foreach ($line in $lines) { if ($line -match '^(.*?)=(.*)$') { $k = $matches[1]; $v = $matches[2] switch($k) { "passPhraseCount" { $passPhraseCount = [int]$v } "wordsPerPassPhrase" { $wordsPerPassPhrase = [int]$v } "minWordLength" { $minWordLength = [int]$v } "maxWordLength" { $maxWordLength = [int]$v } "delimiterChar" { $delimiterChar = $v } "firstCapitalOnly" { $configFirstCapitalOnly = [bool]::Parse($v) } "randomNumberLength" { $randomNumberLength = [int]$v } "noLastSpecialChar" { $configNoLastSpecialChar = [bool]::Parse($v) } "specialCharacterList" { $specialCharacterList = $v -split ',' } "wordListPath" { $wordListPath = $v } } } } } if ('firstCapitalOnly' -in $PSBoundParameters.Keys) { $useFirstCapitalOnly = $firstCapitalOnly.IsPresent } elseif ($null -ne $configFirstCapitalOnly) { $useFirstCapitalOnly = $configFirstCapitalOnly } else { $useFirstCapitalOnly = $false } if ('noLastSpecialChar' -in $PSBoundParameters.Keys) { $useNoLastSpecialChar = $noLastSpecialChar.IsPresent } elseif ($null -ne $configNoLastSpecialChar) { $useNoLastSpecialChar = $configNoLastSpecialChar } else { $useNoLastSpecialChar = $false } if ($SaveDefaults) { Save-Defaults ` $passPhraseCount ` $wordsPerPassPhrase ` $minWordLength ` $maxWordLength ` $delimiterChar ` (('firstCapitalOnly' -in $PSBoundParameters.Keys) -and $firstCapitalOnly.IsPresent) ` $randomNumberLength ` (('noLastSpecialChar' -in $PSBoundParameters.Keys) -and $noLastSpecialChar.IsPresent) ` $specialCharacterList ` $wordListPath ` $configPath return } if ($minWordLength -gt $maxWordLength) { throw "The minimum word length ($minWordLength) cannot be greater than the maximum word length ($maxWordLength)" } if (-not $wordListPath) { $moduleDir = Get-ModulePath $wordListPath = "$moduleDir\src\private\DefaultWordList.txt" } if (-not (Test-Path -Path $wordListPath)) { throw "Word list file not found at path: $wordListPath" } $wordList = Get-Content -Path $wordListPath $pattern = '^[a-zA-Z]+$' $filteredWordList = $wordList | Where-Object { $_ -match $pattern -and $_.Length -ge $minWordLength -and $_.Length -le $maxWordLength } switch ($delimiterChar) { 'space' { $delimiter = " " } 'hyphen' { $delimiter = "-" } 'underscore' { $delimiter = "_" } 'none' { $delimiter = "" } default { $delimiter = "" } } $passPhrases = @() for ($i = 0; $i -lt $passPhraseCount; $i++) { $passPhraseWords = @() for ($j = 0; $j -lt $wordsPerPassPhrase; $j++) { $random = Get-Random -InputObject $filteredWordList $passPhraseWords += $random } if ($useFirstCapitalOnly) { $concatWords = ($passPhraseWords | ForEach-Object { $_.ToLower() }) -join $delimiter $passPhrase = $concatWords.Substring(0,1).ToUpper() + $concatWords.Substring(1) } else { $concatWords = ($passPhraseWords | ForEach-Object { $_.Substring(0,1).ToUpper() + $_.Substring(1).ToLower() }) -join $delimiter $passPhrase = $concatWords } $numberPart = "" for ($n = 0; $n -lt $randomNumberLength; $n++) { $numberPart += (Get-Random -Minimum 0 -Maximum 10) } $passPhrase = $passPhrase + $delimiter + $numberPart if (-not $useNoLastSpecialChar) { $randomSpecialChar = Get-Random -InputObject $specialCharacterList $passPhrase += $randomSpecialChar } $passPhrases += @{ Index = ($i + 1) PassPhrase = $passPhrase } } Write-Output "Index PassPhrase" Write-Output "----- ----------" foreach ($entry in $passPhrases) { $formatString = "{0,-5} {1}" Write-Output ($formatString -f $entry.Index, $entry.PassPhrase) } } |