Files
powershell-scripts/winrm-https/SECURE_CREDENTIAL_MANAGEMENT.md
cproudlock 62c0c7bb06 Initial commit: Organized PowerShell scripts for ShopDB asset collection
Structure:
- asset-collection/: Local PC data collection scripts
- remote-execution/: WinRM remote execution scripts
- setup-utilities/: Configuration and testing utilities
- registry-backup/: GE registry backup scripts
- winrm-https/: WinRM HTTPS certificate setup
- docs/: Complete documentation

Each folder includes a README with detailed documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 10:57:54 -05:00

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"

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

  1. Always use SecureString for passwords

    $password = Read-Host "Password" -AsSecureString
    
  2. Use Get-Credential for domain accounts

    $cred = Get-Credential
    
  3. Store credentials in encrypted files or credential manager

    $cred | Export-Clixml -Path "C:\Secure\cred.xml"
    
  4. 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
    
  5. Clear sensitive variables after use

    $certPass = $null
    $cred = $null
    [System.GC]::Collect()
    

Don'ts

  1. Never hardcode passwords

    # BAD
    $password = "MyPassword123!"
    
  2. Never pass passwords as plaintext parameters

    # BAD
    .\Script.ps1 -Password "MyPassword"
    
  3. Never store passwords in environment variables

    # BAD
    $env:PASSWORD = "MyPassword"
    
  4. Never commit credentials to version control

    # BAD - Adding cred.xml to git
    
  5. 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:

  1. Testing/Development: Use interactive prompts
  2. Production Manual: Use encrypted files
  3. Scheduled Tasks: Use Windows Credential Manager
  4. 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.