Public/Get-SecureCredential.ps1

<#
.SYNOPSIS
    Retrieves stored credentials from an encrypted file.
 
.DESCRIPTION
    This function retrieves stored credentials from an encrypted file. If a plaintext credential file is found,
    it encrypts the credentials and deletes the plaintext file.
 
.PARAMETER EncryptedCredsName
    The name of the credential to be retrieved.
 
.OUTPUTS
    PSCredential
    The retrieved credentials.
 
.EXAMPLE
    $credential = Get-SecureCredential -EncryptedCredsName "MyCredentials"
    $credential.UserName
    $credential.GetNetworkCredential().Password
 
    The function first checks if a plaintext credential file exists. If found, it reads the credentials, encrypts
    them, deletes the plaintext file, and stores the encrypted credentials. If no plaintext file is found, it
    attempts to load the encrypted credentials directly.
#>


function Get-SecureCredential {
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'EncryptedCredsName', Justification = 'This parameter does not contain plaintext password')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '' ,Justification = 'This functionality allows encrypting plaintext password and storing securely')]
    param (
        [Parameter(Mandatory = $true)]
        [Alias('Name')]
        [string]$EncryptedCredsName
    )

    $CredentialFileRoot = (Resolve-Path .\).Path
    $PlaintextCredsFileName = "$EncryptedCredsName.plaintext"
    $EncryptedCredsFileName = "$EncryptedCredsName.encrypted"
    $PlaintextCredsFile = Join-Path -Path $CredentialFileRoot -ChildPath $PlaintextCredsFileName
    $EncryptedCredsFile = Join-Path -Path $CredentialFileRoot -ChildPath $EncryptedCredsFileName

    Write-Debug "Checking for plaintext credential file at '$PlaintextCredsFile'..."
    if (Test-Path -Path $PlaintextCredsFile -PathType Leaf) {
        Write-Debug "Plaintext credential file found at '$PlaintextCredsFile'."

        $Content = Get-Content -Path $PlaintextCredsFile
        $LineCount = ($Content | Measure-Object).Count
        if ($LineCount -ne 2) {
            Throw "Credential file has incorrect structure (line count: $LineCount). Please refer to the script help for details about credential management."
        }

        Write-Verbose "Encrypting credentials and removing plaintext file."
        # Overwriting plaintext file content to mitigate file restoration attack vector
        "Plaintext credentials removed" | Out-File -FilePath $PlaintextCredsFile -Force
        Remove-Item -Path $PlaintextCredsFile -Force -ErrorAction Stop

        # Writing new credentials
        $NewCreds = [PSCredential]::New($Content[0], ($Content[1] | ConvertTo-SecureString -AsPlainText -Force))
        $Content = $null
        $NewCreds | Export-Clixml -Path $EncryptedCredsFile -Force
        Write-Verbose "Credentials saved to '$EncryptedCredsFile'."
    }
    elseif (-not (Test-Path -Path $EncryptedCredsFile -PathType Leaf)) {
        Throw "Credential file '$EncryptedCredsFile' could not be found."
    }

    try {
        $Credential = Import-Clixml -Path $EncryptedCredsFile
    }
    catch {
        Throw "An error occurred while loading credentials. Message: $($_.Exception.Message)"
    }

    Write-Verbose "Encrypted credentials for '$EncryptedCredsName' loaded successfully."
    return $Credential
}