How To Save and Read Sensitive Data with PowerShell
If you deal with private data, automate how you access it with this handy tip.
- By Adam Bertram
Unfortunately, IT professionals have to deal with sensitive information all the time. That information could be in the form of passwords, proprietary company information or anything that you'd rather the world not see. When performing work manually, we deal with this information by interactively working with it. We fill in password prompts, access a "secret" folder while logged in as a domain administrator and so on. This works fine but what happens when we need to automate these processes? Interactivity is the enemy of automation!
We need to automatically read this information without our intervention. This is where you sometimes see people get lazy and add passwords in plain text into the script. That's a big no-no, and there's a better way.
PowerShell has native support for something called the data protection API (DPAPI). DPAPI is a built-in way Windows users can use certificates to encrypt and decrypt information on the fly which is perfect for PowerShell scripting. No username and password required. We simply need a certificate installed which can be self-signed.
Saving and retrieving credentials are the most common reason to encrypt sensitive information. Luckily, PowerShell gives us a built-in way to both store and retrieve username and passwords securely using the commands Get-Credential, Export-CliXml and Import-CliXml.
Let's say I have a script that requires an alternate username and password to run some process. I want this process automated so I can't prompt for the password when needed and I definitely don't want to get lazy and store it in plain text! I need to save it somewhere before the process runs so I can securely retrieve it when I need to.
To save a PSCredential object to the file system, we'll use Get-Credential to provide an interactive input to supply the username and password and then we'll use Export-CliXml to export that credential object to the file system encrypted. The encryption is done automatically when Export-CliXml is invoked.
Here's how you'd save a PSCredential object to a file:
Get-Credential | Export-CliXml -Path MyCredential.xml
That's it! I'll now look at the XML file generated. Notice that the username (userhere) is not encrypted but the password is. PowerShell is smart enough to automatically encrypt the password.
<Objs Version="188.8.131.52" xmlns="http://schemas.microsoft.com/powershell/2004/04">
Once the credential is saved, you'd place this file somewhere where your script has access to. Then, inside of the script, instead of using plain-text passwords, you'd have a line that looks like this:
$credential = Import-CliXml -Path <PathToXml>\MyCredential.xml
At this point, you can use use the PSCredential object using any -Credential parameter you desire. If, for some reason, you don't need a PSCredential object and would like to just retrieve the password, this can also be done using:
If just needing to encrypt text, you may not want to store an entire object on the file system like we did with credentials. Storing and retieving encrypted text is a little bit different. PowerShell doesn't have a built-in way to store encrypted text to the file system but it can encrypt text in memory. It's just up to you to store it somewhere either in a file, a database, wherever. Encrypting strings in PowerShell comes in the form of the ConvertTo-SecureString cmdlet. This is a cmdlet that "converts" text into a secure string in memory.
Perhaps I have some sensitive text I need to encrypt. To do that, I simply need to run ConvertTo-SecureString using a few parameters.
'nuclearlaunchcodes' | ConvertTo-SecureString -AsPlainText -Force
PS> 'nuclearlaunchcodes' | ConvertTo-SecureString -AsPlainText -Force
This isn't going to help much in our script because it's not saved anywhere and it's in the form of an object. We need an encrypted string and we need it saved to a file. To do that, we can do this:
'nuclearlaunchcodes' | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Set-Content -Path secretstuff.txt
This gives us a file that looks like this:
Now that the encrypted string is saved, it's time to retrieve it. Unfortunately, there's no good way to do this with PowerShell and we're forced to use some messy .NET but wrap this little snippet in a function and you'll never have to worry about it again.
PS> $secretStuff = Get-Content -Path secretstuff.txt | ConvertTo-SecureString
Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.