Introduction
Do you have processes or scripts that require you to provide a password? Against the desires of your security officer, do you have to save those passwords in plain text, in your scripts? PowerShell offers a way that you can store a password or prompt the user for the information. You can then utilize that information to build what is known as a PSCredential. The majority of commands for PowerShell that support remote connections to servers (WMI, CIM, Invoke-Command, etc.), offer the ability to pass in a credential. While some only need the password, some need the full object to authenticate a user. This object in PowerShell can be made a few different ways based on your needs. I will go over a few options that are commonly used, but first lets discuss what makes up a PSCredential.
PSCredential
The full type is “System.Management.Automation.PSCredential” object. Commands that utilize a “-Credential” parameter will generally require this type to be passed in. You have a few different ways to go about it based on your needs. Each method generally lines up to two different scenarios: interactive or automated. The PSCredential object requires two arguments:
- UserName (System.String)
- Password (System.Security.SecureString)
The username is pretty obvious, but that password is not just a string value. You cannot just take a string and declare it as a SecureString. In order to even build a SecureString means you have to provide a password. Based on how you do this it can pose a security risk in most environments, because you either pass in (or store) your password in plain text. When you are working with passwords in PowerShell it is best to obfuscate your password to protect against those folks with wandering eyes. PowerShell offers a few different options to hide the password. I will go over these below and provide a few examples.
A small caveat
When you work with PSCredential objects you will find that there is a way to read that password back as plain text. This object contains properties on a particular method, that will return the password back as plain text. The method is: GetNetworkCredential(). This method has four properties: Domain, Password, SecurePassword, and UserName. This is there because there could be times you work with commands or third party executables that you need to pass in that password as plain text. You can pull the property to do that without having to necessarily show it in your script as plain text.
That is all to say you just need to understand that while the methods being shown do work for meeting certain security requirements, understand the risk of leaving variables like this in memory once your script completes. It is best to get into the habit of cleaning up at the end. Anytime you use the methods below they need to be locally scoped to the specific function using it. I would also recommend using “Remove-Variable” to ensure the variable you capture the password in is cleared from memory once you are done with it.
Read-Host
Interactive commands mean you are going to prompt the user to enter some bit of information, like a password. Read-Host is useful to prompt for the password at the command line, especially if you don’t need the username as well. In order to get the SecureString object you just need to use the “-AsSecureString” parameter. This will show the password with asterisks as the user types, and will return a SecureString object.
1 2 3 |
$t = Read-Host -Prompt "Please enter your password" -AsSecureString$t.GetType() |
Get-Credential
Using this command will give the user a pop-up window they are likely more familiar with, to enter their username and password. The advantage you have with this command is after the user clicks the “Ok” button it will return a PSCredential object. This can save you a few more lines of code compared to the “Read-Host” command, that would still require you build the PSCredential object. This command is a favorite for one-liner commands where you need to quickly build a PSCredential object for a given command in PowerShell.
1 2 3 |
’’’powershell $t = Get-Credential -Message “Please enter your password” ``` |
After clicking OK we can see that a PSCredential object is returned.
If you wanted to use this in a one-liner like with “Get-WmiObject” you can simply do this:
1 2 3 |
Get-WmiObject win32_service -ComputerName Server1 -Credential (Get-Credential) |
SecureString Conversion
Ok, now that we covered the interactive scenario, we can get to the fun one. There are two specific commands that are involved when you need to provide a password to your script and want to do it in an automated fashion. The commands do not build the PSCredential object, but help specifically in handling the password in a secure manner:
- ConvertTo-SecureString
- ConvertFrom-SecureString
To build this process out you cannot talk about one command with out the other. Even reading the documentation you can see these commands are used in with each other. The first command, ConvertTo, takes a plain text string as input or an encrypted string (via methods shown previously). The output of the command is a SecureString object:
1 2 3 4 |
$pwd = ConvertTo-SecureString "MySuperSecretPassword" -AsPlainText -Force $pwd.GetType() |
Now, when you pipe the output to ConvertFrom, it changes it to an encrypted standard string. This is where a bit of automation can be used. If you output the contents to a text file, it can then be read back and used in the creation of a PSCredential object.
1 2 3 4 5 6 7 |
# build the file with the encrypted password ConvertTo-SecureString "MySuperSecretPassword" -AsPlainText -Force | ConvertFrom-SecureString | Out-File "C:\SomeSecureLocation\pwd.txt" # read it back in to get the SecureString $t = Get-Content "C:\SomeSecureLocation\pwd.txt" | ConvertTo-SecureString |
While this process would work for a single server, it does not if you need to use this password across multiple servers. Why is that you ask? There is a parameter for both commands, “-Key”, that is not required, but as stated in the help documentation:
Specifies the encryption key to use when converting a secure string into an encrypted standard string. Valid key lengths are 16, 24, and 32 bytes.
If you do not provide a value (Byte[]) for this parameter the command will utilize the DPAPI (Windows Data Protection API) to do the encryption. If you are not familiar with this API it is basically the operating system-level data protection (encryption) on each server. So if you let DPAPI do the encryption, then it goes to reason that only that single machine can decrypt it. If you try to convert the contents of that file on another server you will get an error about an invalid key.
So to make this automated and mobile, to use on any server in your environment, we need to utilize the “-Key” parameter. We just need to generate a “Byte[]” key and pass that into the command. You can generate a key with random bytes, and then use that each time you need to read that password into your script.
Helper Functions
I often forget the full syntax with commands that I do not have to use often. I don’t remember things I can look up most of the time. However, other ways I use to remember things with PowerShell, is to build functions that remember for me. So I built out three commands to share with you for the above process. I hope you find these useful for those one-time processes like building a secure password file a script being deployed in your environment. The functions contain help information but I will provide a full example that shows how they can be used below.
You can download the script here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# (1) Create our key file New-KeyFile -KeyFile .\MyKey.key -KeySize 16 # (2) Create our password file New-PasswordFile -PwdFile .\MyPwd.txt -Key (Get-Content .\MyKey.key) # (3) Pull in the password to use $pwd = Get-SecurePassword -PwdFile .\MyPwd.txt -KeyFile .\MyKey.key # build the PSCredential object $mycred = New-Object System.Management.Automation.PSCredential("MyUserName",$pwd) # show the password was captured $mycred.GetNetworkCredential().Password |
Summary
I hope the above shows that there are methods to help in keeping passwords a bit more secure than plain text. You may have processes or scripts that could already utilize the above code (e.g. creating Active Directory accounts, etc., and hope this helps.
- Learning PowerShell and SQL Server – Introduction - April 23, 2018
- Connecting PowerShell to SQL Server – Using a Different Account - January 24, 2017
- How to secure your passwords with PowerShell - January 18, 2017