Encrypt and store passwords securely in PowerShell scripts

To totally unlock this section you need to Log-in

Many system admins, system engineers that are usually managing automation scripts know that storing passwords in PowerShell scripts is a tricky task: this is because there is always risk that someone may find the password by simply taking your code from server or automation tool and reviewing it.

To fix/overcome this critical scenario, we could call for stored encrypted password somewhere and referencing it in a script for proper authentication.

In PowerShell you can store sensitive information on disk is through secure strings. Secure strings are just like they simple strings encrypted through the logged-in user’s certificate. In the following example we will convert/encrypt the Secure String created by using Get-Credential cmdlet to an encrypted string by using the ConvertFrom-SecureString cmdlet.

ConvertFrom-SecureString does the opposite of what ConvertTo-SecureString does. It will take a secure string and convert it to an encrypted string. Note that a secure string is simply a string that is masked so it can’t be read on the screen. It’s not actually encrypted. Don’t mistake secure for encrypted.

Create your encrypted password file

In the following method we will use our login credential as password.

First you need a standalone .ps1 script to generate your password file with Encrypted string. Here we are encrypting our password. The following code will achieve this:

#### Set and encrypt our own password to file using default ConvertFrom-SecureString method
(get-credential).password | ConvertFrom-SecureString | set-content "C:\Vault\Personal\Encrypted.txt"

After executing above script, you will get a prompt for the password, then input your credentials that you want to save. In our example an encrypted password file will be saved to "C:\Vault\Personal\Encrypted.txt".

An alternative method, interactive, is the following: let’s say we need to get the password by asking it as input, then the below script will prompt for input via the Read-Host command using the AsSecureString parameter, which will obfuscate your input and return a secure string as shown below:

$securePassword = Read-host -AsSecureString | ConvertFrom-SecureString
$securePassword | Out-File -FilePath "C:\Vault\Personal\Encrypted.txt"

After execution of the above script code, we can look at that variable’s value, by issuing $securePassword in the same Powershell IDE console, for example, and it will be clear that the input has been encrypted. Then the encrypted password will be save to the text file.

Use the Encrypted password in Powershell scripts

We are now in the status that we have the encrypted password, but how do we retrieve these credentials?

If we ever need to retrieve these we will include the following syntax in our scripts to provide the proper credentials (we need to modify it based on our own scenario):

$username = "Administrator"
$password = Get-Content "C:\Vault\Personal\Encrypted.txt" | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential($username,$password)

We can see that after calling the encrypted password into $password, we then just pass it and the $username to $credential by calling PsCredential to authenticate. If we look at what’s in the $credential variable we can see our username and its encrypted password.

Now you have a password with file name Encrypted stored securely on disk as encrypted format. At this point, if we need to retrieve it from the file we can use Get-Content to read the file and then create a PSCredential object from the secure string.

IMPORTANT NOTE: PowerShell creators were smarter making the ConvertTo-SecureString and ConvertFrom-SecureString cmdlets based their encryption key on the identity of the user logged in (at the time of the creation of the encrypted strings). In hindsight, this, of course, make perfect sense when we thought about it as it allows us to encrypt a string and save it to a file but prevents anyone else from reading the same file and decrypting the same string.

Additionally, we could use the above approach also to encrypt the Username in the resulting script, in the following way:

$securecred = Get-Credential
$securecred.UserName | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | set-content "C:\Username.txt"
$securecred.Password | ConvertFrom-SecureString | set-content "C:\Password.txt"

Validating encrypted password and username in Powershell

Let's say that we want test/validate the overall process in a single Powershell code (not to use in production); we can check/validate the encryption/decryption process of both username and password and also validate them by using the following approach:

### Create encrypted username and password (both) #####

$securecred = Get-Credential
$securecred.UserName | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | set-content "C:\Username.txt"
$securecred.Password | ConvertFrom-SecureString | set-content "C:\Password.txt"

### Decrypting both username and password again to be used properly #####

####### Converting from Encrypted to SecureString and then again to PlainText ######
$username = Get-Content "C:\Username.txt" | ConvertTo-SecureString
$BSTRU = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($username)
$username = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTRU)

####### Converting from Encrypted to SecureString and then again to PlainText ######
$password = Get-Content "C:\Password.txt" | ConvertTo-SecureString
$BSTRP = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTRP)

####### Creating the PSCredential object (not used for validation) ##########
$credential = New-Object System.Management.Automation.PsCredential($username,$password)
# $credential

### Validating Credentials #####

$computer = $env:COMPUTERNAME
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$obj = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('machine', $computer)
$obj.ValidateCredentials($username, $password)

Using encrypted username/password for Service Accounts in scheduled Powershell scripts

Last thing to consider and remember, about scheduling Powershell code under specific service accounts, is that we will have to log in as that service account and run the script/code to generate proper encrypted files before scheduling the execution of the predefined Powershell script running with that service account.

If that is not possible, for example, you are not allowed to log in as the service account, but you can run the following script to create the file in a scheduled task running as that user:

### Set Service Account Password Script.
### Remember to remove the plain-text password after running this under the service account.
if (-not (Test-Path $LocalFilePath\Password.txt))
"username" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File $LocalFilePath\Username.txt
"password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File $LocalFilePath\Password.txt
"Setting Password first time for $env:UserName" | out-file $LocalFilePath\App_log.txt -Append

Once the script is run once as the service account in question, remove the above script with the plain-text password and username from the file system.

Last Notes

Remember that SecureStrings are encrypted using the Windows Data Protection API (DPAPI). For more details, start here: https://docs.microsoft.com/en-us/previous-versions/ms995355(v=msdn.10)?redirectedfrom=MSDN.

The keys are specific to both the user and machine where the encryption took place, by default.

Article Name
Encrypt and store passwords securely in PowerShell scripts
Encrypting passwords (and usernames) in Powershell scripts that will be used in scheduled tasks could be very useful to automatize important tasks in test/development/production environments. Let's see how to do this.
Publisher Name