Secure Credentials in PowerShell Scripts
The scripts that I write occasionally need access to specific user account credentials in order to do their work, so I wanted to write a quick note about how to securely store those credentials for scripts (especially if they're going to be run as a scheduled task)! It all revolves around the get-credential cmdlet.
The get-credential cmdlet is made for this; by default, the object that it creates is encrypted to the account that executed it as a secure powershell object. This means that you can easily save those credentials to a secure file with these two commands:
$creds = get-credential -message "Enter credentials"
$creds | export-clixml $credsFile
Many PowerShell cmdlets can take credentials in the form of a credentials object (frequently with the "-credential $creds" syntax), but even when you need to supply a username and password, they're really easy to get out of that $creds object:
$creds.UserName
$creds.GetNetworkCredential().password
If some other user attempts to import the xml password object, they'll get an error that reads "import-clixml : Key not valid for use in specified state." If you just open the XML file directly, you can plainly see the password property... and that it's nice and encrypted! So, only the account that ran the get-credential cmdlet can access the password stored in the XML file that we can generate from that object. How's the easiest way to use this in our scripts? If it's a script that people will be executing, I like to put it in a Try/Catch block like this:
try {
$creds = import-clixml $credsFile
}
catch {
$creds = get-credential -message "Enter credentials"
$creds | export-clixml $credsFile
}
The script will try to import the credentials in the specified file and, if that fails, will prompt the user for credentials and then save them in the file. In practice, this means that the first time a given user runs the script, they'll need to enter their credentials, but after that they're saved in a file that only that user account can access. But what do you do if you're preparing this script to run as a scheduled task? Well, our old friend "runas.exe" comes in handy there!
runas.exe /user:domain\serviceAccount powershell.exe
The easiest way to create the credentials object for the account that'll be running the scheduled task is to just open up a PowerShell prompt as that user. So, you can fire off that above command to give you such a window, and from there you can do the whole "get-credential | export-clixml $credsFile" process to pre-seed the credentials XML file for the service account!
Might also check out the SecretManagement module which is cross-platform
ReplyDeletehttps://github.com/powershell/secretmanagement