Add support for SecretManagement and SecretStore providers, enhance configuration data handling, and introduce new utility functions
This commit is contained in:
101
Private/ConvertFrom-ConfigurationDataSecretValue.ps1
Normal file
101
Private/ConvertFrom-ConfigurationDataSecretValue.ps1
Normal file
@@ -0,0 +1,101 @@
|
||||
function ConvertFrom-ConfigurationDataSecretValue {
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[AllowNull()]
|
||||
$Secret,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateSet("credential", "securestring", "string")]
|
||||
[string]
|
||||
$ExpectedType,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]
|
||||
$Name = "",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]
|
||||
$UserName = ""
|
||||
)
|
||||
|
||||
if($null -eq $Secret){
|
||||
throw "Secret [$Name] was not found."
|
||||
}
|
||||
|
||||
if($ExpectedType -eq "credential"){
|
||||
if($Secret -is [System.Management.Automation.PSCredential]){
|
||||
return $Secret
|
||||
}
|
||||
|
||||
if($Secret -is [System.Collections.IDictionary]){
|
||||
if([string]::IsNullOrWhiteSpace($UserName) -and $Secret.Contains("UserName")){
|
||||
$UserName = [string]$Secret["UserName"]
|
||||
}
|
||||
|
||||
if($Secret.Contains("Password")){
|
||||
return ConvertTo-ConfigurationDataCredential -UserName $UserName -Password $Secret["Password"]
|
||||
}
|
||||
}
|
||||
|
||||
if($Secret.PSObject.Properties["UserName"] -and [string]::IsNullOrWhiteSpace($UserName)){
|
||||
$UserName = [string]$Secret.UserName
|
||||
}
|
||||
|
||||
if($Secret.PSObject.Properties["Password"]){
|
||||
return ConvertTo-ConfigurationDataCredential -UserName $UserName -Password $Secret.Password
|
||||
}
|
||||
|
||||
if([string]::IsNullOrWhiteSpace($UserName)){
|
||||
throw "Secret [$Name] cannot be converted to [PSCredential] because no username was provided."
|
||||
}
|
||||
|
||||
return ConvertTo-ConfigurationDataCredential -UserName $UserName -Password $Secret
|
||||
}
|
||||
|
||||
if($ExpectedType -eq "securestring"){
|
||||
if($Secret -is [System.Security.SecureString]){
|
||||
return $Secret
|
||||
}
|
||||
|
||||
if($Secret -is [System.Management.Automation.PSCredential]){
|
||||
return $Secret.Password
|
||||
}
|
||||
|
||||
if($Secret -is [System.Collections.IDictionary] -and $Secret.Contains("Password")){
|
||||
$Secret = $Secret["Password"]
|
||||
}elseif($Secret.PSObject.Properties["Password"]){
|
||||
$Secret = $Secret.Password
|
||||
}
|
||||
|
||||
if($Secret -is [System.Security.SecureString]){
|
||||
return $Secret
|
||||
}
|
||||
|
||||
return ConvertTo-SecureString -String ([string]$Secret) -AsPlainText -Force
|
||||
}
|
||||
|
||||
if($Secret -is [System.Management.Automation.PSCredential]){
|
||||
return $Secret.GetNetworkCredential().Password
|
||||
}
|
||||
|
||||
if($Secret -is [System.Security.SecureString]){
|
||||
$Pointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($Secret)
|
||||
try {
|
||||
return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($Pointer)
|
||||
}
|
||||
finally {
|
||||
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($Pointer)
|
||||
}
|
||||
}
|
||||
|
||||
if($Secret -is [System.Collections.IDictionary] -and $Secret.Contains("Password")){
|
||||
return [string]$Secret["Password"]
|
||||
}
|
||||
|
||||
if($Secret.PSObject.Properties["Password"]){
|
||||
return [string]$Secret.Password
|
||||
}
|
||||
|
||||
return [string]$Secret
|
||||
}
|
||||
63
Private/ConvertTo-PowerShellDataFileText.ps1
Normal file
63
Private/ConvertTo-PowerShellDataFileText.ps1
Normal file
@@ -0,0 +1,63 @@
|
||||
function ConvertTo-PowerShellDataFileText {
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[AllowNull()]
|
||||
$InputObject,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[int]
|
||||
$Indent = 0
|
||||
)
|
||||
|
||||
$IndentText = " " * $Indent
|
||||
$ChildIndent = $Indent + 4
|
||||
$ChildIndentText = " " * $ChildIndent
|
||||
|
||||
if($null -eq $InputObject){
|
||||
return '$null'
|
||||
}
|
||||
|
||||
if($InputObject -is [bool]){
|
||||
if($InputObject){
|
||||
return '$true'
|
||||
}
|
||||
|
||||
return '$false'
|
||||
}
|
||||
|
||||
if($InputObject -is [int] -or $InputObject -is [long] -or $InputObject -is [decimal] -or $InputObject -is [double]){
|
||||
return ([string]$InputObject)
|
||||
}
|
||||
|
||||
if($InputObject -is [string]){
|
||||
return "'$($InputObject.Replace("'", "''"))'"
|
||||
}
|
||||
|
||||
if($InputObject -is [System.Collections.IDictionary]){
|
||||
$Lines = @("@{")
|
||||
foreach($Key in $InputObject.Keys){
|
||||
$ValueText = ConvertTo-PowerShellDataFileText -InputObject $InputObject[$Key] -Indent $ChildIndent
|
||||
$Lines += "$ChildIndentText$Key = $ValueText"
|
||||
}
|
||||
$Lines += "$IndentText}"
|
||||
return ($Lines -join [Environment]::NewLine)
|
||||
}
|
||||
|
||||
if($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]){
|
||||
$Items = @($InputObject)
|
||||
if($Items.Count -eq 0){
|
||||
return '@()'
|
||||
}
|
||||
|
||||
$Lines = @("@(")
|
||||
foreach($Item in $Items){
|
||||
$ValueText = ConvertTo-PowerShellDataFileText -InputObject $Item -Indent $ChildIndent
|
||||
$Lines += "$ChildIndentText$ValueText"
|
||||
}
|
||||
$Lines += "$IndentText)"
|
||||
return ($Lines -join [Environment]::NewLine)
|
||||
}
|
||||
|
||||
return "'$(([string]$InputObject).Replace("'", "''"))'"
|
||||
}
|
||||
28
Private/Export-PowerShellDataFile.ps1
Normal file
28
Private/Export-PowerShellDataFile.ps1
Normal file
@@ -0,0 +1,28 @@
|
||||
function Export-PowerShellDataFile {
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[System.Collections.IDictionary]
|
||||
$InputObject,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$Path,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[switch]
|
||||
$Force
|
||||
)
|
||||
|
||||
if((Test-Path -Path $Path -PathType Leaf) -and (-not $Force)){
|
||||
throw "File [$Path] already exists. Use -Force to overwrite it."
|
||||
}
|
||||
|
||||
$Parent = Split-Path -Path $Path -Parent
|
||||
if(-not [string]::IsNullOrWhiteSpace($Parent) -and -not (Test-Path -Path $Parent)){
|
||||
New-Item -Path $Parent -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
|
||||
$Text = ConvertTo-PowerShellDataFileText -InputObject $InputObject
|
||||
Set-Content -Path $Path -Value $Text -Encoding UTF8
|
||||
}
|
||||
14
Private/Get-ConfigurationDataSecretProviderName.ps1
Normal file
14
Private/Get-ConfigurationDataSecretProviderName.ps1
Normal file
@@ -0,0 +1,14 @@
|
||||
function Get-ConfigurationDataSecretProviderName {
|
||||
[CmdletBinding()]
|
||||
Param()
|
||||
|
||||
if($null -eq $script:ConfigurationDataSecretProviders){
|
||||
$script:ConfigurationDataSecretProviders = @{}
|
||||
}
|
||||
|
||||
return @(
|
||||
$script:ConfigurationDataSecretProviders.Values |
|
||||
Sort-Object -Property Name |
|
||||
ForEach-Object { $_.Name }
|
||||
)
|
||||
}
|
||||
31
Private/New-ConfigurationDataAesKeyFile.ps1
Normal file
31
Private/New-ConfigurationDataAesKeyFile.ps1
Normal file
@@ -0,0 +1,31 @@
|
||||
function New-ConfigurationDataAesKeyFile {
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$Path,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[switch]
|
||||
$Force
|
||||
)
|
||||
|
||||
if((Test-Path -Path $Path -PathType Leaf) -and (-not $Force)){
|
||||
$Key = [Convert]::FromBase64String((Get-Content -Path $Path -Raw).Trim())
|
||||
if($Key.Length -notin @(16, 24, 32)){
|
||||
throw "Existing key file [$Path] does not contain a valid AES key length."
|
||||
}
|
||||
|
||||
return $Key
|
||||
}
|
||||
|
||||
$Parent = Split-Path -Path $Path -Parent
|
||||
if(-not [string]::IsNullOrWhiteSpace($Parent) -and -not (Test-Path -Path $Parent)){
|
||||
New-Item -Path $Parent -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
|
||||
$Key = New-Object byte[] 32
|
||||
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($Key)
|
||||
Set-Content -Path $Path -Value ([Convert]::ToBase64String($Key)) -Encoding ASCII
|
||||
return $Key
|
||||
}
|
||||
51
Private/Resolve-ConfigurationDataProviderSecureString.ps1
Normal file
51
Private/Resolve-ConfigurationDataProviderSecureString.ps1
Normal file
@@ -0,0 +1,51 @@
|
||||
function Resolve-ConfigurationDataProviderSecureString {
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
$Value
|
||||
)
|
||||
|
||||
if($Value -is [System.Security.SecureString]){
|
||||
return $Value
|
||||
}
|
||||
|
||||
if($Value -is [string]){
|
||||
return ConvertTo-SecureString -String $Value -AsPlainText -Force
|
||||
}
|
||||
|
||||
if(-not (Test-ConfigurationDataMap -Value $Value)){
|
||||
throw "Provider secure string value must be a SecureString, string, or hashtable."
|
||||
}
|
||||
|
||||
if(Test-ConfigurationDataMapContainsKey -Map $Value -Key "EnvironmentVariable"){
|
||||
$VariableName = [string](Get-ConfigurationDataMapValue -Map $Value -Key "EnvironmentVariable")
|
||||
$EnvironmentValue = [Environment]::GetEnvironmentVariable($VariableName)
|
||||
if([string]::IsNullOrEmpty($EnvironmentValue)){
|
||||
throw "Environment variable [$VariableName] is not defined or empty."
|
||||
}
|
||||
|
||||
return ConvertTo-SecureString -String $EnvironmentValue -AsPlainText -Force
|
||||
}
|
||||
|
||||
if(Test-ConfigurationDataMapContainsKey -Map $Value -Key "ProtectedValue"){
|
||||
$ProtectedValue = [string](Get-ConfigurationDataMapValue -Map $Value -Key "ProtectedValue")
|
||||
$Key = $null
|
||||
|
||||
if(Test-ConfigurationDataMapContainsKey -Map $Value -Key "Key"){
|
||||
$Key = [Convert]::FromBase64String([string](Get-ConfigurationDataMapValue -Map $Value -Key "Key"))
|
||||
}elseif(Test-ConfigurationDataMapContainsKey -Map $Value -Key "KeyPath"){
|
||||
$KeyPath = [string](Get-ConfigurationDataMapValue -Map $Value -Key "KeyPath")
|
||||
if(-not (Test-Path -Path $KeyPath -PathType Leaf)){
|
||||
throw "Secure string key file [$KeyPath] was not found."
|
||||
}
|
||||
|
||||
$Key = [Convert]::FromBase64String((Get-Content -Path $KeyPath -Raw).Trim())
|
||||
}else{
|
||||
throw "Protected provider secure string requires [Key] or [KeyPath]."
|
||||
}
|
||||
|
||||
return ConvertTo-SecureString -String $ProtectedValue -Key $Key
|
||||
}
|
||||
|
||||
throw "Unsupported provider secure string reference."
|
||||
}
|
||||
25
Private/Resolve-ConfigurationDataProviderSettingsPath.ps1
Normal file
25
Private/Resolve-ConfigurationDataProviderSettingsPath.ps1
Normal file
@@ -0,0 +1,25 @@
|
||||
function Resolve-ConfigurationDataProviderSettingsPath {
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$Provider,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[AllowEmptyString()]
|
||||
[string]
|
||||
$SettingsPath
|
||||
)
|
||||
|
||||
$FileName = "ProviderSettings.$Provider.psd1"
|
||||
|
||||
if([string]::IsNullOrWhiteSpace($SettingsPath)){
|
||||
return (Join-Path -Path (Get-Location).Path -ChildPath $FileName)
|
||||
}
|
||||
|
||||
if((Test-Path -Path $SettingsPath -PathType Container) -or $SettingsPath.EndsWith("\") -or $SettingsPath.EndsWith("/")){
|
||||
return (Join-Path -Path $SettingsPath -ChildPath $FileName)
|
||||
}
|
||||
|
||||
return $SettingsPath
|
||||
}
|
||||
Reference in New Issue
Block a user