PowerShell: Legacy Methods to Save Credentials

PowerShell version 7 or later may already have facilities to store and retrieve credentials without special scripting. As there are plenty of systems ‘in the wild’ without this special apparatus. Perhaps, snippets below would be useful to deal with this type of information.

#####################################################
# Method 1: saving credentials as an XML file
#####################################################

$path='c:\scripts'
$xmlCredFile='$path\Credentials\$userID`.xml'

function confirmation($content,$testValue="I confirm",$maxAttempts=3){
    $confirmed=$false;
    $attempts=0;        
    $content|write-host
    write-host "Please review this content for accuracy.`r`n"
    while ($attempts -le $maxAttempts){
        if($attempts++ -ge $maxAttempts){
            write-host "A maximum number of attempts have reached. No confirmations received!`r`n"
            break;
            }
        $userInput = Read-Host -Prompt "Please type in this value => $testValue <= to confirm";
        if ($userInput.ToLower() -ne $testValue.ToLower()){
            cls;
            $content|write-host
            write-host "Attempt number $attempts of $maxAttempts`: $userInput does not match $testValue. Try again..`r`n"
            }else{
                $confirmed=$true;
                write-host "Confirmed!`r`n";
                break;
                }
        }
    return $confirmed;
}

function validateCredential($cred){
    # Initialize variables
    $authenticated = $domainRoot = $username = $password = $null

    # Checkpoint
    If($cred -eq $null){
        write-warning "No credentials detected."
        return $false
    }    

    # Perform validation
    Try{
        # Obtain username and password
        $username = $cred.username
        $password = $cred.GetNetworkCredential().password
  
        # Get Domain
        $domainDistinguishedName=([ADSI]'').distinguishedName
        if ($domainDistinguishedName -eq $null){
            write-warning "Unable to contact domain controller(s)."
            return $false
            }
        $domainRoot = "LDAP://" + $domainDistinguishedName        
        $authenticated = New-Object System.DirectoryServices.DirectoryEntry($domainRoot,$username,$password)
        }
    Catch{
        $_.Exception.Message
        Write-Warning "Unable to bind given credential to the domain"
        return $false        
        }
    
    If ($authenticated.name -ne $null){
        Write-Host "Authentication successful"
        return $true
    }
    Else{
        Write-Warning "Unable to authenticate"
        return $false
    }

}

function saveCredentialToXml($path,$credential){    
    $validCredential=validateCredential $credential
    if ($validCredential){
        $userId=$credential.UserName
        $credentialFolder="$path\Credentials"
        $credentialFile="$credentialFolder\"+"$userID`.xml"
        $fileExists=[System.IO.File]::Exists($credentialFile)
        If (!$fileExists){
            # Create folder if it doesn't exist
            if(!(Test-Path -Path $credentialsFolder)){New-Item -path $credentialsFolder -type directory;}       
            $credential | Export-Clixml $credentialFile;
            write-host "$credentialFile has been saved for user $userId."
            return $True
            }else{
                write-host "$credentialFile already exists. Do you want to over-write it?"
                $userConfirm=confirmation "Overwrite $credentialFile?"
                if ($userConfirm){
                    Remove-Item $credentialFile -Force
                    $credential | Export-Clixml $credentialFile;
                    write-host "$credentialFile has been overwritten for user $userId."
                    return $true
                    }
                else{
                    write-host "No credential files were changed."
                    return $false
                    }
                }
    }else{
        write-warning "Invalid credential."
        return $false
        }
}

function getCredentialfromXML($file){
    $fileContent=get-content $file  
    if ($fileContent){      
        # Import stored XML credential
        $xmlCred=Import-clixml $file
        if ($xmlCred){
            $runasUser="$env:USERDOMAIN\"+$xmlCred.GetNetworkCredential().username
            $runasPassword=ConvertTo-securestring $xmlCred.GetNetworkCredential().password -AsPlainText -Force
            $runasCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $runasUser,$runasPassword
            if (validateCredential $runasCred){
                return $runasCred
                }
            # Start-Process powershell.exe -Credential $runasCred -NoNewWindow -ArgumentList "Start-Process powershell.exe -Verb runAs $PSCommandPath"
            }
        else{
            $newCred=get-credential
            if(saveCredentialToXml $path $newCred){
                return $newCred
                }
            }
    }else{
        "$file NOT found...`n";
        return $false
        }
}

$credFromXml=getCredentialfromXML $xmlCredFile

# $psCommands='ping google.com'
# Start-Process powershell.exe -Credential $credFromXml -NoNewWindow -ArgumentList "Start-Process powershell.exe -Verb runAs $psCommands"

###############################################################
# Method 2: saving credentials as an encrypted password file
###############################################################

# Save password as an encrypted file
$credentialFile='C:\Scripts\encryptedCredential.csv'
New-Item -ItemType Directory -Force -Path c:\scripts
$credential = Get-Credential
$encryptedPassword=$credential.password|ConvertFrom-SecureString
#$encryptedPassword=ConvertTo-SecureString $credential.GetNetworkCredential().password -AsPlainText -Force
$credentialObject=New-Object -TypeName PSObject -Property (@{username=$($credential.username);encryptedPassword=$encryptedPassword})
$credentialObject|export-csv -notypeinformation $credentialFile

# Retrieve password from encrypted credential file
$encryptedCredential=Import-Csv $credentialFile
$userName=$encryptedCredential.Username
$securedPassword = $encryptedCredential.encryptedPassword|ConvertTo-SecureString
$credential = New-Object System.Management.Automation.PsCredential($userName, $securedPassword)


###############################################################################################
# Method 3: saving credentials as an environmental variable localized to certain logon sessions
###############################################################################################

function confirmation($content,$testValue="I confirm",$maxAttempts=3){
    $confirmed=$false;
    $attempts=0;        
    $content|write-host
    write-host "Please review this content for accuracy.`r`n"
    while ($attempts -le $maxAttempts){
        if($attempts++ -ge $maxAttempts){
            write-host "A maximum number of attempts have reached. No confirmations received!`r`n"
            break;
            }
        $userInput = Read-Host -Prompt "Please type in this value => $testValue <= to confirm";
        if ($userInput.ToLower() -ne $testValue.ToLower()){
            cls;
            $content|write-host
            write-host "Attempt number $attempts of $maxAttempts`: $userInput does not match $testValue. Try again..`r`n"
            }else{
                $confirmed=$true;
                write-host "Confirmed!`r`n";
                break;
                }
        }
    return $confirmed;
}

function validateCredential($cred){
    # Initialize variables
    $authenticated = $domainRoot = $username = $password = $null

    # Checkpoint
    If($cred -eq $null){
        write-warning "No credentials detected."
        return $false
    }    

    # Perform validation
    Try{
        # Obtain username and password
        $username = $cred.username
        $password = $cred.GetNetworkCredential().password
  
        # Get Domain
        $domainDistinguishedName=([ADSI]'').distinguishedName
        if ($domainDistinguishedName -eq $null){
            write-warning "Unable to contact domain controller(s)."
            return $false
            }
        $domainRoot = "LDAP://" + $domainDistinguishedName        
        $authenticated = New-Object System.DirectoryServices.DirectoryEntry($domainRoot,$username,$password)
        }
    Catch{
        $_.Exception.Message
        Write-Warning "Unable to bind given credential to the domain"
        return $false        
        }
    
    If ($authenticated.name -ne $null){
        Write-Host "Authentication successful"
        return $true
    }
    Else{
        Write-Warning "Unable to authenticate"
        return $false
    }

}

function getCredFromEnv{
    # Load username and password from environmental variables
    if (Test-Path env:ServiceAccount) {
      $serviceAccount = $env:ServiceAccount
    }else{
        return $false
        Write-Warning 'Unable to retrieve `$env:ServiceAccount'
        }
    if (Test-Path env:ServiceAccountPassword) {
      $serviceAccountPassword = $env:ServiceAccountPassword
    }else{
        return $false
        Write-Warning 'Unable to retrieve `$env:ServiceAccountPassword'
        }
    # This assumes the password was stored as as hash
    $securedPassword = $serviceAccountPassword|ConvertTo-SecureString
    $credential = New-Object System.Management.Automation.PsCredential($serviceAccount, $securedPassword)

    if (validateCredential $credential){
        write-host 'Valid credentials retrieved from Environment. Program now proceeds...'
        return $credential
    }else{
        $credential = Get-Credential        
        if (validateCredential $credential){
            $username=$credential.UserName
            $encryptedPassword=$credential.Password|ConvertFrom-SecureString
            $saveToEnv=confirmation -content 'I want to save the $username account as an Environmental variable.'
            if ($saveToEnv){
                $env:ServiceAccount = $username
                $env:ServiceAccountPassword = $encryptedPassword
                }else{
                    write-host 'No confirmations received to save this credential.'
                    }
            return $credential
            }else{
                write-warning 'Invalid credential.'
                return $false
                }       
    }
}

$credFromXml=getCredFromEnv

Leave a Reply

Your email address will not be published. Required fields are marked *