<# .SYNOPSIS Prompts for Historian credentials and persists them to an encrypted file under the current user's profile, or loads previously-saved credentials into the current session's HISTORIAN_USER and HISTORIAN_PASSWORD environment variables. .DESCRIPTION Persistence uses Windows DPAPI via Export-Clixml. The resulting XML can only be decrypted by the same user account on the same machine. The file is saved outside the repository (default: $env:USERPROFILE\.histsdk\credentials.xml) so it cannot be accidentally committed. The integration tests in this repo read HISTORIAN_USER and HISTORIAN_PASSWORD from the process environment. Run this script with -Load before launching `dotnet test` to inject the saved credentials into the current session. .PARAMETER Load Read previously-saved credentials and set $env:HISTORIAN_USER + $env:HISTORIAN_PASSWORD in the current PowerShell session. The variables persist for the lifetime of this session only - re-run with -Load in a new shell. .PARAMETER Clear Delete the saved credentials file. Subsequent -Load calls will fail until credentials are re-saved. .PARAMETER Path Override the default storage path. Useful for keeping multiple credential sets side-by-side (e.g. local-vs-remote). .PARAMETER UserName Skip the username prompt and use the supplied value. Password is still prompted interactively. Convenient for scripted re-saves where only the password rotates. .EXAMPLE PS> .\scripts\Set-HistorianCredentials.ps1 Prompts for username + password, saves both to %USERPROFILE%\.histsdk\credentials.xml. .EXAMPLE PS> .\scripts\Set-HistorianCredentials.ps1 -Load Loads the saved credentials into HISTORIAN_USER and HISTORIAN_PASSWORD for this session. .EXAMPLE PS> .\scripts\Set-HistorianCredentials.ps1 -Clear Deletes the saved credentials file. #> [CmdletBinding(DefaultParameterSetName = 'Save')] param( [Parameter(ParameterSetName = 'Load')] [switch]$Load, [Parameter(ParameterSetName = 'Clear')] [switch]$Clear, [string]$Path = (Join-Path $env:USERPROFILE '.histsdk\credentials.xml'), [Parameter(ParameterSetName = 'Save')] [string]$UserName ) $ErrorActionPreference = 'Stop' function ConvertTo-PlainText { param([Parameter(Mandatory)][securestring]$SecureString) $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) try { [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } if ($Clear) { if (Test-Path $Path) { Remove-Item -LiteralPath $Path -Force Write-Host "Deleted $Path" } else { Write-Host "Nothing to clear (file does not exist): $Path" } return } if ($Load) { if (-not (Test-Path $Path)) { throw "No saved credentials at $Path. Run this script without -Load to save them first." } $cred = Import-Clixml -LiteralPath $Path if ($cred -isnot [System.Management.Automation.PSCredential]) { throw "File at $Path is not a PSCredential - re-save with this script." } $env:HISTORIAN_USER = $cred.UserName $env:HISTORIAN_PASSWORD = ConvertTo-PlainText $cred.Password Write-Host "Loaded credentials for '$($cred.UserName)' from $Path." Write-Host "HISTORIAN_USER and HISTORIAN_PASSWORD are set for this PowerShell session." return } # Save mode (default). $dir = Split-Path -Parent $Path if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null } if ([string]::IsNullOrWhiteSpace($UserName)) { $suggested = "$env:COMPUTERNAME\$env:USERNAME" $UserName = Read-Host "Historian user [$suggested]" if ([string]::IsNullOrWhiteSpace($UserName)) { $UserName = $suggested } } $securePassword = Read-Host "Historian password for '$UserName'" -AsSecureString $cred = New-Object System.Management.Automation.PSCredential($UserName, $securePassword) $cred | Export-Clixml -LiteralPath $Path Write-Host "" Write-Host "Saved credentials for '$UserName' to $Path" Write-Host "(DPAPI-encrypted; decryptable only by '$env:USERNAME' on '$env:COMPUTERNAME'.)" Write-Host "" Write-Host "Run '.\scripts\Set-HistorianCredentials.ps1 -Load' in any new session to inject" Write-Host "the credentials into HISTORIAN_USER and HISTORIAN_PASSWORD."