# 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 ```powershell # 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 # 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:** ```powershell # 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:** ```powershell # 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:** ```powershell # 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:** ```powershell # 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:** ```powershell # 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):** ```powershell # 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:** ```powershell # 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:** ```powershell # 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** ```powershell # 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** ```powershell # 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** ```powershell # 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** ```powershell # 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) ```powershell # 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) ```powershell # 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) ```powershell # 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** ```powershell $password = Read-Host "Password" -AsSecureString ``` 2. **Use Get-Credential for domain accounts** ```powershell $cred = Get-Credential ``` 3. **Store credentials in encrypted files or credential manager** ```powershell $cred | Export-Clixml -Path "C:\Secure\cred.xml" ``` 4. **Set proper file permissions on credential files** ```powershell # 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** ```powershell $certPass = $null $cred = $null [System.GC]::Collect() ``` ### Don'ts 1. **Never hardcode passwords** ```powershell # BAD $password = "MyPassword123!" ``` 2. **Never pass passwords as plaintext parameters** ```powershell # BAD .\Script.ps1 -Password "MyPassword" ``` 3. **Never store passwords in environment variables** ```powershell # BAD $env:PASSWORD = "MyPassword" ``` 4. **Never commit credentials to version control** ```powershell # BAD - Adding cred.xml to git ``` 5. **Never log passwords** ```powershell # BAD Write-Host "Password: $password" Add-Content "log.txt" "Password: $password" ``` --- ## Setting Up Secure Credential Storage ### Step 1: Create Secure Directory ```powershell # 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 ```powershell # 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 ```powershell # 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 ```powershell # 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 ```powershell # 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.**