There are secrets that can be deadly.
Here we’re not going to talk about this kind 😊 But that doesn’t mean it’s not important.
It happens quite often that a script you need to run needs access to a resource, and for this you need to provide a secret. It might be a password, a token, whatever.
The easy way is obviously to have them in the script as variables. Is that a good solution?
If you did not answer NO THAT’S HORRIBLE… please change your answer until you do.
Ok so you don’t want to leave it lying around in a script. You can ask at runtime, like this:
$token = Read-Host -Prompt "Please enter the connection token:" -AsSecureString
That’s definitely not as bad. But the follow up problem is, the user needs to type (or, most probably, copy-paste) the secret every time they run the script. Where do the users store their secrets? Are you nudging them to store it in a notepad file for convenience?
In order to keep our systems safe, we need a way that is both secure and convenient.
That’s why using the Windows Credential Manager is a much, much better way. The users only have to recover the secret once, and then they have it stored in a safe way.
Here’s an example of how you can save the secret in Windows Credential manager. It uses the CredentialManager module.
# === DO NOT SAVE THIS SCRIPT ===
# How to save a secret
# PREREQUISITE:
# Install-Module CredentialManager -Scope CurrentUser
$secretName = 'myAzureServiceBusToken' # or whatever
New-StoredCredential -Target $secretName -Username 'myusername' -Pass 'mysecret' -Persist LocalMachine
And here’s how you can recover and use it:
# How to use the secret
# PREREQUISITE:
# Install-Module CredentialManager -Scope CurrentUser
$secretName = 'myAzureServiceBusToken' # or whatever
$cred=Get-StoredCredential -Target $secretName
$userName = $cred.UserName
$secret = $cred.GetNetworkCredential().Password
# do whatever you need with the secret
Just for completeness, here’s an example of how to call a REST API with this secret. I imagine that’s one of the most common use cases.
#
# Source: DotJim blog (https://dandraka.com)
# Jim Andrakakis, April 2024
#
# PREREQUISITES:
# 1. Install-Module CredentialManager -Scope CurrentUser
# 2. New-StoredCredential -Target 'myRESTAPICredential' -Username 'myusername' -Pass 'mysecret' -Persist LocalMachine
# === Constants ===
$uri = 'https://myhost/myapi'
$credName = 'myRESTAPICredential'
$fileName = 'C:\somepath\data.json'
# === Constants ===
$cred=Get-StoredCredential -Target $credName
$pair="$($cred.UserName):$($cred.GetNetworkCredential().Password)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$headers = @{
Authorization = $basicAuthValue;
ContentType = 'application/json';
Accept = 'application/json'
}
try {
$resp = Invoke-WebRequest -UseBasicParsing -Uri $uri -Headers $headers -Method Post -InFile $fileName
}
catch {
$errorMsg = "Error sending file '$fileName', exception in line $($_.InvocationInfo.ScriptLineNumber): $_.Exception.Message $_"
Write-Warning $errorMsg
}