SecretManagement.KeePass.Extension/SecretManagement.KeePass.Extension.psm1
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. using namespace Microsoft.PowerShell.SecretManagement function GetKeepassParams ([String]$VaultName, [Hashtable]$AdditionalParameters) { $KeepassParams = @{} if ($VaultName) { $KeepassParams.DatabaseProfileName = $VaultName } $SecureVaultPW = (Get-Variable -Scope Script -Name "Vault_$VaultName" -ErrorAction SilentlyContinue).Value.Password if (-not $SecureVaultPW) {throw "${VaultName}: Error retrieving the master key from cache"} $KeePassParams.MasterKey = $SecureVaultPW return $KeepassParams } function Get-Secret { param ( [string]$Name, [string]$VaultName, [hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters ) $ErrorActionPreference = 'Stop' if (-not (Test-SecretVault -VaultName $vaultName)) {throw "Vault ${VaultName}: Not a valid vault configuration"} $KeepassParams = GetKeepassParams $VaultName $AdditionalParameters if ($Name) { $KeePassParams.Title = $Name } $keepassGetResult = Get-KeePassEntry @KeepassParams | Where-Object ParentGroup -NotMatch 'RecycleBin' if ($keepassGetResult.count -gt 1) { throw "Multiple ambiguous entries found for $Name, please remove the duplicate entry" } if (-not $keepassGetResult.Username) { $keepassGetResult.Password } else { [PSCredential]::new($KeepassGetResult.UserName, $KeepassGetResult.Password) } } function Set-Secret { param ( [string]$Name, [object]$Secret, [string]$VaultName, [hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters ) if (-not (Test-SecretVault -VaultName $vaultName)) {throw "Vault ${VaultName}: Not a valid vault configuration"} $KeepassParams = GetKeepassParams $VaultName $AdditionalParameters #Set default group [String]$KeepassParams.KeePassEntryGroupPath = Get-KeePassGroup @KeepassParams | Where-Object fullpath -NotMatch '/' | ForEach-Object fullpath | Select-Object -First 1 switch ($Secret.GetType()) { ([String]) { $KeepassParams.Username = $null $KeepassParams.KeepassPassword = $Secret } ([PSCredential]) { $KeepassParams.Username = $Secret.Username $KeepassParams.KeepassPassword = $Secret.Password } default { throw 'This vault provider only accepts string and PSCredential secrets' } } return [Bool](New-KeePassEntry @KeepassParams -Title $Name -PassThru) } function Remove-Secret { param ( [string]$Name, [string]$VaultName, [hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters ) if (-not (Test-SecretVault -VaultName $vaultName)) {throw "Vault ${VaultName}: Not a valid vault configuration"} $KeepassParams = GetKeepassParams $VaultName $AdditionalParameters $GetKeePassResult = Get-KeePassEntry @KeepassParams -Title $Name if (-not $GetKeePassResult) { throw "No Keepass Entry named $Name found" } Remove-KeePassEntry @KeepassParams -KeePassEntry $GetKeePassResult -ErrorAction stop -Confirm:$false return $true } function Get-SecretInfo { param( [string]$Filter, [string]$VaultName = (Get-SecretVault).VaultName, [hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters ) if (-not (Test-SecretVault -VaultName $vaultName)) {throw "Vault ${VaultName}: Not a valid vault configuration"} $KeepassParams = GetKeepassParams -VaultName $VaultName -AdditionalParameters $AdditionalParameters $KeepassGetResult = Get-KeePassEntry @KeepassParams | Where-Object {$_ -notmatch '^.+?/Recycle Bin/'} [Object[]]$secretInfoResult = $KeepassGetResult.where{ $PSItem.Title -like $filter }.foreach{ [SecretInformation]::new( $PSItem.Title, #string name [SecretType]::PSCredential, #SecretType type $VaultName #string vaultName ) } [Object[]]$sortedInfoResult = $secretInfoResult | Sort-Object -Unique Name if ($sortedInfoResult.count -lt $secretInfoResult.count) { $filteredRecords = (Compare-Object $sortedInfoResult $secretInfoResult | Where-Object SideIndicator -eq '=>').InputObject Write-Warning "Vault ${VaultName}: Entries with non-unique titles were detected, the duplicates were filtered out. Duplicate titles are currently not supported with this extension, ensure your entry titles are unique in the database." Write-Warning "Vault ${VaultName}: Filtered Non-Unique Titles: $($filteredRecords -join ', ')" } $sortedInfoResult } function Test-SecretVault { [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName,Mandatory)] [string]$VaultName, [Parameter(ValueFromPipelineByPropertyName)] [hashtable]$AdditionalParameters = (Get-SecretVault -Name $vaultName).VaultParameters ) $VaultParameters = $AdditionalParameters $ErrorActionPreference = 'Stop' Write-Verbose "SecretManagement: Testing Vault ${VaultName}" if (-not $VaultName) { throw 'Keepass: You must specify a Vault Name to test' } if (-not $VaultParameters.Path) { #TODO: Add ThrowUser to throw outside of module scope throw "Vault ${VaultName}: You must specify the Path vault parameter as a path to your KeePass Database" } if (-not (Test-Path $VaultParameters.Path)) { throw "Vault ${VaultName}: Could not find the keepass database $($VaultParameters.Path). Please verify the file exists or re-register the vault" } try { $VaultMasterKey = (Get-Variable -Name "Vault_$VaultName" -Scope Script -ErrorAction Stop).Value Write-Verbose "Vault ${VaultName}: Master Key found in Cache, skipping user prompt" } catch { $GetCredentialParams = @{ Username = 'VaultMasterKey' Message = "Enter the Vault Master Password for Vault $VaultName" } $VaultMasterKey = (Get-Credential @GetCredentialParams) if (-not $VaultMasterKey.Password) { throw 'You must specify a vault master key to unlock the vault' } Set-Variable -Name "Vault_$VaultName" -Scope Script -Value $VaultMasterKey } if (-not (Get-KeePassDatabaseConfiguration -DatabaseProfileName $VaultName)) { New-KeePassDatabaseConfiguration -DatabaseProfileName $VaultName -DatabasePath $AdditionalParameters.Path -UseMasterKey Write-Verbose "Vault ${VaultName}: A PoshKeePass database configuration was not found but was created." return $true } try { Get-KeePassEntry -DatabaseProfileName $VaultName -MasterKey $VaultMasterKey -Title '__SECRETMANAGEMENT__TESTSECRET_SHOULDNOTEXIST' -ErrorAction Stop } catch { Clear-Variable -Name "Vault_$VaultName" -Scope Script -ErrorAction SilentlyContinue throw $PSItem } #If the above doesn't throw an error, we are good return $true } |