Secure credential management ensures that sensitive secrets (such as administrative passwords, API keys, and connection strings) are never stored in plaintext within scripts or version control repositories. PowerShell protects sensitive data using System.Security.SecureString memory-encrypted objects, credential objects via Get-Credential, and modern vault orchestration using the Microsoft.PowerShell.SecretManagement module.
Interactive Authentication with Get-Credential
The Get-Credential cmdlet prompts an interactive user for a username and password, returning a System.Management.Automation.PSCredential object. In a PSCredential object, the username is accessible as plain text (.UserName), but the password is encrypted and stored in memory as a SecureString (.Password).
$cred = Get-Credential -UserName "CORP\admin_svc" -Message "Enter administrative credentials"
[PSCustomObject]@{
UserName = $cred.UserName
PasswordType = $cred.Password.GetType().Name
IsEncrypted = ($cred.Password -is [System.Security.SecureString])
}where:
Get-Credentiallaunches an authentication dialog or terminal prompt to collect login details.-UserNamepre-populates the username field, requiring the user to supply only the password.-Messagesets the administrative banner or prompt text.$cred.Passwordreturns the password encapsulated inside aSecureStringobject, preventing plaintext memory exposure.
./get-credential-demo.ps1UserName PasswordType IsEncrypted
-------- ------------ -----------
CORP\admin_svc SecureString TruePre-populating the Username
$cred = Get-Credential -UserName "admin" -Message "Enter password for deployment account"Using a Credential Object
Invoke-Command -ComputerName "SRV01" -Credential $cred -ScriptBlock { Get-Service }where -Credential accepts the PSCredential object for authenticating the remote connection.
Working with SecureString
A SecureString encrypts string data in memory, reducing the risk of the value being exposed accidentally (e.g., in a process dump or console log).
Converting Plain Text to SecureString
$securePassword = ConvertTo-SecureString -String "P@ssw0rd123!" -AsPlainText -Forcewhere:
-Stringis the plain text value to convert.-AsPlainTextis required to explicitly acknowledge you're converting from plain text.-Forceconfirms the (inherently risky) conversion without an additional prompt.
Converting SecureString Back to Plain Text
$plainText = [System.Net.NetworkCredential]::new("", $securePassword).PasswordEncrypting Data: ConvertTo-SecureString and ConvertFrom-SecureString
The ConvertTo-SecureString and ConvertFrom-SecureString cmdlets convert sensitive strings into encrypted data structures and back. By default, Windows encrypts these strings using the Windows Data Protection API (DPAPI), binding the ciphertext to the current Windows user profile on the local computer.
$securePass = ConvertTo-SecureString "ConfidentialToken2026!" -AsPlainText -Force
$encryptedCipher = ConvertFrom-SecureString -SecureString $securePass
$encryptedCipher.Substring(0, 48) + "..."
$decryptedPass = ConvertTo-SecureString $encryptedCipherwhere:
ConvertTo-SecureStringconverts plaintext into an encrypted in-memorySecureStringobject.-AsPlainTextinstructs the cmdlet to accept a standard string input.-Forceis required safety override acknowledging the risk of passing plaintext into memory.ConvertFrom-SecureStringserializes theSecureStringinto an encrypted hexadecimal ciphertext string using DPAPI.
./dpapi-encryption.ps101000000d08c9ddf0115d1118c7a00c04fc297eb01000000...Saving a SecureString to Disk
$securePassword | ConvertFrom-SecureString | Out-File -FilePath "C:\Secrets\password.txt"where ConvertFrom-SecureString encrypts the SecureString into a text representation, tied to the current user and machine.
$loadedSecure = Get-Content -Path "C:\Secrets\password.txt" | ConvertTo-SecureStringPersistent Secret Storage with SecretManagement
For scripts that need to securely store and retrieve secrets across sessions (without the machine/user limitations of DPAPI), the Microsoft.PowerShell.SecretManagement module provides a standardized interface backed by pluggable vaults.
To standardize secret retrieval across cloud platforms, local servers, and development machines, Microsoft provides two complementary modules:
Microsoft.PowerShell.SecretManagement: an abstraction layer providing unified cmdlets (Get-Secret,Set-Secret) that connect to any configured vault backend.Microsoft.PowerShell.SecretStore: a cross-platform, local, file-based vault extension that encrypts secrets using .NET cryptographic libraries.
Install-Module -Name Microsoft.PowerShell.SecretManagement -Scope CurrentUser
Install-Module -Name Microsoft.PowerShell.SecretStore -Scope CurrentUserRegistering a Vault
Register-SecretVault -Name "LocalVault" -ModuleName Microsoft.PowerShell.SecretStore -Defaultwhere:
-Nameis a friendly name for this vault.-Defaultmarks this vault as the default target for secret operations.
Storing a Secret with Set-Secret
Set-Secret -Name "DeployApiKey" -Secret "sk_live_abc123XYZ"where:
-Nameis the identifier used to retrieve the secret later.-Secretis the sensitive value to store (accepts strings,SecureString, orPSCredential).
Retrieving a Secret with Get-Secret
$apiKey = Get-Secret -Name "DeployApiKey" -AsPlainTextwhere -AsPlainText returns the secret as a plain string rather than a SecureString object.
Listing Stored Secrets
Get-SecretInfoName Type VaultName
---- ---- ---------
DeployApiKey String LocalVaultRemoving a Secret
Remove-Secret -Name "DeployApiKey"Complete Secret Store Example
Register-SecretVault -Name "LocalVault" -ModuleName "Microsoft.PowerShell.SecretStore" -DefaultVault
$apiToken = "sk_live_9847120938401923"
Set-Secret -Name "ProductionApiKey" -Secret $apiToken -Vault "LocalVault"
Get-SecretInfo -Vault "LocalVault" | Select-Object -Property Name, Type, VaultName
$retrievedToken = Get-Secret -Name "ProductionApiKey" -AsPlainText
Write-Output "Retrieved secret length: $($retrievedToken.Length)"where:
Register-SecretVaultconnects a vault implementation module to theSecretManagementframework.Set-Secretsecurely stores a string,SecureString,PSCredential, or hashtable into the specified vault.Get-SecretInfodiscovers stored secret names and metadata without exposing the underlying secret payloads.Get-Secretfetches the decrypted secret payload from the vault backend.-AsPlainTextdecrypts the secret directly to a standard string for REST endpoints or binaries.
./secret-store-demo.ps1Name Type VaultName
---- ---- ---------
ProductionApiKey String LocalVault
Retrieved secret length: 24Practical Usage
Secure Script Authentication Without Hardcoded Credentials
A common pattern uses SecretManagement to retrieve credentials at runtime, keeping automation scripts free of hardcoded secrets entirely.
$apiKey = Get-Secret -Name "DeployApiKey" -AsPlainText
$headers = @{
Authorization = "Bearer $apiKey"
}
Invoke-RestMethod -Uri "https://api.example.com/deploy" -Method Post -Headers $headerswhere:
Get-Secretretrieves the API key at runtime from the encrypted local vault.$headersbuilds an authorization header using the retrieved secret, never written to disk in plain text.
./deploy-with-secret.ps1Deployment triggered successfully.Non-Interactive Script Authentication Using Exported DPAPI Credentials
Scheduled tasks and continuous automation scripts frequently need to run without human intervention while avoiding hardcoded credentials in the script body.
$credPath = "C:\SecureConfig\svc_backup.cred"
$credential = Get-Credential -UserName "CORP\svc_backup" -Message "Set Service Account Password"
$credential.Password | ConvertFrom-SecureString | Set-Content -Path $credPath
Write-Output "Encrypted credential file generated at $credPath"./setup-headless-credential.ps1Encrypted credential file generated at C:\SecureConfig\svc_backup.cred$credPath = "C:\SecureConfig\svc_backup.cred"
$serviceUser = "CORP\svc_backup"
if (-not (Test-Path -Path $credPath)) {
throw "Credential file missing at $credPath!"
}
$encryptedData = Get-Content -Path $credPath
$securePassword = ConvertTo-SecureString $encryptedData
$runtimeCred = [System.Management.Automation.PSCredential]::new($serviceUser, $securePassword)
$session = New-PSSession -ComputerName "Server01" -Credential $runtimeCred
[PSCustomObject]@{
TargetServer = $session.ComputerName
SessionState = $session.State
ConnectedAs = $runtimeCred.UserName
}
Remove-PSSession -Session $sessionwhere:
[System.Management.Automation.PSCredential]::new()is the strongly-typed constructor creating aPSCredentialobject from a username andSecureString.Test-Pathvalidates that the credential file is accessible before initiating the connection.New-PSSessionconsumes the rehydratedPSCredentialto establish an authenticated remote session without displaying the password.
./unattended-script.ps1TargetServer SessionState ConnectedAs
------------ ------------ -----------
Server01 Opened CORP\svc_backup