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:

  1. Having NO authentication in your script is extremely bad
  2. We want something that can be used when VCenter is not in an environment or we want to extend beyond virtualization

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-Host
Get-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" -AsSecureString
ConvertFrom-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.)

As I stated before, I prefer to dump the Encrypted Standard String out to a file, as it does make it a much cleaner looking script, and can be used for any number of scripts I am executing.  If you make an attempt to execute the script as a user that is different from the one that generated the Encrypted Secure String, you will encounter the following error, and the script will terminate: Key not valid for use in specified state.
If you would like to retrieve your password in clear text format, you can use the following script…again as the user that generated the original Encrypted Secure String.

$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

The standard disclaimer here applies in that it is extremely difficult to truly secure your scripts from malicious use.  For example, anyone that has access can modify your script contents to do anything to the remote system based on the secured credentials, but at least they will not be able to take your username and password and directly access the remote systems.  This is just one step to providing a little more security, and possibly help with the security compliance regulations in place at many organizations.  Similar structures in your code will also allow you to use these methods any time a PowerShell Credential object is involved.