My buddy Pat Richard wrote a really cool PowerShell function to Generate complex random passwords and then produce a Phonetic pronunciation on the screen to read it back to a user.

I thought it was pretty darn cool. 

But I looked at the use of the Switch statement as a solution for matching letters to actual words and kept thinking “I’ll bet I could do this with an array.”

Now let me point out, I am NOT poking fun or criticizing Pat’s work.  I’m just taking a stab at a different twist on the same solution.  

I think it’s a good example of how you do something to make it work in PowerShell and then you can look at it again and say “Oh! I could this a bit differently!” and improve upon the original design.

So I did a few pieces differently.  First since most of what the original Function did was match characters to words, and SINCE all of those characters have a numeric position in the ASCII table; I built a small array to hold that information.

$Words=@()
For($x=0;$x -le 125; $x++) { $Words+=([char][byte]$x).tostring() }

The reason I tack on a “.tostring()” is that we need an array of strings.   Using [byte][char] will simply produce a System.Char type of object and they need to be System.String.  

Why?  Later on the Function uses a “.toupper()” method which is not available with System.Char.

I then swapped out all of the single letters with actual words to replace the content from the Switch process that existed before.

$Words[48]="zero"
$Words[49]="one"
$Words[50]="two"
$Words[51]="three"
$Words[52]="four"
$Words[53]="five"
$Words[54]="six"
$Words[55]="seven"
$Words[56]="eight"
$Words[57]="nine"
$Words[97]="alpha"
$Words[98]="bravo"
$Words[99]="charlie"

Then some slight modification on the ForEach statement to go through the password.  The Words are now just accessed by pulling up the ASCII value of the letter to access the Array member….

$ThisWord=$Words[([byte][char]($ThisLetter).tolower())]

The end result is the same output from Pat’s original function.  I think it might be a little faster if you were creating Ninety-Eight (98) character long passwords for your Development team Winking smile

Then of course the nerd in me got bored.  I kept seeing the Phonetic speech and thought “This NEEDS to talk!”

So drop in the Speech synthesis object near the top of the script.

Add-Type -AssemblyName System.Speech    
$synth = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
$synth.SelectVoice("Microsoft Zira Desktop")
$synth.rate=-1

…and of course a line near the bottom to talk.

$Synth.Speak($ThisWord)

But there WAS a glitch.   It didn’t say “UPPER CASE LIMA” or “lower case alpha”.  Another piece needed to be added.    The words needed to be expanded with extra bits but also trap for numbers and special characters.

if ($ThisLetter -cmatch $ThisLetter.ToUpper() -and ‘0123456789%$!#^{}<>’ -notmatch $ThisLetter){$ThisWord = "UPPERCASE "+$ThisWord.ToUpper()}
else {
    if (‘0123456789%$!#^{}<>’ -notmatch $ThisLetter) {$ThisWord="lowercase "+$ThisWord}
    }   

So here’s the end result for those of you curious.  

function New-Password    {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [int]$length
    )
    Add-Type -AssemblyName System.Speech    
    $synth = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
    $synth.Select("Microsoft Zira Desktop")
    $synth.rate=-5
    #
    # Word Array Added by Sean Kearney who sings way too much
    #
    $Words=@()
    For($x=0;$x -le 125; $x++) { $Words+=([char][byte]$x).tostring() }
   
    $Words[33]="EXCLAMATION MARK"
    $Words[35]="NUMBER SIGN"
    $Words[36]="DOLLAR SIGN"
    $Words[37]="PERCENT SIGN"
    $Words[48]="zero"
    $Words[49]="one"
    $Words[50]="two"
    $Words[51]="three"
    $Words[52]="four"
    $Words[53]="five"
    $Words[54]="six"
    $Words[55]="seven"
    $Words[56]="eight"
    $Words[57]="nine"
    $Words[60]="LESS THAN SIGN"
    $Words[62]="GREATER THAN SIGN"
    $Words[94]="CARROT"
    $Words[97]="alpha"
    $Words[98]="bravo"
    $Words[99]="charlie"
    $Words[100]="delta"
    $Words[101]="echo"
    $Words[102]="foxtrot"
    $Words[103]="golf"
    $Words[104]="hotel"
    $Words[105]="india"
    $Words[106]="juliett"
    $Words[107]="kilo"
    $Words[108]="lima"
    $Words[109]="mike"
    $Words[110]="november"
    $Words[111]="oscar"
    $Words[112]="papa"
    $Words[113]="quebec"
    $Words[114]="romeo"
    $Words[115]="sierra"
    $Words[116]="tango"
    $Words[117]="uniform"
    $Words[118]="victor"

    $Words[119]="whiskey"
    $Words[120]="xray"
    $Words[121]="yankee"
    $Words[122]="zulu"
    $Words[123]="LEFT PARENTHESES"
    $Words[125]="RIGHT PARENTHESES"
   
    #
    # Pat Richard’s original code to generate and
    # print to the screen a random password
    #
    $password = -join ([Char[]]’abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789%$!#^{}<>’ | Get-Random -count $length)
    Write-Host "`n$password`n" -ForegroundColor green
    # Write-Host
    ForEach ($character in [char[]]"$password"){
        [string]$ThisLetter = $character
        #
        # Here’s where the Switch code used to be
        # replaced by this one line
        #
        $ThisWord=$Words[([byte][char]($ThisLetter).tolower())]
        #
        # Back to Pat’s code
        #
        if ($ThisLetter -cmatch $ThisLetter.ToUpper() -and ‘0123456789%$!#^{}<>’ -notmatch $ThisLetter){$ThisWord = "UPPERCASE "+$ThisWord.ToUpper()}
        else {
            if (‘0123456789%$!#^{}<>’ -notmatch $ThisLetter) {$ThisWord="lowercase "+$ThisWord}
            }   
        Write-Host "$ThisWord " -NoNewLine -ForegroundColor yellow
        $Synth.Speak($ThisWord)
    }
    Write-Host "`n"
    $password | clip
} # end function New-Password


 

If you’d like to compare the original here is Pat Richard’s original post on EHLO World.

For me, it was just a little fun with a friend’s function …

Thanks Pat for writing a cool function and letting me play Smile

Sean
The Energized Tech