When using the PowerShell plugin for vCenter Orchestrator (vCO) you might receive a ‘System.Security.Cryptography.CryptographicException’ exception when using ‘Import-CliXml’ with a PSCredential object. Furthermore, you might get the message: ‘The requested operation cannot be completed. The computer must be trusted for delegation and the current user account must be configured to allow delegation’.
The reason behind this seems that the security token of the user running in the PowerShell session is constrained and thus cannot implicitly decrypt the password in the credential object. Though it might be possible to configure delegation, I fonud in most of the cases this is not a viable approach. Instead I recommend to use a static password to decrypt and encrypt the credentials. That static password then might be saved in a Configuration DB entry within vCO and passed on to the PowerShell script to be executed.
Within the PowerShell script you would then use that password to decrypt the actual password and convert the resulting object to a proper PSCredential object. I therefore created two Cmdlets that mimic the behaviour of Import-CliXml and Export-CliXml and accept an additional parameter for the static decryption key (‘KeyPhrase’). If you omit a ‘KeyPhrase’ if behaves just like the normal ‘Import-CliXml’:
function Import-Credential{ [CmdletBinding( SupportsShouldProcess=$true, ConfirmImpact="Low", HelpURI='http://dfch.biz/PS/System/Utilities/Export-Credential/' )] Param( [Parameter(Mandatory = $true, ValueFromPipeline = $True, Position = 0)] [string] $Path , [Parameter(Mandatory = $false, Position = 1)] [string] $KeyPhrase = [NullString]::Value ) [Boolean] $fReturn = $false; $OutputParameter = $null; try { # Parameter validation # N/A if($PSCmdlet.ShouldProcess($Path)) { $Credential = Import-CliXml $Path; if($KeyPhrase) { $KeyPhrase = $KeyPhrase.PadRight(32, '0').Substring(0, 32); $Enc = [System.Text.Encoding]::UTF8; $k = $Enc.GetBytes($KeyPhrase); $Credential.Password = $Credential.Password | ConvertTo-SecureString -Key $k; $Credential = New-Object System.Management.Automation.PSCredential( $Credential.Username, $Credential.Password); } else { $Credential = Import-CliXml $Path; } # if $fReturn = $true; $OutputParameter = $Credential; } # if } # try catch { # ... } # catch finally { # ... } # finally return $OutputParameter; } # Import-Credential
To encrypt you call the following Cmdlet with a ‘KeyPhrase’:
function Export-Credential{ [CmdletBinding( SupportsShouldProcess=$true, onfirmImpact="Low", HelpURI='http://dfch.biz/PS/System/Utilities/Export-Credential/' )] Param( [Parameter(Mandatory = $true, Position = 0)] [string] $Path , [Parameter(Mandatory = $true, ValueFromPipeline = $True, Position = 1)] [Alias('Credential')] [PSCredential] $InputObject , [Parameter(Mandatory = $false, Position = 2)] [string] $KeyPhrase = [NullString]::Value ) [Boolean] $fReturn = $false; $OutputParameter = $null; try { # Parameter validation # N/A if($KeyPhrase) { Log-Debug $fn ("Creating KeyPattern from Keyphrase ..."); $KeyPhrase = $KeyPhrase.PadRight(32, '0').Substring(0, 32); $Enc = [System.Text.Encoding]::UTF8; $k = $Enc.GetBytes($KeyPhrase); Log-Debug $fn ("Encrypting password ..."); $Cred = Select-Object -Property '*' -InputObject $InputObject; $Cred.Password = ConvertFrom-SecureString -SecureString $Cred.Password -Key $k; } else { $Cred = $InputObject; } # if if($PSCmdlet.ShouldProcess( ("Cred.Username '{0}' to '{1}'" -f $Cred.Username, $Path) )) { Log-Debug $fn ("Saving PSCredential ..."); $OutputParameter = Export-CliXml -Path $Path -InputObject $Cred -WhatIf:$false -Confirm:$false; $fReturn = $true; } # if } # try catch { # ... } # catch finally { # ... } # finally return $OutputParameter;