15 KiB
Secure Credential Management for WinRM HTTPS
This guide covers secure methods for handling passwords and credentials in PowerShell, avoiding plaintext passwords in scripts and command history.
Never Do This
# BAD - Password visible in script and command history
$password = "MyPassword123!"
$certPass = ConvertTo-SecureString "MyPassword123!" -AsPlainText -Force
Problems:
- Password stored in plaintext in script files
- Visible in PowerShell command history
- Can be read by anyone with file access
- Logged in transcripts and logs
Secure Methods
Method 1: Interactive Prompt (Most Secure for Manual Use)
For certificate password:
# PowerShell will prompt securely (characters are hidden)
$certPass = Read-Host "Enter certificate password" -AsSecureString
# Confirm password
$certPassConfirm = Read-Host "Confirm certificate password" -AsSecureString
# Verify they match (optional but recommended)
$pwd1 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPass)
)
$pwd2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPassConfirm)
)
if ($pwd1 -ne $pwd2) {
Write-Error "Passwords do not match!"
exit 1
}
# Use the secure password
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
For domain credentials:
# Get-Credential shows a secure dialog
$cred = Get-Credential -Message "Enter domain admin credentials"
# Use the credentials
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
Method 2: Encrypted File Storage (For Automation)
Store credentials securely:
# One-time setup: Save encrypted credentials
# This creates an encrypted file that only YOUR USER ACCOUNT on THIS COMPUTER can decrypt
# For domain credentials
$cred = Get-Credential -Message "Enter domain admin credentials"
$cred | Export-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
# For certificate password
$certPass = Read-Host "Enter certificate password" -AsSecureString
$certPass | Export-Clixml -Path "C:\Secure\cert-password.xml"
Use stored credentials:
# Import encrypted credentials
$cred = Import-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
$certPass = Import-Clixml -Path "C:\Secure\cert-password.xml"
# Use in scripts
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
Important notes:
- Encrypted files can ONLY be decrypted by the same user on the same computer
- Safe to store in version control (but not recommended)
- Won't work if script runs as different user (e.g., scheduled task with service account)
- Won't work on different computer
Method 3: Windows Credential Manager (Best for Scheduled Tasks)
Store credentials in Windows Credential Manager:
# Install CredentialManager module (one time)
Install-Module -Name CredentialManager -Force -Scope CurrentUser
# Store credentials
New-StoredCredential -Target "ShopfloorAdmin" `
-Username "DOMAIN\serviceaccount" `
-Password "password" `
-Type Generic `
-Persist LocalMachine
Retrieve and use:
# Import module
Import-Module CredentialManager
# Get stored credential
$cred = Get-StoredCredential -Target "ShopfloorAdmin"
# Use in script
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
Advantages:
- Works with scheduled tasks
- Can be used by service accounts
- Centralized management
- Encrypted by Windows
Method 4: Environment Variables (For Automation Contexts)
Set environment variable (for current session):
# Not recommended for passwords, but okay for non-sensitive config
$env:WINRM_DOMAIN = "logon.ds.ge.com"
$env:WINRM_CERT_PATH = "C:\Certs\wildcard.pfx"
# Use in scripts
.\Setup-WinRM-HTTPS.ps1 `
-CertificatePath $env:WINRM_CERT_PATH `
-Domain $env:WINRM_DOMAIN
** Do NOT use for passwords:**
# BAD - Environment variables are not secure for passwords
$env:CERT_PASSWORD = "MyPassword" # DON'T DO THIS
Method 5: Azure Key Vault (Enterprise)
For production environments with Azure:
# Install Az modules
Install-Module -Name Az.KeyVault -Force
# Connect to Azure
Connect-AzAccount
# Store secret
$secretPassword = ConvertTo-SecureString "YourPassword" -AsPlainText -Force
Set-AzKeyVaultSecret -VaultName "YourKeyVault" `
-Name "ShopfloorCertPassword" `
-SecretValue $secretPassword
# Retrieve secret
$secret = Get-AzKeyVaultSecret -VaultName "YourKeyVault" `
-Name "ShopfloorCertPassword" -AsPlainText
$certPass = ConvertTo-SecureString $secret -AsPlainText -Force
# Use in script
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
🔐 Recommended Approaches by Scenario
Scenario 1: Manual Testing (You Running Scripts)
Use: Interactive Prompts
# Let scripts prompt you
.\Generate-WildcardCert.ps1
# (Will prompt for password)
$cred = Get-Credential
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
Why: Simple, secure, no storage needed
Scenario 2: Scheduled Task (Same User)
Use: Encrypted File Storage
# One-time setup (run as the user who will run scheduled task)
$cred = Get-Credential
$cred | Export-Clixml -Path "C:\Secure\shopfloor-cred.xml"
# In scheduled task script
$cred = Import-Clixml -Path "C:\Secure\shopfloor-cred.xml"
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile "C:\Scripts\winrm-https\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
Why: Secure, works automatically, tied to specific user
Scenario 3: Scheduled Task (Service Account)
Use: Windows Credential Manager
# Setup (run as service account or with appropriate permissions)
Install-Module CredentialManager -Force
New-StoredCredential -Target "ShopfloorAdmin" `
-Username "DOMAIN\svc-shopfloor" `
-Password "ServiceAccountPassword" `
-Type Generic `
-Persist LocalMachine
# In scheduled task script
Import-Module CredentialManager
$cred = Get-StoredCredential -Target "ShopfloorAdmin"
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile "C:\Scripts\winrm-https\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
Why: Works across users, secure, manageable
Scenario 4: Enterprise Production
Use: Azure Key Vault or HashiCorp Vault
# Retrieve from Key Vault
$certPass = Get-AzKeyVaultSecret -VaultName "ProdVault" `
-Name "WinRMCertPassword" -AsPlainText |
ConvertTo-SecureString -AsPlainText -Force
$cred = Get-AzKeyVaultSecret -VaultName "ProdVault" `
-Name "ShopfloorAdminCred" -AsPlainText |
ConvertTo-SecureString -AsPlainText -Force
# Use in deployment
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
Why: Centralized, audited, compliant, scalable
📝 Updated Script Examples
Generate Certificate (Secure)
# Interactive (recommended for manual use)
.\Generate-WildcardCert.ps1
# Will prompt: "Enter password for PFX file:"
# With pre-stored password (for automation)
$certPass = Import-Clixml -Path "C:\Secure\cert-password.xml"
.\Generate-WildcardCert.ps1 -Password $certPass
Setup WinRM HTTPS (Secure)
# Interactive (recommended for testing)
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
-Domain "logon.ds.ge.com"
# Will prompt: "Enter certificate password:"
# With stored password (for automation)
$certPass = Import-Clixml -Path "C:\Secure\cert-password.xml"
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
# With Credential Manager (for scheduled tasks)
Import-Module CredentialManager
$certPassPlain = (Get-StoredCredential -Target "CertPassword").Password
$certPass = ConvertTo-SecureString $certPassPlain -AsPlainText -Force
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
Remote Collection (Secure)
# Interactive (recommended for manual use)
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com"
# Will prompt: "Enter credentials for remote computer access:"
# With stored credentials (for automation)
$cred = Import-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
# With Credential Manager (for scheduled tasks)
Import-Module CredentialManager
$cred = Get-StoredCredential -Target "ShopfloorAdmin"
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
🛡️ Security Best Practices
Do's
-
Always use SecureString for passwords
$password = Read-Host "Password" -AsSecureString -
Use Get-Credential for domain accounts
$cred = Get-Credential -
Store credentials in encrypted files or credential manager
$cred | Export-Clixml -Path "C:\Secure\cred.xml" -
Set proper file permissions on credential files
# Only allow current user access $acl = Get-Acl "C:\Secure\cred.xml" $acl.SetAccessRuleProtection($true, $false) $rule = New-Object System.Security.AccessControl.FileSystemAccessRule( $env:USERNAME, "FullControl", "Allow" ) $acl.AddAccessRule($rule) Set-Acl "C:\Secure\cred.xml" $acl -
Clear sensitive variables after use
$certPass = $null $cred = $null [System.GC]::Collect()
Don'ts
-
Never hardcode passwords
# BAD $password = "MyPassword123!" -
Never pass passwords as plaintext parameters
# BAD .\Script.ps1 -Password "MyPassword" -
Never store passwords in environment variables
# BAD $env:PASSWORD = "MyPassword" -
Never commit credentials to version control
# BAD - Adding cred.xml to git -
Never log passwords
# BAD Write-Host "Password: $password" Add-Content "log.txt" "Password: $password"
Setting Up Secure Credential Storage
Step 1: Create Secure Directory
# Create directory for secure files
$securePath = "C:\Secure"
New-Item -Path $securePath -ItemType Directory -Force
# Set permissions (only current user)
$acl = Get-Acl $securePath
$acl.SetAccessRuleProtection($true, $false)
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$env:USERNAME, "FullControl", "Allow"
)
$acl.AddAccessRule($rule)
Set-Acl $securePath $acl
Write-Host "Secure directory created: $securePath"
Step 2: Store Certificate Password
# Prompt for certificate password
Write-Host "Setting up certificate password storage..." -ForegroundColor Cyan
$certPass = Read-Host "Enter certificate password" -AsSecureString
$certPassConfirm = Read-Host "Confirm certificate password" -AsSecureString
# Verify match
$pwd1 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPass)
)
$pwd2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPassConfirm)
)
if ($pwd1 -ne $pwd2) {
Write-Error "Passwords do not match!"
exit 1
}
# Save encrypted
$certPass | Export-Clixml -Path "C:\Secure\cert-password.xml"
Write-Host "Certificate password saved securely to C:\Secure\cert-password.xml" -ForegroundColor Green
Step 3: Store Domain Credentials
# Prompt for domain credentials
Write-Host "Setting up domain admin credentials..." -ForegroundColor Cyan
$cred = Get-Credential -Message "Enter domain admin credentials for shopfloor PCs"
# Save encrypted
$cred | Export-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
Write-Host "Domain credentials saved securely to C:\Secure\shopfloor-admin-cred.xml" -ForegroundColor Green
Step 4: Create Helper Script
# Create script to load credentials
@'
# Load-SecureCredentials.ps1
# Helper script to load stored credentials
function Get-CertificatePassword {
if (Test-Path "C:\Secure\cert-password.xml") {
return Import-Clixml -Path "C:\Secure\cert-password.xml"
}
else {
Write-Warning "Certificate password not found. Please run setup."
return Read-Host "Enter certificate password" -AsSecureString
}
}
function Get-DomainCredential {
if (Test-Path "C:\Secure\shopfloor-admin-cred.xml") {
return Import-Clixml -Path "C:\Secure\shopfloor-admin-cred.xml"
}
else {
Write-Warning "Domain credentials not found. Please run setup."
return Get-Credential -Message "Enter domain admin credentials"
}
}
Export-ModuleMember -Function Get-CertificatePassword, Get-DomainCredential
'@ | Out-File -FilePath ".\Load-SecureCredentials.ps1" -Encoding UTF8
Write-Host "Helper script created: .\Load-SecureCredentials.ps1" -ForegroundColor Green
Step 5: Use in Scripts
# Import helper
. .\Load-SecureCredentials.ps1
# Get stored credentials
$certPass = Get-CertificatePassword
$cred = Get-DomainCredential
# Use in operations
.\Setup-WinRM-HTTPS.ps1 -CertificatePath ".\wildcard.pfx" `
-CertificatePassword $certPass -Domain "logon.ds.ge.com"
.\Invoke-RemoteAssetCollection-HTTPS.ps1 `
-HostnameListFile ".\shopfloor-hostnames.txt" `
-Domain "logon.ds.ge.com" `
-Credential $cred
Summary Comparison
| Method | Security | Ease of Use | Automation | Cross-User | Enterprise |
|---|---|---|---|---|---|
| Interactive Prompt | |||||
| Encrypted File | |||||
| Credential Manager | |||||
| Azure Key Vault | |||||
| Plaintext (DON'T) |
🎯 Recommendation
For your shopfloor PC scenario:
- Testing/Development: Use interactive prompts
- Production Manual: Use encrypted files
- Scheduled Tasks: Use Windows Credential Manager
- Enterprise Scale: Consider Azure Key Vault
Start with Method 1 (Interactive Prompts) for testing, then move to Method 2 (Encrypted Files) or Method 3 (Credential Manager) for production automation.