-1

Let me preface by noting this is only my third day with powershell, sorry if this is common knowledge.

This is to create a randomly generated password with requirements. I've set the minimums to 2 for each value, but it doesn't always have a minimum of 2 of each when the password is generated. I think it may be because of the Get-Random -count 10 is pulling from the pool that the rest of the string has created. I’m unsure how i would force it to create a password of 10-12 characters with a minimum of each requirement specified. Can/Should i validate the password after it's been created?

$ErrorActionPreference = "silentlycontinue"
function OnApplicationLoad {
    #Note: This function is not called in Projects
    #Note: This function runs before the form is created
    #Note: To get the script directory in the Packager use: Split-Path $hostinvocation.MyCommand.path
    #Note: To get the console output in the Packager (Windows Mode) use: $ConsoleOutput (Type: System.Collections.ArrayList)
    #Important: Form controls cannot be accessed in this func
    else{Stop-Process -name powershell.exe}
    return $true #return true for success or false for failure
}

function OnApplicationExit {
    #Note: This function is not called in Projects
    #Note: This function runs after the form is closed
    $script:ExitCode = 0 #Set the exit code for the Packager
}

#######Static Password Resources######

$caps = [char[]] "ABCDEFGHJKMNPQRSTUVWXY"
$lows = [char[]] "abcdefghjkmnpqrstuvwxy" 
$nums = [char[]] "2346789"
$spl = [char[]] "#%$+<=>?"
$ofs = ""

####################Starts code############################
function Call-test_pff {
    $form1 = New-Object 'System.Windows.Forms.Form'
    $form1.ClientSize = '514, 640'
    $form1.Name = "form1"
    $form1.Text = "Password tool"
    $form1.add_Load($form1_Load)

#Add Icon to window
$Icon = [system.drawing.icon]::ExtractAssociatedIcon($PSHOME + "\powershell.exe")
$form1.icon = $Icon

#form1: Button1 "Generate password" 
$button1 = New-Object system.windows.forms.button
$button1.location = "10, 25"
$button1.size = "125, 35"
$button1.text = "Generate password"
$button1.add_click({
    $first = Get-Random -Minimum 2 
    $second = Get-Random -Minimum 2
    $third = Get-Random -Minimum 2 
    $fourth = Get-Random -Minimum 2
    $pwd = [string](@($nums | Get-Random -Count $first) + @($lows | Get-Random -Count $second) + @($caps | Get-Random -Count $third) + @($spl | Get-Random -Count $fourth) | Get-Random -Count 10)
    $textbox1.text = $pwd
})
$form1.controls.add($button1)

#form1: Label4: "Password"
$label4 = New-Object system.windows.forms.label
$label4.location = "10, 475"
$label4.size = "200, 20"
$label4.text = "Password:"
$form1.controls.add($label4)

#form1: password box "Password"
$textbox1 = New-Object system.windows.forms.textbox
$textbox1.location = "10, 500"
$textbox1.size = "200, 30"
$textbox1.multiline = $true
$textbox1.readonly = $true
$form1.controls.add($textbox1)

#####################Stop Code Here#####################

#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$form1.add_FormClosed($Form_Cleanup_FormClosed)
#Show the Form
return $form1.ShowDialog()

} #End Function

#Call OnApplicationLoad to initialize
if((OnApplicationLoad) -eq $true)
{
    #Call the form
    Call-test_pff | Out-Null
    #Perform cleanup
    OnApplicationExit
}
  • 5
    As presented here, your code doesn't work at all - you're missing a closing `}` somewhere. Please reduce your code to the absolute minimum required to demonstrate the issue you're having. – alroc Feb 24 '14 at 18:35
  • 1
    How specific do your requirements need to be? You could use `Add-Type -AssemblyName System.Web; [Web.Security.Membership]::GeneratePassword(12,2)` to generate a 12 digit password with at least 2 non-alphanumeric chars. – Rynant Feb 24 '14 at 18:39
  • All that useless form code but you don't even set visible to true – Cole9350 Feb 24 '14 at 18:59
  • @alroc I've added the full code, minus everything that is correctly working. I've tested it and it works for me as presented. – user3341736 Feb 24 '14 at 19:02
  • @Cole9350 Can you explain? As i said, im new. – user3341736 Feb 24 '14 at 19:15
  • @user3341736 The original code you posted didn't have the necessary pieces to get the form to load... Thanks for editing it – Cole9350 Feb 24 '14 at 19:21

1 Answers1

6

Assuming that your problem is in this code:

$first = Get-Random -Minimum 2 
$second = Get-Random -Minimum 2
$third = Get-Random -Minimum 2 
$fourth = Get-Random -Minimum 2
$pwd = [string](@($nums | Get-Random -Count $first) + @($lows | Get-Random -Count $second) + @($caps | Get-Random -Count $third) + @($spl | Get-Random -Count $fourth) | Get-Random -Count 10)

You have two related problems:

  1. You're specifying no maximum for get-random, so you are getting very large numbers in most cases. As a result, $nums|get-random -count $first is always going to return the whole array (but in a random order) - you're asking for a random collection of (for example) 1000 selections from a list of fewer than 10.
  2. Because of the above, Your trailing get-random -count 10 is picking 10 random characters from the entire search space ($caps, $lows,$lows,$spl)

So, you need to do two things:

  1. Get a subset of each of your character types by limiting get-random to both a minimum and maximum.
  2. Ensure that you get the correct distribution of characters in that final line. This can't be done with the current final line, because in theory you could end up with 10 upper-case characters if the random distribution fell that way

To solve the first, by limiting your counts to just the length of those source arrays:

$caps = [char[]] "ABCDEFGHJKMNPQRSTUVWXY"
$lows = [char[]] "abcdefghjkmnpqrstuvwxy" 
$nums = [char[]] "2346789"
$spl = [char[]] "#%$+<=>?"
$ofs = ""
$first = Get-Random -Minimum 2 -Maximum $nums.Length;
$second = Get-Random -Minimum 2 -Maximum $lows.Length;
$third = Get-Random -Minimum 2 -Maximum $caps.Length;
$fourth = Get-Random -Minimum 2 -Maximum $spl.Length;

But this still doesn't solve the second problem, because you will be passing more than 50 characters into that final get-random -count10`, very easily excluding one or more required character types.

If you can deal with a set number of each type of character (in this case, 2 numbers, 3 upper, 3 lower, 2 special), do the following:

$caps = [char[]] "ABCDEFGHJKMNPQRSTUVWXY"
$lows = [char[]] "abcdefghjkmnpqrstuvwxy" 
$nums = [char[]] "2346789"
$spl = [char[]] "#%$+<=>?"
$ofs = ""

$first = $nums | Get-Random -count 2;
$second = $caps | Get-Random -count 3;
$third = $lows | Get-Random -count 3;
$fourth = $spl | Get-Random -count 2;

$pwd = [string](@($first) + @($second) + @($third) + @($fourth) | Get-Random -Count 10)
$pwd

This picks random items in the correct quantity from each group, then randomizes the order of those characters.

alroc
  • 27,574
  • 6
  • 51
  • 97
  • There's what i was missing. I tried setting counts before the string, but I didn't think about setting the values with the counts before the string. Thanks for spelling it out for me so i could understand it easily! – user3341736 Feb 24 '14 at 19:14