HEMM Part 4: PowerShell Secure Credentials Save Once, Reuse Anywhere

🧠 TL;DR

Storing credentials securely in PowerShell is a nightmare people love to ignore — until it bites them in the middle of an automation task. These two functions (Set-SecureCredential and Get-SecureCredential) let you store creds once and reuse them safely across your scripts, without hardcoding passwords or getting stuck at prompts.


Overview

You’re automating tasks across servers. You’ve got scripts managing Active Directory, shares, scheduled tasks — the usual chaos. Some require alternate credentials. And what do most folks do?

  • Type Get-Credential into every script
  • Copy/paste creds into a $securePassword = '...' block (🙃)
  • Or worse — store plain-text passwords in a file

None of that scales, and all of it’s risky. So, these two functions fix it:

You can clone this from my git repo.


🧰 Before You Start

What these functions do:

  • Set-SecureCredential
    Prompts for a username and password, then saves it securely as an encrypted .secxml file in your common folder.
  • Get-SecureCredential
    Reads that .secxml file back in, and assigns it as a global variable using the alias you gave it.

What you need:

  • PowerShell
  • The common folder in your HEMM module
  • You running the function on the same machine and user profile that created it (that’s how Export-CliXml security works)

🔐 Security considerations

When it comes to storing credentials in PowerShell, Export-CliXml is the best native option — it encrypts creds using your current user and machine context, meaning the files can’t be decrypted by anyone else. That said, security is only as strong as your weakest link, and if your account password is weak tea, that encrypted file might as well be plain text. These credential files are obscured with .secxml extensions and aliased filenames (like HE-DEV_SCF.secxml), which helps avoid drawing attention — but that doesn’t mean you can skip good hygiene. Make sure the account used to create these creds follows a proper password policy. No P@ssword123. No reused secrets. If you’re automating admin tasks, act like one.

🧾The scripts

Set-SecureCredential

<#
.SYNOPSIS
    Creates Secure credentials and stores them to be used later.

.DESCRIPTION
    Prompts for credentials and saves them securely as an XML file under the "common" folder.
    These can then be recalled in other functions using the saved alias.

.PARAMETER Alias
    Optional alias to name the credential file. Defaults to 'SCF.secxml' if not provided.

.EXAMPLE
    Set-SecureCredential -Alias HE-DEV

.NOTES
    Author   : Ashley Hay-Ellis  
    Version  : 1.0  
    License  : MIT  
    Change History:
        2025-05-10 : Initial release
#>

function Set-SecureCredential {
    [CmdletBinding()]
    param (
        [string]$Alias
    )

    $FileName = if ($Alias) { "${Alias}_SCF.secxml" } else { "SCF.secxml" }
    $FilePath = ".\common\$FileName"

    if (Test-Path -Path $FilePath) {
        $response = Read-Host "File '$FilePath' already exists. Overwrite? (Y/N)"
        if ($response -notin @('Y','y')) {
            Write-Host "Credentials not updated." -ForegroundColor Yellow
            return
        }
    }

    $Credential = Get-Credential
    $Credential | Export-CliXml -Path $FilePath
    Write-Host "Credential saved to '$FilePath'" -ForegroundColor Green
}

Get-SecureCredential

<#
.SYNOPSIS
    Loads secure credentials from a saved file and stores them as a global variable.

.DESCRIPTION
    Reads credentials previously saved using Save-CredentialSecurely. 
    Imports the XML file and stores the result in a global variable named after the alias.

.PARAMETER Alias
    The alias used when saving the credential.

.EXAMPLE
    Get-SecureCredential -Alias HE-DEV

.NOTES
    Author   : Ashley Hay-Ellis  
    Version  : 1.1  
    License  : MIT  
    Change History:
        2025-05-10 : Initial release
        2025-05-11 : Standardised naming, improved error handling
#>

function Get-SecureCredential {
    [CmdletBinding()]
    param (
        [string]$Alias
    )

    $FileName = if ($Alias) { "${Alias}_SCF.secxml" } else { "SCF.secxml" }
    $FilePath = ".\common\$FileName"

    if (-not (Test-Path -Path $FilePath)) {
        Write-Warning "No credentials found for alias '$Alias'."
        Write-Host "Run Set-SecureCredential -Alias $Alias to create them." -ForegroundColor Yellow
        return
    }

    try {
        $Credential = Import-CliXml -Path $FilePath
        New-Variable -Name $Alias -Value $Credential -Scope Global -Force
        Write-Host "Credential loaded into variable '$Alias'" -ForegroundColor Green
    } catch {
        Write-Error "Failed to import credential from $FilePath"
    }
}

📦 How It Works

🧾 Step 1: Save your credentials

Set-SecureCredential -Alias HE-DEV

You’ll get prompted to enter creds. The function will save them as:

.\common\HE-DEV_SCF.secxml

🧾 Step 2: Load them in later

Get-SecureCredential -Alias HE-DEV

The function will:

  • Load the .secxml file
  • Store it in a global variable called $HE-DEV

So now in any function or script, you can use:

Invoke-Command -ComputerName SERVER01 -Credential $HE-DEV

Clean. Consistent. No prompts.


🛑 What This Doesn’t Do

  • It won’t let you use the creds on a different machine or user profile — this isn’t a vault
  • It won’t let you store multiple usernames under the same alias without overwriting
  • It won’t commit your .secxml files to Git unless you’re reckless (you did add them to .gitignore, right?)

✅ Final Thoughts

Credentials are always a pain — until you bake in a system to handle them. These two functions give you just enough control without having to build or deploy a full credential vault. For internal modules, small teams, or solo admins who care about doing things right, this setup hits the sweet spot between secure enough and actually usable.

Leave a Reply

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