Powershell: How to encrypt and store credentials securely for use with automation scripts.

I recently worked on a quick and dirty Powershell script to send me email notifications when content on a web page changed. To send the email, I also went for the quick and dirty method and just used SMTP with my Gmail account. After I finished this dirty masterpiece, I couldn't handle seeing my password sitting in plain text, and I knew something had to be done. Here is how I solved this dilemma:

Step 1: Create your encrypted password file.

First you need a standalone .ps1 script to generate your password file. The following code will achieve this:

$credential = Get-Credential
$credential.Password | ConvertFrom-SecureString | Set-Content c:\scripts\encrypted_password1.txt

Run this script in Powershell, remember to set the execution policy appropriately, and Windows will prompt you for a username and password. Username isn't important since we are just storing the password, but go ahead and enter it anyway. This will create a text file in the specified location with a hash of your password. They "key" to this...Wah wah...is your Windows account. If you don't specify a Key or SecureKey parameter, the default is to use the Windows Data Protection API. Basically, that means using your Windows profile as the key. Note that it's also specifc to the machine where you encrypted it. So, you can't decrypt with the same account from another machine. For more information check out this long boring article: http://msdn.microsoft.com/en-us/library/ms995355.aspx. Why wouldn't I specify a key? Laziness mostly, and I like methods that integrate with Windows authentication. More importantly, I didn't see an obvious way of making the the key secure and accessible. In a production environment, I would recommend a service account used solely for creating and running the encryption and automation scripts.

Step 2: Use the encrypted password file in your automation scripts.

Here is a simplified snippet of code using the encrypted password:

    Set some variables
$emailusername = "myemail"
$encrypted = Get-Content c:\scripts\encrypted_password.txt | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential($emailusername, $encrypted)

if($something = $somethingElse)
        Do some stuff

    $EmailFrom = "[email protected]"
    $EmailTo = "[email protected]"
    $Subject = "I did some stuff!" 
    $Body = "This is a notification from Powershell." 
    $SMTPServer = "smtp.gmail.com" 
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587) 
    $SMTPClient.EnableSsl = $true 
    $SMTPClient.Credentials = $credential;
    $SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)

Note the secret sauce that imports the password on lines 6 and 7. We don't specify any parameters with the ConvertTo-SecureString method because we want it to use the Windows account running the script for decryption, exactly like we did with the ConvertFrom-SecureString for the encryption. After that we can use that credential object willy nilly, example on line 23.

I hope this has been helpful in showing that with a small amount of effort you can get away from storing passwords in plain text in your Powershell scripts.