A question that I am commonly asked when doing PowerShell/EcoShell presentations is a problem that has plagued scripts since time began. ”How do I secure my credentials so I don’t need to supply them in clear-text?” With PowerCLI, VMware enables pass-through credentials of the current logged-on user back to the VCenter server. Let’s assume two things up front:
As an initial test, which is only a proof of concept, let’s take a look at some functions that can help us bring some security into our scripts.
function Set-Password { $pw = Read-Host "Please Enter your Password" -AsSecureString $return = ConvertFrom-SecureString $pw $return}function Get-PassCred { $pass = $key | ConvertTo-SecureString $return = New-Object System.Management.Automation.PSCredential -ArgumentList "UserName",$pass $return}function Get-Connection { Connect-VIServer vcserver.domain.com -Credential $cred Get-VM | select Name}$key = Set-Password$key$cred = Get-PassCred$cred | Out-HostGet-Connection
Let’s take a look at what this does. The first function Set-Password simply prompts a user for their password and reads it directly into a SecureString Variable. Through the use of the ConvertFrom-SecureString Cmdlet, we then take our SecureString and convert it into an Encrypted Standard string. PwoerShell uses the Windows Data Protection API by default to encrypt the returned standard string.
The second function Get-PassCred takes our encrypted Standard String and converts it back into a SecureString. We then create a new PowerShell Credential object that includes a user name of “UserName” and a password made up of our SecureString value. You will need to replace the “UserName” string for this to function in your environment.
The third and final function Get-Connection uses the VMware Cmdlet Connect-VIServer to connect to our environment using our credential object. It then returns a list of VM names in our environment based on the Get-VM Cmdlet from VMware’s PowerCLI.
Again, this is all just a simple POC to show that at now time was the clear-text password ever displayed to you as the user. So how can we take these concepts and more safely deploy scripts in our environment?
One important thing to note is that your converted Standard String value is going to be different every time it is run. The Microsoft Data Protection API uses timestamps as part of its encryption algorithm, so it will likely be impossible to generate the identical key twice. The following standalone script should be your starting point in securing your scripts.
$pw = Read-Host "Please Enter your Password" -AsSecureStringConvertFrom-SecureString $pw#Optional replacement to Line 2#ConvertFrom-SecureString $pw | Out-File c:\encryptedpw.txt
The above script is identical to our Set-Password function in our proof of concept code. The one optional modification here is that we can either dump out the Standard String to host output, or we can capture it into a text file. I personally prefer dumping it out to a text file as it makes the script code more manageable and easier to understand. You will be using the Standard Screen output or the text file in every script you want to protect. It is also important to note here that the Microsoft Data Protection API ties the specific encryption detail to the user ID that generates it. It will not be possible to create the encrypted Standard String as UserX, and execute it as UserY. You will need to run this script and generate the encrypted Standard String as the user you will be scheduling or executing your scripts with.
Now that we have our Encrypted Standard String, we are all set to create some scripts that use this information. You will need to start every script with the following block of code to take advantage of our Encrypted Standard String (If you choose the Copy/Paste method of your Encrypted Standard String, it will be much longer that what is displayed..I cut mine off for display/formatting purposes.)
$secPass = "01000000d08c9ddf0115d1118c7a00c04fc297eb0100" | ConvertTo-SecureString#Optional Replacement for Line 1#$secPass = Get-Content c:\encryptedpw.txt | ConvertTo-SecureString$bStr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secPass)$pass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bStr)$pass