Generate Random Passwords with Powershell

by alexpav 14. May 2009 10:31

Recently, I ran across the need to generate a random password, and decided to write a quick PowerShell script to accomplish this task. I wanted to make sure that I can supply the desired password length and that there are two different levels of complexity: letters (uppercase and lowercase) and numbers, and letters (uppercase and lowercase), numbers, and special characters.

Since I have Powershell v2 on my Windows Server 2008 R2 laptop, that’s what I will use. Powershell v2 contains a nifty random generator cmdlet (get-random), and it will be very useful. If you don’t yet have Powershell V2 installed, grab the latest version listed here: http://blogs.msdn.com/powershell/pages/download-windows-powershell.aspx . As of the time of the writing, CTP3 was the latest released version – download it from the Microsoft Download Center: http://go.microsoft.com/fwlink/?LinkID=131969 .

If you are impatient like me, just grab the script file here:  

pwgen.zip (1.47 kb)

But to truly understand how it works, read on... 

When I started thinking about the problem at hand, I already knew that there would be two different parameters that I need to pass to the script: password length, and password complexity. Getting these values into the script can be achieved with param statement in Powershell:

 param (

[string]$pwcomplexity = $(Read-Host "Please select password complexity. 1 for alphanumeric, or 2 for alphanumeric plus special characters"),

[string]$pwlength = $(Read-Host "Please enter the desired password length")

) 

 

The use of the param statement allows us to pass the values to the script using the –pwcomplexity and –pwlength arguments. The use of the Read-Host statement will allow us to simply run the script – we will be prompted to enter these values as the script executes. Additionally, note the use of the [string] cast. We want to make sure that the input values are strings, not integers, hence the cast .

Before we move on, let’s pre-define the character strings to be used in our passwords. I wanted to have a set with letters (uppercase and lowercase) and numbers, and another set with letters, numbers, and special characters. Note that this is two lines of code (broken up for ease of viewing).

 $strComplex =

"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T",

"U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9","0","a","b","c","d",

"e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z”,

"!","@","#","$","%","^","&","*","(",")","_","=","~","?","<",">"

$strSimple =

"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T",

"U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9","0","a","b","c","d","e","f",

"g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z”

 

 Because we assign values separated by commas, the $strComplex and $strSimple become character arrays. In an array, we can query array’s value by supplying its position within the array. For example, $strSimple[3] will return capital letter D, since the first character in the array has a position of zero. So $strSimple[0] is letter A. You get the idea – we will use a random number to peek inside the array and get characters for our password.

Now we need to process the arguments – make sure that they are valid, and configure options for password generation based on input.

  if ($pwcomplexity -eq "1")#simple password

{

    $strCharacters =  $strSimple

}

elseif ($pwcomplexity -eq "2")#complex password

{

   $strCharacters =  $strComplex

}else

#not sure what was entered, let's assume complex password

{

     write-warning "Invalid passwod complexity supplied, generating a complex password (equivalent to option 2)"

     $strCharacters =  $strComplex

}

if ($pwlength -notmatch "\d+")

#validate that what was entered is a digit using regular expressions.

{

   #not a digit, assume that desired length is 8 characters

   write-warning "Invalid password length supplied, generating an 8 character password instead"

$pwlength = 8

} 

 

Note the use of the regular expression matching with the –notmatch parameter. “\d+” means “any number of digits only”. So if someone types in a letter or any other non-digit during the password length input, the script will trigger the contents of the if block, write a warning to the screen, and assume the default length of 8 characters.

At this point in the script, we have our desired password generator options all taken care of. Let’s write a function to generate the random password. By making it a function, we have a better ability to call it multiple times. Hint: making this a function will come handy as we write the rest of our script.

function generate-password

{

   $pw = $null

   for ($i=0;$i -lt $pwlength; $i++)

   {

     $pw += get-random -InputObject $strCharacters

    }

    return $pw

} 

 

In the function, we will store our generated password in a $pw variable. It is a good idea to reset it so that any previous data that could possibly be stored in that variable is erased before we proceed. So we will set it to $null to begin with. Then, we run a FOR block, which will iterate while the $i variable is between 0 and one less than the desired password length, incrementing $i by one each time the block loops. For example, if our desired password length is 8 characters, we will run it 8 times, with $i being 0,1,2,3,4,5,6, and 7. Within the FOR block, we will use a PowerShell v2 function called get-random (you can find out about get-random in a very cool Microsoft TechNet article here: http://www.microsoft.com/technet/scriptcenter/topics/winpsh/getrandom.mspx)

In a nutshell, get-random will generate a random number or pick a random object from an array. Remember the arrays we setup in the beginning? Well, we will now feed one of the arrays (depending on password complexity requirements) into the get-random cmdlet using the –InputObject parameter. We will get back a random character from that array. Then, we will add this character to the $pw string by using the += operator, which means “take whatever $pw had before, and add the result of the get-random command to it”. Basically, we will continue to increment $pw with random characters. Pretty cool! At the end, we return the password when the function is done.

A function itself does not run as part of script processing. You have to explicitly call it. Let’s do that by assigning the return value of the function to the $genPassword variable:

 $genPassword = generate-password 

 

Great, now the $genPassword variable contains our password. But is it a good password? What if the random generator did not randomly pick any numbers? Or there are no lower case letters? Or no special characters? To make sure that the password meets our requirements, let’s check it.

First, let’s check that the password has the desired length:

if ($genPassword.length -ne $pwlength)

{

   write-warning "Failed to generate the password due to length mismatch"

   $genPassword = $null

   break

}

 

Ok, something went wrong if we ran the loop 8 times but did not get an 8 character password. Let’s exit the whole script with a break statement, and try again. We’ll inform the operator with the write-warning message. Assuming that this part works, let’s check the complexity. And if we don’t meet the complexity requirements, let’s try to re-generate our password by calling the function over and over again, until the complexity requirements are met. We will use a while block, which basically loops over and over again until a pre-defined condition is met. Let’s use the $checkpasswordcomplexity boolen variable (true or false) to control it.

 while ($checkcomplexity -eq $true)

{

   if (($pwcomplexity -eq "1") -and ($genPassword -match "^.*(?=.{$pwlength,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).*$"))

  {

     write-host "Complexity level 1 validated - at least one lower case, one upper case, and one digit"

     write-host "The password is: $genPassword"

     $checkcomplexity = $false

  }

  elseif (($pwcomplexity -eq "2") -and ($genPassword -match "^.*(?=.{$pwlength,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_=~?<>]).*$"))

  {

     write-host "Complexity level 2 validated - at least one lower case, one upper case, and one digit"

     write-host "The password is: $genPassword"

     $checkcomplexity = $false

  }

  else

  {

     write-warning "$genPassword : Complexity validation failed, regenerating..."

     $genpassword=$null$genPassword = generate-password

  }

}

 

So, if the desired complexity is “1” (simple password), and if we can match a regular expression that validates for lowercase, uppercase, and digits, we are good to go and can write the password to console. If the desired complexity is “2” (complex password), and if we can match a regular expression that validates lowercase, uppercase, numbers, and special characters, then we are also good to go and can write the password to console. Otherwise, something went wrong and we randomly picked a too simple of a password, so let’s re-run the generate-password function.

If you are confused or scared of regular expressions, don’t be. Just read this cool MSDN article on .Net regular expressions and you will feel better. Maybe. We will try to talk more about regular expressions in later articles.

As you can see, with the power of PowerShell v2, you can do really cool things, such as generate random passwords. Script on, infradevs!

-alexpav

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Powershell | Scripting | Security

Comments are closed

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen