Using PSCredentials

I’ve been working on a small project that shuts down machines attached by network and of course power feed to an APC Smart-UPS.
The code that was shutting down the guests required authentication to be passed to the receiving services.

I decided to give the following PowerShell cmdlets a try.

  • Get-Credential
  • ConvertTo-SecureString

———————————————————————————-

Script that creates the password file

(Set-Credential.ps1) looks like this:

Param($file)
$credential = Get-Credential
$credential.Password | ConvertFrom-SecureString | Set-Content $file

Get-Credential prompts for a username and password and creates the PSCredential associating the password with the username.
ConvertFrom-SecureString from the PS documentation…
The ConvertFrom-SecureString cmdlet converts a secure string
(System.Security.SecureString) into an encrypted standard string (System.String).
Then writes the string to the file specified.

Set-Credential can be invoked like this:

C:\Scripts\UPS\Set-myCredential.ps1 C:\Scripts\UPS\mp.txt

———————————————————————————-

Script that reads the password file

(Get-Credential.ps1) into a SecureString.
Then creates the PSCredential based on the username provided and the password as a SecureString.
Then returns the PSCredential:

Param($user,$passwordFile)
$password = Get-Content $passwordFile | ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential($user,$password)
$credential

———————————————————————————-

By the look of it, when creating the encrypted password Get-Credential adds some machine specific information.
As the password file is not machine agnostic (can’t be shared or tranfered).

From my PowerShell script that loaded the assembly into memory and started the shutdown procedure, it looked something like this.

param (
   [parameter(Mandatory=$true, position=0)][string] $scriptPath,
   [parameter(Mandatory=$true, position=1)][string] $fileServerName,
   [parameter(Mandatory=$true, position=2)][string] $fileServerUser,
   [parameter(Mandatory=$true, position=3)][string] $vSphereServerName,
   [parameter(Mandatory=$true, position=4)][string] $vSphereServerUser
)

Set-StrictMode -Version 2.0
# Creates a .net assembly in memory containing the PowerOffUPSGuests class.
# Then we call the InitShutdown passing the details of the machines that need to be shutdown.

$credentialRetrievalScript = Join-Path -Path $scriptPath -ChildPath 'Get-Credential.ps1'
$fileServerUserPwLocation = Join-Path -Path $scriptPath -ChildPath 'FileServerPw.txt'
$vSphereServerUserPwLocation = Join-Path -Path $scriptPath -ChildPath 'VMHostPw.txt'

# names of the ServerController's I.E. the collection of servers that will be shutdown
# these class's need to exist in the $scriptPath and derive from ServerController
$freeNASController = 'FreeNASController'
$vMServerController = 'VMServerController'

# instantiate the credential objects
$fileServerCredential = & $credentialRetrievalScript $fileServerUser $fileServerUserPwLocation
$vSphereServerCredential = & $credentialRetrievalScript $vSphereServerUser $vSphereServerUserPwLocation

# add the assembly that does the work.
Add-Type -Path .\PowerOffUPSGuests.dll

# instantiate a ServerAdminDetails for each server we want to shutdown
$fileServerAdminDetailsInstance = New-Object -TypeName BinaryMist.Networking.Infrastructure.ServerAdminDetails -ArgumentList $freeNASController, $fileServerName, $fileServerCredential
$vSphereServerAdminDetailsInstance = New-Object -TypeName BinaryMist.Networking.Infrastructure.ServerAdminDetails -ArgumentList $vMServerController, $vSphereServerName, $vSphereServerCredential

# instantiate a PowerOffUPSGuests
$powerOffUPSGuestsInstance = New-Object -TypeName BinaryMist.Networking.Infrastructure.PowerOffUPSGuests

# create generic queue and populate with each of the ServerAdminDetail items
# ServerAdminDetails is the base class of FileServerAdminDetails and vSphereServerAdminDetails
$serverAdminDetailsQueueInstance = .\New-GenericObject System.Collections.Generic.Queue BinaryMist.Networking.Infrastructure.ServerAdminDetails
$serverAdminDetailsQueueInstance.Enqueue($fileServerAdminDetailsInstance)
$serverAdminDetailsQueueInstance.Enqueue($vSphereServerAdminDetailsInstance)

$powerOffUPSGuestsInstance.InitShutdownOfServers($serverAdminDetailsQueueInstance)

To debug my library code, I needed to run it somehow.
So I just wrote a small test which passed the PSCredential instance to the code that was going to shutdown the UPS guest.

private PSCredential GetMyCredential(string userName, string pWFileName) {

   string encryptedPw;
   using (StreamReader sR = new StreamReader(pWFileName)) {
   //read the encrypted bytes into a string
   encryptedPw = sR.ReadLine();
   }

   PSCredential pSCredential;
   using(SecureString pW = new SecureString()) {
      char[] pWChars = encryptedPw.ToCharArray();
      foreach(char pWChar in pWChars) {
         pW.AppendChar(pWChar);
      }
      pSCredential = new PSCredential(userName, pW);
   }
   return pSCredential;
}

[TestMethod]
public void TestInitFileServerShutdown() {
   _powerOffUPSGuests = new PowerOffUPSGuests(ConfigurationManager.AppSettings[LogFilePath]);

   PSCredential fileServerCredential = GetMyCredential(
      ConfigurationManager.AppSettings[FileServerUser],
      Path.GetFullPath(ConfigurationManager.AppSettings[FileServerUserPwFile])
   );

   _powerOffUPSGuests.InitFileServerShutdown(ConfigurationManager.AppSettings[FileServer], fileServerCredential);
}

Inspiration

Tags: , , , ,

One Response to “Using PSCredentials”

  1. Preparing APC Smart-UPS 1500 clients « Binarymist Says:

    […] we had to create our password file to use to log in to vSphere. See this post for how this was […]

Leave a comment