Fun with PSCredentials

Back in October of 2012, I posted Securely Storing a Password, which showed the key commands to capture a password and store it as a SecureString in a file.  However, this doesn’t address using that password as a PSCredential.  Let’s remedy that.  Here are three functions to save the password from a PSCredential to a file, to recreate a PSCredential with that stored password, to test the PSCredential (by starting a PSSession over WinRM to the local host.)

Update:  Thanks to Lee Holmes for this bit of good news from V3:

 

PSH> $cred = Get-Credential

 

cmdlet Get-Credential at command pipeline position 1

Supply values for the following parameters:

Credential

 

PSH> $cred | Export-CliXml c:\temp\cred.clixml

 

PSH> $cred2 = Import-CliXml c:\temp\cred.clixml

 

function Test-PSCredential {
    <#
    .synopsis
    Validates a PSCredential by starting a PSSession.
    
    .description
    Validates a PSCredential by starting a PSSession by default on the localhost.  Specify -ComputerName to target another host, such as if localhost does not have WinRM enabled.
    
    .parameter Credential
    PSCredential to validate. Required.
    
    .parameter ComputerName
    Computer to test against.  Defaults to localhost.
    
    #>
    
    param ( 
        [System.Management.Automation.PSCredential]$Credential = $null,
        [string]$ComputerName = $env:COMPUTERNAME
    );
    
    try {
        if ($Credential) {
            $session = New-PSSession -ComputerName $ComputerName -Credential $Credential -ErrorAction SilentlyContinue;
            if ($session -and ((Get-PSSession -Id $session.Id).State -eq 'opened')) {
                $true;
            } else {
                Write-Warning "Test-Credential -credential invalid";
            }
        } else {
            Write-Warning "Test-Credential -credential not specified.";
        }
    } catch {
        # insert error handling here
    }
}

function Export-PSCredential {
    <#
    .synopsis
    Securely save the password from a PSCredential to file.
    
    .description
    Save the password from a PSCredential to file as a SecureString.  This is encrypted with keys from both the current user and the local machine, so the file will not be usable by any other user, or on any other machine.
    
    .parameter Credential
    PSCredential to store.  Required.
    
    .parameter Path
    File to store the password.  Defaults to "$home\<DNS domain>#<user name>.credential.dat" of the current user.
    
    #>
    
    param ( 
        [System.Management.Automation.PSCredential]$Credential = $null,
        [string]$Path = $null
    );
    
    if (!$Path) { 
        $Path = "$home\{0}.credential.dat" -f ($Credential.UserName -replace "\\", "#"); 
    }
    
    if ($Credential) {
        $Credential.Password | ConvertFrom-SecureString | Set-Content -Encoding Ascii -Path $Path;
        if (Test-Path -Path $Path) {
            Get-Item $Path;
        } else {
            Write-Warning "Export-Credential credential for '$($Credential.UserName)' failed to save to file '$Path'."
        }
    } else {
        Write-Warning "Export-Credential -credential not specified.";
    }
}

function Import-PSCredential {
    <#
    .synopsis
    Create a PSCredential from a password stored in a file.
    
    .description
    Create a PSCredential from a password stored in a file as a SecureString.  While it can generate a PSCredential for another user, it still decrypts the SecureString in the file with keys from btoh the current user and local machine.
    
    .parameter Path
    File containing the password.  Defaults to "$home\<DNS domain>#<user name>.credential.dat" for the value specified in -UserName.

    .parameter UserName
    Domain\User for whom to create the PSCredential.  Defaults to "<DNS domain>\<user name>" of the current user.
    #>
    
    param ( 
        [string]$Path = $null,
        [string]$Username = "$($env:userDnsDomain.ToLower())\$env:UserName"
    );
    
    try {
        if (!$Path) { 
            $Path = "$home\{0}#{1}.credential.dat" -f $env:USERDNSDOMAIN, $env:USERNAME; 
        }
        
        if (Test-Path -Path $Path) {
            $SecurePass = Get-Content -Path $Path | ConvertTo-SecureString -ErrorAction SilentlyContinue;
            if ($SecurePass) {
                New-Object System.Management.Automation.PSCredential $Username, $SecurePass
            } else {
                Write-Warning "Import-Credential -path '$Path' unable to be converted.";
            }
        } else {
            Write-Warning "Import-Credential -path '$Path' not found.";
        }
    } catch { 
        # insert error handling here
    }
}