Automation is great. However most of the time, while doing certain tasks, you need to use a different set of credentials then the current logged on user. You can put direct password in plain text inside script but this is not a best practice or recommended one. After all, passwords are safe only if they are kept secret. Writing them in clear text inside script defies the overall purpose. So we’ll see how to avoid this scenario. Let’s start with basics first.
We can get user credential by using cmdlet Get-Credential and storing them inside a variable:
$creds = Get-Credential
This will pop-up a windows box, allowing you to type the credentials. Notice that the output only displays “System.Security.SecureString” on the screen as it is stored in secure string format. You can then use this $creds variable for cmdlets that accepts the $PSCredential object. You can also fetch username and password using $creds.username and $creds.password respectively.
Alternatively, you can ask user to submit credentials at run time by using Read-Host cmdlet:
$username = Read-Host "Enter Username" $password = Read-Host "Enter Password" -AsSecureString
Then you can form a $PSCredential object by using this command:
$creds = New-Object -TypeName System.Management.Automation.PSCredential ` -ArgumentList $username,$password
This is great for interactive scripts but not for scenarios where script needs to run without user interaction. And majority of the scripts are of latter kind.
We can use ConvertTo-SecureString cmdlet to convert a plain string into securestring object. The SecureString object can then be used with cmdlets that support parameters of type SecureString, as is the case with a PSCredential object. You can use the command directly or pipe results into the command:
$password = "Pass@word1" $securePassword = ConvertTo-SecureString $password -AsPlainText -Force or "Pass@word1" | ConvertTo-SecureString $password -AsPlainText -Force
Parameter -AsPlainText tells command to treat string as plain text. The string is not encrypted when using this command. -Force is also required with this parameter.
Unfortunately, you cannot directly save a SecureString object to a file for later use. You have to convert this SecureString object to an encrypted standard string. You can do this with ConvertFrom-SecureString:
"P@ssword1" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
You should see output something like this:
We can now just pipe this output to a file using out-file cmdlet or alternatively store this as variable.
"P@ssword1" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File "C:\temp\Password.txt"
Now even if anyone has access to the output, he/she will not be able to read the password.
When you need to use this encrypted password, you can simply reverse the process by importing the data from password.txt file and use ConvertTo-SecureString cmdlet:
$password = Get-Content "C:\temp\Password.txt" | ConvertTo-SecureString
We can also form a credential object like below:
$username = "itg-admin" $passwordFile = "C:\temp\Password.txt" $creds = New-Object -TypeName System.Management.Automation.PSCredential ` -ArgumentList $username, (Get-Content $passwordFile | ConvertTo-SecureString)
Note that this is not a foolproof method of securing password, but its thousand times better than storing passwords as plain text.
Also this encrypted output can be read only by same user account running on the same machine. Or any process running under the same user account for that matter. So, if you transfer the file containing encrypted output to a different machine, or try to read it under a different user account, you will get an error like this:
This is because, by default, convertfrom-securestring cmdlet uses windows data protection api to encrypt/decrypt strings. We need to use this cmdlet with -Key and -SecureKey parameters to force it to use Advanced Encryption Standard enryption algorithm. This allows a output which can be read on different machines.
So improving on our example, while storing password, we should below commands:
[Byte] $key = (1..16) $password = "P@ssword1" | ConvertTo-SecureString -AsPlainText -Force $password | ConvertFrom-SecureString -Key $key | Out-File "C:\temp\Password.txt"
If we fetch the content of this file, it should be like this:
You can instantly notice that this output is more randomized which means more secure. We can export this file to another machine and then read password using below command:
[Byte] $key = (1..16) Get-Content "C:\Users\itg-admin\Desktop\Password.txt"| ConvertTo-SecureString -Key $key
Now that you know how to use an AES key to make SecureStrings created by different user accounts and workstations, you have to protect that key as best as you can since anybody who has that AES key can now decrypt the data protected.
We can also generate and secure AES key in a secure file. For this, we can use below commands:
$Key = New-Object Byte 16 # You can use 16, 24, or 32 for AES [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key) $Key | Out-File "C:\temp\aes.key"
If we see output, it should be something like this:
And can read the same using below commands:
$username = "itg-admin" $passwordFile = "C:\Users\itg-admin\Desktop\Password.txt" $keyFile = "C:\Users\itg-admin\Desktop\aes.key" $key = Get-Content $keyFile $creds = New-Object -TypeName System.Management.Automation.PSCredential ` -ArgumentList $username, (Get-Content $passwordFile | ConvertTo-SecureString -Key $key)
Again, protect that AES key as if it were your password. Anybody who can read the AES key can decrypt anything that was encrypted with it.