PS.PasswordState.Extension/PS.PasswordState.Extension.psm1
|
#region Helpers function ConnectToPWST { param($AdditionalParameters) return (PS.PasswordState\Connect-PasswordState @AdditionalParameters -Verbose) } function New-PSSecretInfo { param([string]$Name, [string]$VaultName, [string]$Type) $stype = switch ($Type) { 'String' { [Microsoft.PowerShell.SecretManagement.SecretType]::String } 'Hashtable' { [Microsoft.PowerShell.SecretManagement.SecretType]::Hashtable } 'SecureString' { [Microsoft.PowerShell.SecretManagement.SecretType]::SecureString } 'PSCredential' { [Microsoft.PowerShell.SecretManagement.SecretType]::PSCredential } 'ByteArray' { [Microsoft.PowerShell.SecretManagement.SecretType]::ByteArray } default { [Microsoft.PowerShell.SecretManagement.SecretType]::Unknown } } [Microsoft.PowerShell.SecretManagement.SecretInformation]::new($Name, $stype, $VaultName, $null) } function GetPasswordStateType { param($Response) if (($Response | Select-Object -ExcludeProperty PasswordID, Title | Get-Member -MemberType NoteProperty | Measure-Object).Count -gt 2) { return 'Hashtable' } if ((-not [String]::IsNullOrWhiteSpace($Response.UserName)) -and (-not [String]::IsNullOrWhiteSpace($Response.Password))) { return 'PSCredential' } return 'SecureString' } function FormatPasswordStateCredential { param($Response) $StateType = GetPasswordStateType -Response $Response Write-Debug "Formatting Passwordstate response as type '$StateType'" switch ($StateType) { 'PSCredential' { $pw = $Response.Password if ($pw -is [string]) { $pw = ConvertTo-SecureString -String $pw -AsPlainText -Force } return New-Object System.Management.Automation.PSCredential($Response.Username, $pw) } 'SecureString' { $pw = $Response.Password if ($pw -is [string]) { $pw = ConvertTo-SecureString -String $pw -AsPlainText -Force } return $pw } 'Hashtable' { $ht = @{} $props = $Response | Select-Object -ExcludeProperty PasswordID, Title | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name foreach ($p in $props) { $ht[$p] = $Response.$p } return $ht } 'ByteArray' { $pw = $Response.Password if ($pw -is [string]) { $pw = [System.Text.Encoding]::ASCII.GetBytes($pw) } return $pw } Default { return $Response.Password } } } #endregion #region Finished and confirmed working. function Get-Secret { [CmdletBinding()] param( [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$VaultName, [hashtable]$AdditionalParameters ) Write-Verbose "Get-Secret:: Getting secret '$Name' from vault '$VaultName'..." $Connected = (PS.PasswordState\Connect-PasswordState @AdditionalParameters -Verbose) if (-not $Connected) { throw "Could not connect to Passwordstate vault '$VaultName'." } $Passwords = PS.PasswordState\Find-PasswordStatePassword -SearchTerm $Name # If multiple results, take the one with exact title match if ($Passwords.Count -gt 1) { throw "Multiple passwords found with name '$Name' in vault '$VaultName'." } if ($Passwords.Count -eq 0) { throw "No password found with name '$Name' in vault '$VaultName'." } $Password = $Passwords | Select-Object -First 1 Write-Verbose "Found password with ID $($Password.PasswordID) and title '$($Password.Title)'. Retrieving full details..." #$Password = $Password | Select-Object -ExpandProperty PasswordID $Password = (PS.PasswordState\Get-PasswordStatePassword -PasswordID $Password.PasswordId) Write-Verbose "Retrieved password details. Processing return value..." return (FormatPasswordStateCredential -Response $Password) } function Get-SecretInfo { [CmdletBinding()] param( [string]$Filter, [Parameter(Mandatory)][string]$VaultName, [hashtable]$AdditionalParameters ) begin { Write-Verbose "Get-SecretInfo:: Getting secrets with filter '$Filter' from vault '$VaultName'..." $Connected = (PS.PasswordState\Connect-PasswordState @AdditionalParameters -Verbose) if (-not $Connected) { throw "Could not connect to Passwordstate vault '$VaultName'." } } process { $Passwords = PS.PasswordState\Find-PasswordStatePassword -SearchTerm $Filter $Infos = foreach ($i in $Passwords) { $pw = PS.PasswordState\Get-PasswordStatePassword -PasswordID $i.PasswordId # If username exists, it's a PSCredential; else SecureString #if($null -ne $pw.Username -and $pw.Username -ne '') { $type = 'PSCredential' } else { $type = 'SecureString' } $type = (GetPasswordStateType -Response $pw) Write-Debug "Found secret $($pw.Title) with type $type" New-PSSecretInfo -Name $pw.Title -VaultName $VaultName -Type $type } return $Infos } } function Test-SecretVault { [CmdletBinding()] param( [Parameter(Mandatory)][string]$VaultName, [hashtable]$AdditionalParameters ) Write-Verbose "Testing connection to vault '$VaultName'..." $Connected = $false try { Write-Debug "Connecting with parameters: $($AdditionalParameters | Out-String)" $Connected = (PS.PasswordState\Connect-PasswordState @AdditionalParameters -Verbose) Write-Verbose "Connection test: $Connected" } catch { Write-Error $_ } return $Connected if ($true) { $ctx = PS.PasswordState\Get-PasswordStateContext if ($null -ne $ctx) { return $true } } return $false } function Unlock-SecretVault { [CmdletBinding()] param([SecureString]$Password, [string]$VaultName, [hashtable]$AdditionalParameters) # Not required for Passwordstate’s API (API key already unlocks). return } #endregion # WIP function Set-Secret { [CmdletBinding()] param( [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][object]$Secret, [Parameter(Mandatory)][string]$VaultName, [hashtable]$AdditionalParameters ) $BodyParams = @{ Title = $Name } switch ($Secret.GetType().Name) { 'Byte[]' { $SecretString = [System.Text.Encoding]::ASCII.GetString($Secret) $BodyParams.Password = $SecretString #Set-String -Name $Name -Secret $SecretString -AZKVaultName $AZKVaultName -ContentType $ContentType #Set-ByteArray -Name $Name -Secret $Secret -AZKVaultName $AdditionalParameters.AZKVaultName -ContentType 'ByteArray' } 'String' { $BodyParams.Password = $Secret | ConvertTo-SecureString -AsPlainText -Force #Set-String -Name $Name -Secret $Secret -AZKVaultName $AdditionalParameters.AZKVaultName -ContentType 'String' } 'SecureString' { #$SecretString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Secret)) $BodyParams.Password = $Secret #Set-SecureString -Name $Name -Secret $Secret -AZKVaultName $AdditionalParameters.AZKVaultName -ContentType 'SecureString' } 'PSCredential' { $pw = $Secret.GetNetworkCredential().Password $BodyParams.Password = $pw | ConvertTo-SecureString -AsPlainText -Force $BodyParams.UserName = $Secret.UserName #Set-PSCredential -Name $Name -Secret $Secret -AZKVaultName $AdditionalParameters.AZKVaultName -ContentType 'PSCredential' } 'Hashtable' { throw "Storing hashtables is not supported in Passwordstate." #Set-Hashtable -Name $Name -Secret $Secret -AZKVaultName $AdditionalParameters.AZKVaultName -ContentType 'Hashtable' } Default { #throw "Invalid type. Types supported: byte[], string, SecureString, PSCredential, Hashtable"; throw "Invalid type. Types supported: byte[], string, SecureString and PSCredential"; } } Write-Verbose "Set-Secret:: Setting secret '$Name' in vault '$VaultName'..." $Connected = (PS.PasswordState\Connect-PasswordState @AdditionalParameters -Verbose) if (-not $Connected) { throw "Could not connect to Passwordstate vault '$VaultName'." } $Existing = PS.PasswordState\Find-PasswordStatePassword -SearchTerm $Name if ($Existing.Count -gt 1) { throw "Multiple passwords found with name '$Name' in vault '$VaultName'." } if ($Existing.Count -eq 0) { Write-Verbose "No existing password found with name '$Name'. Creating new entry..." #$BodyParams.PasswordListID = $AdditionalParameters.PasswordListID #if(-not $BodyParams.PasswordListID) { throw "To create a new password, you must provide a PasswordListID in AdditionalParameters." } try { $New = PS.PasswordState\New-PasswordStatePassword @BodyParams -ErrorAction Stop $PulledFresh = PS.PasswordState\Get-PasswordStatePassword -PasswordID $New.PasswordID Write-Verbose "Created new password with ID $($New.PasswordID) and title '$($New.Title)'." return (FormatPasswordStateCredential -Response $PulledFresh) } catch { throw "Failed to create new password: $($_.Exception.Message)" } } else { if ($Existing.Count -eq 1) { $Existing = $Existing | Select-Object -First 1 Write-Verbose "Found existing password with ID $($Existing.PasswordID). Updating entry..." $Updated = PS.PasswordState\Set-PasswordStatePassword -PasswordID $Existing.PasswordID @BodyParams $PulledFresh = PS.PasswordState\Get-PasswordStatePassword -PasswordID $Updated.PasswordID Write-Verbose "Updated password with ID $($Updated.PasswordID) and title '$($Updated.Title)'." return (FormatPasswordStateCredential -Response $PulledFresh) } } } function Remove-Secret { [CmdletBinding()] param( [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$VaultName, [hashtable]$AdditionalParameters ) } # Not started... function Set-SecretInfo { [CmdletBinding()] param( [Parameter(Mandatory)][string]$Name, [hashtable]$Metadata, [Parameter(Mandatory)][string]$VaultName, [hashtable]$AdditionalParameters ) Write-Verbose "Set-Secret:: Setting secret '$Name' in vault '$VaultName'..." $Connected = (PS.PasswordState\Connect-PasswordState @AdditionalParameters -Verbose) if (-not $Connected) { throw "Could not connect to Passwordstate vault '$VaultName'." } $Existing = PS.PasswordState\Find-PasswordStatePassword -SearchTerm $Name if ($Existing.Count -gt 1) { throw "Multiple passwords found with name '$Name' in vault '$VaultName'." } if ($Existing.Count -eq 0) { throw "No password found with name '$Name' in vault '$VaultName'." } $Password = $Existing | Select-Object -First 1 $Password = (PS.PasswordState\Get-PasswordStatePassword -PasswordID $Password.PasswordId) # UNable to set Username and Password only. Other fields can be set. return PS.PasswordState\Set-PasswordStatePassword -PasswordID $Password.PasswordID @Metadata } |