Functions/PSCredential.ps1

function Export-PSCredential
{
<#
.SYNOPSIS
Encrypts and saves a PowerShell Credential object to file or to Azure KeyVault
 
.DESCRIPTION
Export-PSCredential is used to save a PowerShell Credential object [System.Management.Automation.PSCredential] to disk
or to Azure KeyVault so that it can be retrieved later. When saving to disk, the password is encrypted with either a pre-shared key
or a PKI certificate.
 
.PARAMETER Credential
The Credential object that will be exported.
 
.PARAMETER Path
Path to the JSON file that will be created to save the encrypted credential.
 
.PARAMETER SecureKey
A SecureString that is used as a Pre-Shared-Key for encrypting the credential password.
 
.PARAMETER Thumbprint
The ThumbPrint of a certificate on the local computer that will be used to encrypt the credential password.
 
.PARAMETER CertificateFile
Path to a .CER certificate public key file that will be used to encrypt the credential password.
 
.PARAMETER CertificateStore
Specifies the certifcate store of the specified certificate thumbprint. Either LocalMachine or CurrentUser.
 
.PARAMETER KeyVault
The name of the Azure KeyVault that will be used to store the exported credential.
 
.PARAMETER SecretName
The name of the Azure KeyVault secret to create that will be used to store the exported credential.
 
.EXAMPLE
Export a credential to file using a pre-shared key.
$Credential | Export-PSCredential -Path ./savedcredential.json -SecureKey ('$ecretK3y' | Convertto-SecureString -AsPlainText -Force)
 
.EXAMPLE
Export a credential to file using a Certificate
$Credential | Export-PSCredential -Path ./savedcredential.json -Thumbprint '87BB70A19A7671D389F49AF4C9608B2F381FDD80'
 
.EXAMPLE
Export a credential to an existing Azure KeyVault. The user executing the script must be authenticated to Azure with sufficient permissions to the KeyVault.
$Credential | Export-PSCredential -KeyVault 'My-KeyVault' -SecretName 'SavedCred-Secret'
 
#>

    [CmdletBinding()]
    param(
        [parameter(Mandatory = $true, ParameterSetName="LocalKey",Position=1,ValueFromPipeline=$true)]
        [parameter(Mandatory = $true, ParameterSetName="KeyVault",Position=1,ValueFromPipeline=$true)]
        [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint",Position=1,ValueFromPipeline=$true)]
        [parameter(Mandatory = $true, ParameterSetName="CertFile",Position=1,ValueFromPipeline=$true)]
        [ValidateNotNullorEmpty()]
         [pscredential]$Credential,

        [parameter(Mandatory = $true, ParameterSetName="LocalKey",Position=2)]
        [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint",Position=2)]
        [parameter(Mandatory = $true, ParameterSetName="CertFile",Position=2)]
        [ValidateNotNullorEmpty()]
         [string]$Path,

        [parameter(Mandatory = $true, ParameterSetName="LocalKey")]
        [ValidateNotNullorEmpty()]
         [securestring]$SecureKey,

        [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint")]
        [ValidateNotNullorEmpty()]
         [string]$Thumbprint,

        [parameter(Mandatory = $true, ParameterSetName="CertFile",Position=1,ValueFromPipeline=$true)]
        [ValidateScript({test-path $_})]
         [string]$CertificateFile,

        [parameter(Mandatory = $false, ParameterSetName="CertificateThumbprint")]
        [ValidateSet("LocalMachine","CurrentUser")]
         [string]$CertificateStore,


        [parameter(Mandatory = $true, ParameterSetName="KeyVault")]
         [string]$KeyVault,

         [parameter(Mandatory = $true, ParameterSetName="KeyVault")]
         [string]$SecretName
    )


    if ($PSCmdlet.ParameterSetName -eq 'KeyVault') {
        try
        {
            $keyVaultObject = Get-AzureRmKeyVault -VaultName $keyVault -ErrorAction "Stop"
        }
        catch
        {
            throw "Unable to access KeyVault $KeyVault, ensure that the current session has access to it. Use Add-AzureRmAccount or Login-AzureRmAccount to establish access for the current session. $($_)"
        }

        if ($null -eq $keyVaultObject)
        {
            throw "Unable to find KeyVault $KeyVault within the current subscription"
        }

        Write-Verbose -Message "Saving Credential to KeyVault $KeyVault"
        Set-AzureKeyVaultSecret -VaultName $KeyVault -Name  $SecretName -SecretValue $Credential.Password -Tag @{username = $Credential.UserName}



    }
    elseif ($PSBoundParameters.ContainsKey('Path'))
    {
        $CredentialExport = New-Object -TypeName PSObject -Property @{Username = $Credential.UserName;Password = $null}

        if ($PSCmdlet.ParameterSetName -eq 'LocalKey')
        {
            $CredentialExport.Password = ConvertFrom-FIPSsecureString -SecureString $Credential.Password -SecureKey $SecureKey -Verbose:$Verbose
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'CertificateThumbprint')
        {
            if ($PSBoundParameters.ContainsKey('CertificateStore'))
            {
                $CredentialExport.Password = ConvertFrom-PKISecureString -SecureString $Credential.Password -Thumbprint $Thumbprint -CertificateStore $CertificateStore -Verbose:$Verbose
            }
            else
            {
                $CredentialExport.Password = ConvertFrom-PKISecureString -SecureString $Credential.Password -Thumbprint $Thumbprint -Verbose:$Verbose
            }
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'CertFile') {
            $CredentialExport.Password = ConvertFrom-PKISecureString -SecureString $Credential.Password -CertificateFile $CertificateFile -Verbose:$Verbose
        }

        Write-Verbose -Message "Saving Credential to $Path"
        $CredentialExport | ConvertTo-Json | Out-File $Path

    }



}



function Import-PSCredential
{
<#
.SYNOPSIS
Retrieves and decrypts an exported PowerShell Credential from file or Azure KeyVault back into a PSCredential object.
 
.DESCRIPTION
Import-PSCredential is used to retrieve a previously saved PowerShell Credential object from disk
or from Azure KeyVault and returns a PowerShell Credential object [System.Management.Automation.PSCredential] that can be used within scripts and
Desired State Configurations. When retrieving from disk, the method used to encrypt the credential must be used to decrypt the credential.
 
.PARAMETER Path
Path to the JSON file that contains the encrypted credential.
 
.PARAMETER SecureKey
The SecureString that was used as a Pre-Shared-Key for encrypting the credential password.
 
.PARAMETER Thumbprint
The Thumbprint of the certificate on the local computer that contains the private key of the certificate used to encrypt the credential password.
 
.PARAMETER CertificateStore
Specifies the certifcate store of the specified certificate thumbprint. Either LocalMachine or CurrentUser.
 
.PARAMETER KeyVault
The name of the Azure KeyVault that will that contains the exported credential secret.
 
.PARAMETER SecretName
The name of the Azure KeyVault secret that contains the exported credential.
 
.EXAMPLE
Import a credential from file using a pre-shared key.
$Credential = Import-PSCredential -Path ./savedcredential.json -SecureKey ('$ecretK3y' | Convertto-SecureString -AsPlainText -Force)
 
.EXAMPLE
Import a credential from file using a Certificate
$Credential = Import-PSCredential -Path ./savedcredential.json -Thumbprint '87BB70A19A7671D389F49AF4C9608B2F381FDD80'
 
.EXAMPLE
Import a credential from an Azure KeyVault. The user executing the script must be authenticated to Azure with sufficient permissions to the KeyVault.
$Credential = Import-PSCredential -KeyVault 'My-KeyVault' -SecretName 'SavedCred-Secret'
#>

    [CmdletBinding()]
    param(
        [parameter(Mandatory = $true, ParameterSetName="LocalKey",Position=1)]
        [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint",Position=1)]
         [string]$Path,

        [parameter(Mandatory = $true, ParameterSetName="LocalKey")]
        [securestring]$SecureKey,

        [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint")]
        [ValidateNotNullorEmpty()]
         [string]$Thumbprint,

        [parameter(Mandatory = $false, ParameterSetName="CertificateThumbprint")]
        [ValidateSet("LocalMachine","CurrentUser")]
          [string]$CertificateStore,

        [parameter(Mandatory = $true, ParameterSetName="KeyVault")]
         [string]$KeyVault,

         [parameter(Mandatory = $true, ParameterSetName="KeyVault")]
         [string]$SecretName
    )


    if ($PSCmdlet.ParameterSetName -eq 'KeyVault') {
        try
        {
            $keyVaultObject = Get-AzureRmKeyVault -VaultName $keyVault -ErrorAction "Stop"
        }
        catch
        {
            throw "Unable to access KeyVault $KeyVault, ensure that the current session has access to it. Use Add-AzureRmAccount or Login-AzureRmAccount to establish access for the current session. $($_)"
        }

        if ($null -eq $keyVaultObject)
        {
            throw "Unable to find KeyVault $KeyVault within the current subscription"
        }

        Write-Verbose "Reading credential object data from $KeyVault"
        $SecretData = Get-AzureKeyVaultSecret -VaultName $KeyVault -Name $SecretName

        if ($null -eq $SecretData)
        {
            throw "Could not read data from $SecretName in $KeyVault"
        }

        $Username = ($SecretData.Attributes.Tags).username
        if ($null -eq $Username)
        {
            throw "$SecretName does not have a Username tag"
        }

        New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList @($UserName,$SecretData.SecretValue)

    }
    elseif ($PSBoundParameters.ContainsKey('Path'))
    {
        Write-Verbose "Reading credential object data from $Path"
        $CredentialImport = Get-Content $Path -Raw | ConvertFrom-Json

        if ($PSCmdlet.ParameterSetName -eq 'LocalKey')
        {
            $SecureStringPassword = ConvertTo-FIPSsecureString -EncryptedString $CredentialImport.Password -SecureKey $SecureKey -Verbose:$Verbose
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'CertificateThumbprint')
        {
            if ($PSBoundParameters.ContainsKey('CertificateStore'))
            {
                $SecureStringPassword = ConvertTo-PKISecureString -EncryptedString $CredentialImport.Password -Thumbprint $Thumbprint -CertificateStore $CertificateStore
            }

            $SecureStringPassword = ConvertTo-PKISecureString -EncryptedString $CredentialImport.Password -Thumbprint $Thumbprint
        }

        New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList @(($CredentialImport.UserName),$SecureStringPassword)

    }



}

function New-PSCredential
{
<#
.SYNOPSIS
Create a new PowerShell Credential object with a random password. Encrypts and saves the Credential object to file or to Azure KeyVault
 
.DESCRIPTION
New-PSCredential is used to create a new PowerShell Credential object [System.Management.Automation.PSCredential] with the provided username and
a strong random password. The resulting credential object is returned as well as saved to disk or to Azure KeyVault so that it can be retrieved later.
When saving to disk, the password is encrypted with either a pre-shared key or PKI certificate.
 
.PARAMETER Username
Username to use for the Credential to be created.
 
.PARAMETER Path
Path to the JSON file that will be created to save the encrypted credential.
 
.PARAMETER SecureKey
A SecureString that is used as a Pre-Shared-Key for encrypting the credential password.
 
.PARAMETER Thumbprint
The ThumbPrint of a certificate on the local computer that will be used to encrypt the credential password.
 
.PARAMETER CertificateFile
Path to a .CER certificate public key file that will be used to encrypt the credential password.
 
.PARAMETER CertificateStore
Specifies the certifcate store of the specified certificate thumbprint. Either LocalMachine or CurrentUser.
 
.PARAMETER KeyVault
The name of the Azure KeyVault that will be used to store the exported credential.
 
.PARAMETER SecretName
The name of the Azure KeyVault secret to create that will be used to store the exported credential.
 
.EXAMPLE
Creating a credential to be used as a service account, and creating the account.
$Credential = New-PSCredential -Username 'svc.SharePoint.farm' -Path ./savedcredential.json -Thumbprint '87BB70A19A7671D389F49AF4C9608B2F381FDD80'
New-ADUser -Name $Credential.Username -AccountPassword $Credential.Password -Enabled:$true
 
#>

    [CmdletBinding()]
    param(
        [parameter(Mandatory = $true, ParameterSetName="LocalKey",Position=1,ValueFromPipeline=$true)]
        [parameter(Mandatory = $true, ParameterSetName="KeyVault",Position=1,ValueFromPipeline=$true)]
        [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint",Position=1,ValueFromPipeline=$true)]
        [parameter(Mandatory = $true, ParameterSetName="CertFile",Position=1,ValueFromPipeline=$true)]
         [string]$Username,

         [parameter(Mandatory = $true, ParameterSetName="LocalKey",Position=2)]
         [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint",Position=2)]
         [parameter(Mandatory = $true, ParameterSetName="CertFile",Position=2)]
         [ValidateNotNullorEmpty()]
          [string]$Path,

         [parameter(Mandatory = $true, ParameterSetName="LocalKey")]
         [ValidateNotNullorEmpty()]
          [securestring]$SecureKey,

         [parameter(Mandatory = $true, ParameterSetName="CertificateThumbprint")]
         [ValidateNotNullorEmpty()]
          [string]$Thumbprint,

         [parameter(Mandatory = $true, ParameterSetName="CertFile",Position=1,ValueFromPipeline=$true)]
         [ValidateScript({test-path $_})]
          [string]$CertificateFile,

         [parameter(Mandatory = $false, ParameterSetName="CertificateThumbprint")]
         [ValidateSet("LocalMachine","CurrentUser")]
          [string]$CertificateStore,


         [parameter(Mandatory = $true, ParameterSetName="KeyVault")]
          [string]$KeyVault,

          [parameter(Mandatory = $true, ParameterSetName="KeyVault")]
          [string]$SecretName
    )


    $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList @($Username, (ConvertTo-SecureString -String (New-Password) -AsPlainText -Force) )

    $PSBoundParameters.Remove('Username') | Out-Null
    $ExportParameters = $PSBoundParameters
    $ExportParameters.Add('Credential',$Credential)

    Export-PSCredential @ExportParameters | Out-Null

    return $Credential

}