Git Product home page Git Product logo

secretmanagement's Introduction

PowerShell SecretManagement module

PowerShell SecretManagement module provides a convenient way for a user to store and retrieve secrets. The secrets are stored in SecretManagement extension vaults. An extension vault is a PowerShell module that has been registered to SecretManagement, and exports five module functions required by SecretManagement. An extension vault can store secrets locally or remotely. Extension vaults are registered to the current logged in user context, and will be available only to that user (unless also registered to other users).

In addition to implementing the five required SecretManagement functions, extension vaults are responsible for any authentication, including prompting the user for passphrases, and providing error and informational messages specific to the vault implementation. Error and informational messages common to all extension vaults are provided by SecretManagement.

PowerShell SecretManagement module is essentially an orchestrator for extension vaults which perform the actual secret storage and encryption. The extension vault may implement its own store, or wrap an existing secure store solution, such as Azure KeyVault, KeePass, Keyring, etc.

PowerShell SecretManagement supports the following secret data types:

  • byte[]
  • string
  • SecureString
  • PSCredential
  • Hashtable

All extension vault implementations must also support these data types.

PowerShell SecretManagement module provides cmdlets for for accessing and manipulating secrets, and also cmdlets for registering and manipulating vault extensions.

Installation

You can install the Microsoft.PowerShell.SecretManagement module from the PowerShell Gallery.

Install-Module -Name Microsoft.PowerShell.SecretManagement -Repository PSGallery

Secret metadata

Extension vaults can optionally support storing and retrieving additional secret metadata, that is data associated with the secret.
Secret metadata is not security sensitive and does not need to be stored securely in the extension vault.

Secret metadata can be included by using the -Metadata parameter: Set-Secret -Metadata @{ Name=Value }. The -Metadata parameter takes a Hashtable type argument consisting of name/value pairs.
Extension vaults should at minimum support the following value types:

  • string

  • int

  • DateTime

The secret metadata is included in the SecretInformation type object returned by Get-SecretInfo, in a Metadata property.
The Metadata property is a ReadonlyDictionary<string, object> type.

Vault extension registration cmdlets

Register-SecretVault

Registers a single extension vault to the current user

Get-SecretVault

Retrieves information about one or more registered extension vaults

Unregister-SecretVault

Unregisters a single extension vault

Set-SecretVaultDefault

Sets one registered extension vault as the default vault

Test-SecretVault

Runs the Test-SecretVault function provided by the extension vault

Unlock-SecretVault

Unlocks the extension vault through a passed in SecureString password

Accessing secrets cmdlets

Set-Secret

Adds a secret to a specific extension vault, or to the default vault if no vault is specified

Set-SecretInfo

Adds or replaces additional secret metadata to an existing secret in the vault. Metadata is not stored securely.

Get-Secret

Retrieves a secret from a specific extension vault, or first found over all vaults

Get-SecretInfo

Retrieves information about one or more secrets, but not the secret itself

Remove-Secret

Removes a secret from a specific vault

Vault extension registration

Vault extensions are registered to the current user context. They are registered as PowerShell modules that expose required functions used by SecretManagement to manipulate secrets. The required functions are provided as PowerShell cmdlets, and can be implemented as script or binary cmdlets.

Since each extension vault module exports the same five cmdlets, the module must conform to a directory structure that hides cmdlets from the user and PowerShell command discovery. Therefore the extension vault module itself does not export the five required cmdlets directly, but are instead exported from a nested module that resides within the extension vault module directory. This nested module must have a name that is the parent module name with '.Extension' appended to it.

It is recommended that the parent module manifest file (.psd1) include the 'SecretManagement' tag in its PrivateData section. This allows PowerShellGallery to associate it with the SecretManagement module.

Example: Script module vault extension

This is a minimal vault extension example to demonstrate the directory structure and functional requirements of an extension vault module. The extension vault module name is 'TestVault'.

Module directory structure

./TestVault
./TestVault/TestVault.psd1
./TestVault/TestStoreImplementation.dll
./TestVault/TestVault.Extension
./TestVault/TestVault.Extension/TestVault.Extension.psd1
./TestVault/TestVault.Extension/TestVault.Extension.psm1

TestVault.psd1 file

@{
    ModuleVersion = '1.0'
    RootModule = '.\TestStoreImplementation.dll'
    NestedModules = @('.\TestVault.Extension')
    CmdletsToExport = @('Set-TestStoreConfiguration','Get-TestStoreConfiguration')
    PrivateData = @{
        PSData = @{
            Tags = @('SecretManagement')
        }
    }
}

The TestVault extension module has a binary component (TestStoreImplementation.dll) which implements the vault. It publicly exports two cmdlets that are used to configure the store. It also declares the required nested module (TestVault.Extension) that exports the five functions required by SecretManagement registration.

Note that the nested module conforms to the required naming format:
'[ModuleName].Extension'

Note that only the 'NestedModules' entry is required because it loads 'TestVault.Extension' into the module scope, and allows SecretManagement access to the required five functions. The 'RootModule' and 'CmdletsToExport' entries are only for configuring the TestStore in this specific case, and are not needed in general.

TestVault.Extension.psd1 file

@{
    ModuleVersion = '1.0'
    RootModule = '.\TestVault.Extension.psm1'
    RequiredAssemblies = '..\TestStoreImplementation.dll'
    FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault')
}

This nested module implements and exports the five functions required by SecretManagement. It also specifies the TestStoreImplementation.dll binary as a 'RequiredAssemblies' because the five exported functions depend on it.

TestVault.Extension.psm1 file

function Get-Secret
{
    [CmdletBinding()]
    param (
        [string] $Name,
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    return [TestStore]::GetItem($Name, $AdditionalParameters)
}

function Get-SecretInfo
{
    [CmdletBinding()]
    param (
        [string] $Filter,
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    return @(,[Microsoft.PowerShell.SecretManagement.SecretInformation]::new(
        "Name",        # Name of secret
        "String",      # Secret data type [Microsoft.PowerShell.SecretManagement.SecretType]
        $VaultName,    # Name of vault
        $Metadata))    # Optional Metadata parameter
}

function Set-Secret
{
    [CmdletBinding()]
    param (
        [string] $Name,
        [object] $Secret,
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    [TestStore]::SetItem($Name, $Secret)
}

# Optional function
function Set-SecretInfo
{
    [CmdletBinding()]
    param (
        [string] $Name,
        [hashtable] $Metadata,
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    [TestStore]::SetItemMetadata($Name, $Metadata)
}

function Remove-Secret
{
    [CmdletBinding()]
    param (
        [string] $Name,
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    [TestStore]::RemoveItem($Name)
}

function Test-SecretVault
{
    [CmdletBinding()]
    param (
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    return [TestStore]::TestVault()
}

# Optional function
function Unregister-SecretVault
{
    [CmdletBinding()]
    param (
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    [TestStore]::RunUnregisterCleanup()
}

# Optional function
function Unlock-SecretVault
{
    [CmdletBinding()]
    param (
        [SecureString] $Password,
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    [TestStore]::UnlockVault($Password)
}

This module script implements the five functions, as cmdlets, required by SecretManagement, plus some optional functions. It also implements an optional Unregister-SecretVault function that is called during vault extension un-registration. It also implements an optional Set-SecretInfo function that sets secret metadata to a specific secret in the vault. It also implements an optional Unlock-SecretVault function that unlocks the vault for the current session based on a provided password.

The Set-Secret, Set-SecretInfo, Remove-Secret, Unregister-SecretVault functions do not write any data to the pipeline, i.e., they do not return any data.

The Get-Secret cmdlet writes the retrieved secret value to the output pipeline on return, or null if no secret was found. It should write an error only if an abnormal condition occurs.

The Get-SecretInfo cmdlet writes an array of Microsoft.PowerShell.SecretManagement.SecretInformation type objects to the output pipeline or an empty array if no matches were found.

The Test-SecretVault cmdlet should write all errors that occur during the test. But only a single true/false boolean should be written the the output pipeline indicating success.

The Unregister-SecretVault cmdlet is optional and will be called on the extension vault if available. It is called before the extension vault is unregistered to allow it to perform any needed clean up work.

The Unlock-SecretVault cmdlet is optional and will be called on the extension vault if available. It's purpose is to unlock an extension vault for use without having to prompt the user, and is useful for unattended scripts where user interaction is not possible.

In general, these cmdlets should write to the error stream only for abnormal conditions that prevent successful completion. And write to the output stream only the data as indicated above, and expected by SecretManagement.

In addition, these cmdlets should perform proper authentication and provide errors, and instructions to authenticate, as appropriate. Or prompt the user if needed, for example if a passphrase is required.

A vault extension doesn't need to provide full implementation of all required functions. For example, a vault extension does not need to provide a way to add or remove a secret through the SecretManagement cmdlets, and can just provide retrieval services. If a vault extension doesn't support some functionality, then it should write an appropriate error with a meaningful message.

Be careful with module implementation with scripts, because any data returned by function or method calls are automatically written to the output pipeline (if not assigned to a variable). SecretManagement expects only specific data to appear in the output pipeline, and if other data is inadvertently written, that will cause SecretManagement to not function properly.

Registering the vault

Once the TestVault module is created, it is registered as follows:

Register-SecretVault -Name LocalStore -ModuleName ./TestVault -VaultParameters @{ None="ReallyNeeded" } -DefaultVault

Get-SecretVault

VaultName  ModuleName  IsDefaultVault
---------  ----------  --------------
LocalStore TestVault   True

Extension vault registry file location

SecretManagement is designed to be installed and run within a user account on both Windows and non-Windows platforms. The extension vault registry file is located in a user account protected directory.

For Windows platforms the location is: %LOCALAPPDATA%\Microsoft\PowerShell\secretmanagement

For non-Windows platforms the location: $HOME/.secretmanagement

Windows Managed Accounts

SecretManagement does not currently work for Windows managed accounts.

SecretManagement depends on both %LOCALAPPDATA% folders to store registry information, and Data Protection APIs for safely handling secrets with the .Net SecureString type.
However, Windows managed accounts do not have profiles or %LOCALAPPDATA% folders, and Windows Data Protection APIs do not work for managed accounts.
Consequently, SecretManagement will not run under managed accounts.

Code of Conduct

Please see our Code of Conduct before participating in this project.

Security Policy

For any security issues, please see our Security Policy.

secretmanagement's People

Contributors

410sean avatar andyleejordan avatar michaeltlombardi avatar paulhigin avatar sdwheeler avatar stevel-msft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

secretmanagement's Issues

Register-SecretVault positional parameters unintuitive

I tried to omit the parameter names when using Register-SecretVault, but the positions didn't work out as I expected (given the example in the blog post, and other cmdlets that accept a name/key and a value):

> Register-SecretVault SecretStore Microsoft.PowerShell.SecretStore -DefaultVault
Register-SecretVault: Could not load and retrieve module information for module: SecretStore with error : The specified module 'SecretStore' was not loaded because no valid module file was found in any module directory..

The Register-SecretVault cmdlet violates PowerShell’s best-practice coding rules.

When strict mode is on, the Register-SecretVault cmdlet -- even though it completes what it is supposed to do -- generates a terminating error (“The property 'Path' cannot be found on this object. Verify that the property exists.”). Firstly, I would expect the Microsoft.PowerShell.SecretStore cmdlets to respect PowerShell’s best-practice coding rules and, secondly, if an error occurs anyway, the error to be non-terminating.

Set-StrictMode -Version Latest
try{
  Register-SecretVault -Name ‘MSStore' -ModuleName 'Microsoft.PowerShell.SecretStore'
  Register-SecretVault -Name 'KPStore' -ModuleName 'SecretManagement.Keepass'
} catch { $_ }

Unable to find dependent module(s) (Microsoft.PowerShell.SecretManagement)

If I use the parameter "-Force" when installing the SecretStore it fails with error message :

"Unable to find dependent module(s) (Microsoft.PowerShell.SecretManagement)" but IF I leave the "-Force" off

it will prompt me with:

Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its
InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"):

and I can proceed with "A" and install it.

Awareness of current vault name.

Suggestion.
Can we get an awareness of the current vault name ?
Currently, we have the vault parameters available through $AdditionalParameters.

The use case is for any additional functions shipped with the module.
For instance, the module I am currently working on: , SecretManagement.LastPass

Get-Secret will do attempt to authenticate the user if not connected but if everything fail, it will throw an error

 Error: Could not find decryption key. Perhaps you need to login with Connect-LastPass

Connect-LastPass is a complimentary function shipping with the module so the user do not have to make or know the underlying call. It uses $Vault parameter, even though in the root module so it can get vault parameters and respect the registered vault implementation.

What I'd really want to throw from Get-Secret is :

# The current vault name  is not available from within cmdlets so I cannot indicate it from within.
 Error: Could not find decryption key. Perhaps you need to login with Connect-LastPass -Vault 'CurrentVaultName'

That would requires me to be aware of which vault I am in when I throw the error from within Get-Secret and not just the $AdditionalParameters

Test-SecretVault does not take wildcards for -Name parameter

It is not easy to test all the secret vaults as the name parameter does not take wildcards. Get-SecretVault does take wildcards for name which set the expectation that Test-SecretVault will accept it too.

Current state:

PS /> Get-SecretVault

Name ModuleName IsDefaultVault


SecretStore Microsoft.PowerShell.SecretStore False
SecretStore2 Microsoft.PowerShell.SecretStore True

Actual

PS /> Test-SecretVault -name *
Test-SecretVault: Vault not found in registry: *

Expected

Should test all registered secret vaults.

Workaround

Get-SecretVault -Name * | Test-SecretVault

Request: Vault Extension Module Path Should be VaultName.SecretManagementExtension

@PaulHigin Great job on the thorough blog posts, it's most helpful.

I think that we should support module authors to be able to have a Secret Management Extension as part of their original module rather than having to spin it off as a separate module, similar to how DSC resources can exist in the same module as the main implementing module. This will reduce the proliferation of modules that have to be downloaded and simplify developers not needing to maintain multiple repositories.

However, the existing VaultName.Extension nomenclature is too vague for this use case. Someone coming across the repository will not know this is specially a implementation of a secret vault, so the path should also support something more specific, such as VaultName.SecretManagementExtension or VaultName.SecretManagementVault or something to clearly denote the code is for the secretmanagement vault extension implementation, since this is hardcoded and not configurable.

Example

For a module named KeePass that implements a vault, the folder structure should allow for KeePass.SecretManagementExtension subfolder, then the user has a very simply starting flow:

Install-Module KeePass #Also gets the regular powershell keepass commands
Register-SecretVault MyKeepass -Module Keepass
#etc...

Recommended Alternative

Consider a new PSData attribute in the psd1 to specify the path or name where the secret vault is located. This would have the additonal benefit to make them far more discoverable, for example something like Get-SecretVault -ListAvailable could search the modules and find which ones have the SecretManagementVault psdata attribute and return a list.

No BuiltInLocalVault when installing version 0.5.2-preview3

Installed Microsoft.PowerShell.SecretManagement from Powershell Gallery with:
Install-Module -Name Microsoft.PowerShell.SecretManagement -AllowPrerelease
And the BuiltInLocalVault didn't get created.
PS C:\> Get-SecretVault PS C:\>
Uninstalling the latest version and installing version 0.2.1-alpha1 created the BuildInLocalVault.

Edition Windows 10 Enterprise
Version 20H2
Installed on ‎2020-‎03-‎03
OS build 19042.508
Experience Windows Feature Experience Pack 120.2212.31.0

Needs better documentation about `-SecureStringSecret` in `Set-Secret`

This is the syntax of Set-Secret:

SYNTAX
    Set-Secret [-Name] <String> [-Secret] <Object> [[-Vault] <String>] [-NoClobber] [<CommonParameters>]

    Set-Secret [-Name] <String> [-SecureStringSecret] <SecureString> [[-Vault] <String>] [-NoClobber] [<CommonParameters>]

And this is the current documentation about -Secret and -SecureStringSecret:

    -Secret <Object>
        A secret value to be added. The object type must be one of the supported types.

        Required?                    true
        Position?                    1
        Default value                None
        Accept pipeline input?       True (ByValue)
        Accept wildcard characters?  false

    -SecureStringSecret <SecureString>
        A secret SecretString object to be added.

        Required?                    true
        Position?                    1
        Default value                None
        Accept pipeline input?       True (ByValue)
        Accept wildcard characters?  false

The parameter -Secret is able to accept SecureString object, so it's not clear when should a user uses -SecureStringSecret.
@PaulHigin's clarification on this is:

-SecureStringSecret is the default parameter set and when no parameter is supplied will prompt the user to safely type in a secret string

We probably need better documentation about -SecureStringSecret so it's less confusing to users.

Keepass

Do you have an examples yet for implementing with keepass by anychance? Thanks

Pipeline support

Some examples:

Get-SecretInfo | Get-Secret

Get-SecretInfo | Set-Secret

Get-SecretInfo | Remove-Secret

Set-AuthenticodeSignature should support Key Vault certificates

Today, signing PowerShell scripts/modules requires a certificate to be present in the certificate store or via a PFX file.

There's a growing number of situations where these are not available -- for example, if the key is stored in Key Vault an RSA-HSM key. There's no way to get a private key out.

There should be a way for PowerShell code signing to either use the KeyVault SignAsync API directly or at least have an extension mechanism where the sign digest can be externalized and someone else can write an adapter.

This also has the added benefit of working cross platform as the crypto itself is done in the HSM.

Have a way to view available additional parameters for a vault.

This is a suggestion.

For me, SecretManagement is about having a consistent way to access a ton of different providers in a consistend manner.
(Get-Secret / Set-Secret)

My only "pain point" is when it come to the registration of the vault.
I can try as is, but more often then not, I will need to check the documentation to determine what parameter can be used.

What if ...

There was something such as Get-SecretVaultRegistrationParameters that the implemented module could use to return a list of the available additional parameters ?

Example scenario
Example end user call:
(to determine how to register it)

Get-SecretVaultRegistrationParameters -ModuleName 'SecretManagement.AzKeyvault'

image

I would use that pretty much anytime before I register a vault since there's a lot of variation per module on that level.

Pre-requisites
The module creator wanting to provide this insight to the users would need to expose the optional Get-SecretVaultRegistrationParameters function and add the return values.

function Get-SecretVaultRegistrationParameters() {
    return @(
        [SecretParameterInfos]::new('SubscriptionId', 'Azure subscription id', $true)
        [SecretParameterInfos]::new('VaultName', 'Azure vault name', $true)
        [SecretParameterInfos]::new('SessionTimeout', "Determine the session timeout for the user.`nIf set to 0, default will be used", $false,'60 minutes')
    )    
}

If not implemented, then it could return a Not implemented exception and / or display a warning so a distinction can be made between not implemented or the fact there is just no parameter to configure.

Complete sample




# Example class for the purpose of this example... Although I am well aware c# would be used for that part.
class SecretParameterInfos {
    [String]$Name
    [String]$Description
    [bool]$Mandatory
    [String]$Default

    SecretParameterInfos($Name, $Description) {
        $this.Name = $Name
        $this.Description = $Description
    }

    SecretParameterInfos($Name, $Description, $Mandatory) {
        $this.Name = $Name
        $this.Description = $Description
        $this.Mandatory = $Mandatory
    }

    SecretParameterInfos($Name, $Description, $Mandatory,$Default) {
        $this.Name = $Name
        $this.Description = $Description
        $this.Mandatory = $Mandatory
        $this.Default = $Default
    }
}




#Declared in SecretManagement.MyModule.psm1 
function Get-SecretVaultRegistrationParameters() {
    return @(
        [SecretParameterInfos]::new('SubscriptionId', 'Azure subscription id', $true)
        [SecretParameterInfos]::new('VaultName', 'Azure vault name', $true)
        [SecretParameterInfos]::new('SessionTimeout', "Determine the session timeout for the user.`nIf set to 0, default will be used", $false,'60 minutes')
    )    
}

#End user that want to register a vault but want to know if there are available parameters to him
Get-SecretVaultRegistrationParameters -ModuleName 'SecretManagement.MyModule'

If you were to go even deeper, you could also imagine the main module using that list and making the registration fail if it have elements listed as Required but not provided by the user automatically.

That being said, the core of this suggestion is to have that piece of information embedded (provided the module owner implement it) and readily available without having to do back and forth always with the implemented modules documentations.

Thank you for considering this.

-AsPlainText with PSCredential secrets... is this a layering problem?

image

Right now, it doesn't do anything.

It makes sense because it's returning a PSCredential which can only be represented by a string username and SecureString password...

So either we leave it as is and -AsPlainText only works for some secret types... or

  1. We have some way to "show" the password of a PSCredential (maybe we attach a Note property or just return a username,password pscustomobject...)
  2. We remove -AsPlainText because ConvertFrom-SecureString is already available for this usecase

Passing SecureStringSecret to extensions from Set-Secret

When passing SeureStringSecrets to the SecretStore, the secret is placed in the SecretStore. What is passed from the Set-Secret cmdlet to the extension? I want to pick up the SecureString in my extension, but what should I expect?

Request: Register-SecretVault should have ModuleName as Position 0 Parameter and Name Optional

For a ease of use improvement, if ModuleName is in Position 0 and Name is optional, then the vault name should be the same as the ModuleName by default if one isn't supplied, facilitating easier and more terse usage if a user doesn't want a custom Vault Name.

For instance:

Register-SecretVault Keepass #Implies -ModuleName KeePass
#Will Create a vault named 'KeePass' that can be referenced easily
Set-Secret -VaultName KeePass -Name MySecret -Secret 'ok'

Request: Register-SecretVault should call Test-SecretVault of the implementing extension in host UI Thread

Register-SecretVault does not appear to currently call any validation to the extension to make sure the VaultParameters are correct, for instance if a file path is correct. This also would give the extension the opportunity to do any registration of the vault if required, such as fetching an API key, etc.

EDIT: From what I'm seeing it does actually call it, but it calls it in a separate C# invocation, which disables all the host UI and other items that could be part of a test (e.g. prompting for the database path if one wasn't supplied). Further, Test-SecretVault are difficult to debug since no stacktrace is provided, only $error[0].exception.innerexception to maybe hint at what went wrong.

Feature Request: VaultParameters as dynamic Parameters

A secret module could expose the common parameters it uses via a typed hashtable or even as dynamic params, and SecretManagement could present the parameters dynamically once the vaultname is selected.

For instance, if a KeePass extension defined a Path and MasterKey VaultParameters, then the following could be possible to do temporary overrides of the existing vault configuration, which could be more intuitive for specifying noninteractive parameters such as credentials.

Get-Secret -VaultName KeePass -Name MySecret -Path my\special\path -MasterKey (Get-Credential)

See InvokeBuild for an example of this in action, parameters defined in the build script are available to the Invoke-Build command.
https://github.com/nightroman/Invoke-Build/wiki/Script-Tutorial#script-parameters-for-tasks

Cannot install the SecretStore module

I've gotta be doing something wrong but I've tried a couple times.

PS C:\Users\adamr> Install-Module Microsoft.PowerShell.SecretManagement -AllowPrerelease -Scope CurrentUser -Force
PS C:\Users\adamr> Install-Module Microsoft.PowerShell.SecretStore -AllowPrerelease -Scope CurrentUser -Force
Install-Package: C:\Users\adamr\Documents\PowerShell\Modules\PowerShellGet\2.2.4.1\PSModule.psm1:9709
Line |
9709 |  … talledPackages = PackageManagement\Install-Package @PSBoundParameters
     |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Unable to find dependent module(s) (Microsoft.PowerShell.SecretManagement)
PS C:\Users\adamr> get-module Microsoft.PowerShell.SecretManagement -list


    Directory: C:\Users\adamr\Documents\PowerShell\Modules

ModuleType Version    PreRelease Name                                PSEdition ExportedCommands
---------- -------    ---------- ----                                --------- ----------------
Binary     0.5.2      preview3   Microsoft.PowerShell.SecretManagem… Core      {Register-SecretVault, Unregister-Secre…

Verbose / Debug flags should be passed down to the implementing module

Verbose / Debug from the Microsoft.Powershell.SecretManagement module get printed all right but are not passed down to the implementing module.

If I had these two lines at the top of the Get-SecretInfo implementation, here is what I get when I call Get-SecretInfo -Verbose

# Forcing verbose on purpose so I can see the value.
Write-verbose -message ($PsboundParameters | out-string) -verbose
write-verbose -message "`$VerbosePreference: $VerbosePreference" -verbose

image

Observed here:
TylerLeonhardt/SecretManagement.LastPass#4

still prompted when I provide a securestring with default store

PS> $ss = read-host -AsSecureString 
***********
PS> Set-Secret -Name TestSecret -SecureStringSecret $ss
Creating a new SecretStore vault. A password is required by the current store configuration.
Enter password:

It seems like if I provide a secure string, it shouldn't prompt

The single only available secret vault should be made default automatically

The single only available secret vault should be made default automatically.

When running Register-SecretVault -Name MyStore -ModuleName Microsoft.PowerShell.SecretStore or Unregister-SecretVault SecretStore, if the action results in only a single vault being present, then that vault should be made 'Default' automatically.

This will make the following scenario much intuitive:

## Register the very first vault

PS> Register-SecretVault -Name MyStore -ModuleName Microsoft.PowerShell.SecretStore
PS> Set-Secret -Name nuget -Secret abc
Set-Secret: Unable to set secret because no vault was provided and there is no default vault designated.

Import-module failes on WS2016

Import-module failes running PS 5.1 on Windows Server 2016.
I don't know if this is just my machine or related to WS2016.

PS H:\> import-module Microsoft.PowerShell.SecretManagement
import-module : Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified. At line:1 char:1 import-module Microsoft.PowerShell.SecretManagement CategoryInfo : NotSpecified: (:) [Import-Module], FileNotFoundException FullyQualifiedErrorId : System.IO.FileNotFoundException,Microsoft.PowerShell.Commands.ImportModuleCommand

PS H:\> $PSversiontable
Name Value PSVersion 5.1.14393.3471 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.14393.3471 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1

PS H:\> get-module Microsoft.PowerShell.SecretManagement -ListAvailable
Directory: C:\Program Files\WindowsPowerShell\Modules ModuleType Version Name ExportedCommands Binary 0.5.3 Microsoft.PowerShell.SecretManag... {Register-SecretVault, Unregister-SecretVault, Get-SecretV...

image

Discussion: Should extensions just handle opaque strings?

While writing an extension for KeyChain, the bulk of my code is to handle the various supported types and convert them to a string so that I can store the in KeyChain using the security command line tool. I would think that many if not most extensions don't do anything special with the various types and just transform them to a string form to store.

Perhaps a flag in the extension module manifest can declare that the extension only handles opaque strings and then Set-Secret and Get-Secret within the extension only handles a string. SecretManagement itself can encode the string to know the original type and handle the conversion to and from without the extension knowing anything about that. The additional benefit is that if additional types are supported in the future, only SecretManagment needs to handle them and not the extension.

Also, in this model, the extension would not implement Get-SecretInfo as the info (like Type) is embedded within the string, so SecretManagement just needs a way to enumerate all secrets from the extension and handle creating the SecretInformation objects.

Vaultparameters hashtable should be case insensitive.

Most of Powershell is case insensitive.
VaultParameters is taking in a Powershell hashtable but it is not case insensitive.

This will succed:

Register-SecretVault -ModuleName Az.Keyvault -VaultParameters @{ AZKVaultName= 'devops-cicd'; SubscriptionId = '<ID>' }

This will fail:

Register-SecretVault -ModuleName Az.Keyvault -VaultParameters @{ AZKVaultName= 'devops-cicd'; subscriptionId = '<ID>' }

will fail.

(If you're looking for the issue, "Subscriptionid" in the failing registration have its first 'S' in lower case.


# .Net C# code for SecretManagement
$SensitiveHT = [hashtable]::new()
$SensitiveHT.HELLO = 'WORLD'

#Return Null  :( 
$SensitiveHT.hello 

$PowershellHT = @{'HELLO'='WORLD'}
# Do not care about what anyone think (Insensitive)
# Return WORLD
$PowershellHT.hello 

# Expected .Net C# hashtable generation
$InsensitiveHT = [hashtable]::new([System.StringComparer]::CurrentCultureIgnoreCase)
$InsensitiveHT.HELLO = 'WORLD'

#Return WORLD
$InsensitiveHT.hello

Tab completion everywhere

This should work:

Get-SecretInfo -Vault <TAB>
Get-Secret -Name <TAB>

And complete based on the vaults that are available. Should be very simple to do with an argument completer.

Feature Request: Extensible "Description" property on [SecretInformation]

Carried over from PowerShell/Modules#31

Summary of the new feature/enhancement

As a powershell user, I sometimes want some more information about what a secret is for, and a provider may be able to provide that additional information in a way that I can programatically query/filter that information.

For example:

  • Azure Key Vault could have the description of a byte[] secret as an X509 certificate, when it was modified
  • Keepass may provide a folder structure for where the secret resides, or a flag that a string is an SSH key for use in an SSH agent.

Proposed technical implementation details (optional)

Add a [Object] property to Get-SecretInfo output

Some name possibilities: Notes, Description, Metadata

The provider can populate this property with additional information about the secret. This information would be provider specific and the provider can supply any type of object as long as it has a clean ToString() method to convert it. Most providers may just do a simple string description, others may submit a nested hashtable with properties like created, modified, etc.

Future:
Some natural commonalities like "LastModified","Comment" may become common and should be added to the [SecretInformation] class designation to standardize them, however this property allows providers to deliver their own extensible information that's important to their provider.

registering a second vault may imply a different store

at least that's what I thought would happen. Here's a transcript:

S /> set-secret -name test1                                                                                                                                                                                                                                                                                cmdlet Set-Secret at command pipeline position 1                                                                                                      Supply values for the following parameters:                                                                                                           
SecureStringSecret: *************
Creating a new SecretStore vault. A password is required by the current store configuration.
Enter password:

Enter password again for verification:

PS /> get-secret test1
System.Security.SecureString
PS /> get-secret test1 -as
lsjkflkjsdlkf
PS /> get-secretinfo

Name          Type VaultName
----          ---- ---------
test1 SecureString SecretStore

PS /> Register-SecretVault -Name ss2 -ModuleName Microsoft.PowerShell.SecretStore                      
PS /> get-secretinfo                                                             

Name          Type VaultName
----          ---- ---------
test1 SecureString SecretStore
test1 SecureString ss2

I thought I was creating a second store, but it's the same. Is there a way to create a second vault with the same module?

`Set-DefaultVault` doesn't have the same prefix as other cmdlets

It's best practice for a module to have the same prefix for every cmdlet in a module so that users know that a cmdlet comes from a module... Or use a prefix that gives context to the user on what they are doing.

Set-DefaultVault doesn't have the prefix that the others do and I worry that it could stomp on an existing cmdlet since Vault is fairly overloaded in the key store space.

This cmdlet should either be Set-SecretDefaultVault or Set-SecretVaultDefault.

Should Register-SecretVault lock in the ModulePath where the ModuleName resides?

While doing development on a SecretManagement extension, a different instance of my module kept getting imported into my session and executed, rather than the one I placed earlier in the $PSModulePath. I finally figured out that the ModulePath property was saved with my registered secret vault, and that instance had priority running.

I did an Unregister-SecretVault & Register-SecretVault, which reset the ModulePath based on $PSModulePath and solved the strange import behavior.

My mindset was that the Register-SecretVault was semi-permanent and would not have to be reissued periodically.

Of course, having discovered this behavior, hopefully I'll recognize the symptoms if it crops up again.

Extensions should be able to hook into `Register-SecretVault`

Feature request.
We have a hook into Unregister thanks to this request.

I would like a way to have my extension perform some validation or other actions before registering the vault.

Use cases relevant to me :

  • Implemented module require some parameters to work properly. Validation could be done in the hook and prevent the registration if mandatory conditions are not met.

Az.keyvault is a good example of this. It requires SubscriptionId and VaultName to identify which Azure keyvault is targeted by the registration.

  • Implemented module require some action to be performed prior the registration.

--

I had such a use case while looking at using SecretManagement for a CMS implementation that require :

  • A mandatory vault parameter (for which I would issue a warning and prevent registration if not provided)
  • The creation of a self-signed certificate and adding that newly created certificate to the user certificates.

A Register-SecretVault hook (before registration) would be complimentary to the recently added Unregister-SecretVault hook and also definitely have valid use cases (Validation & performing any relevant actions ).

Thank you for considering this.

Register-SecretVault : Cannot bind argument to parameter 'Path' because it is an empty string.

Trying to follow blog post. After installation:

Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault
Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault

Register-SecretVault : Cannot bind argument to parameter 'Path' because it is an empty string.
At line:1 char:1
+ Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShe ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Register-SecretVault], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.SecretManagement.RegisterSecretVaultCommand

Unregister-SecretVault : Item has already been added. Key in dictionary: 'verbose' Key being added: Verbose

image

There seems to be a bug with the implementation of #66.

Steps to reproduce :

Register-SecretVault -ModuleName secretmanagement.lastpass -VaultParameters @{lpassCommand = 'wsl'; verbose = $true } -Name 'lpt10'
Unregister-SecretVault -Name lpt10 

I used SecretManagement.Lastpass but I assume the actual module implementation is relevant and any valid module could be put in there

Issue:
I cannot unregister that keyvault anymore.

(In the end, I was able to remove the vault manually by tracking down the secretregistryvault location in the filesystem )

Install-Module for SecretStore fails if using -Force

I want to add SecretManagement to a Linux Docker container running PS Core 7.0.3. Since I'm using a dockerfile to build the image, I need to use the -Force parameter to bypass the "Are you sure" question prompt.

The SecretManagement module installs just fine with -Force, but installing SecretStore throws the following error:

PS /nectar10> Install-Module Microsoft.PowerShell.SecretStore -AllowPrerelease -Force Install-Package: /opt/microsoft/powershell/7/Modules/PowerShellGet/PSModule.psm1:9709 Line | 9709 | … talledPackages = PackageManagement\Install-Package @PSBoundParameters | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Unable to find dependent module(s) (Microsoft.PowerShell.SecretManagement)

I get the same issue when running the container interactively. However, if I remove the -Force parameter, the module installs just fine after I press Y.

I can work around the issue by trusting the PSGallery store.

Just a bunch of questions

When searching for a way to secure credentials passwords which must be used within a Powershell/APi module to do sensitive work, I passed by this repository. First of all, this looks awesome. But it raises some questions

  • How can we change the locations of the store and the key files?
  • Is it possible to put a store and a key file in a Azure Devops pipeline to deploy them?
  • If we can only deploy the Store, do we need to install both modules?

These are the questions for now. Thanks in advance for answering them.

Get-Secret not supporting wildcard characters yet preventing their uses as regular characters.

if (WildcardPattern.ContainsWildcardCharacters(Name))

        // Wild card characters are not supported in this cmdlet.

The current Get-Secret implementation do not support wildcards.
This is perfectly fine.

String should be interpreted literally when querying the secret name.
So :

  • "MySecret*xy"
  • "MySecret [Id: 1029301]"

should fetch the corresponding secrets with that literal name.

The current behavior is weird.
If the cmdlet is not supporting wildcards, I don't expect it to throw an error.
I expect it to process the string literally, which it is what it does, despite the error.

The error is unwanted here.

See: Using SecretManagement.Lastpass module, I get the result properly, as wildcards are not supported.
Unlike any other cmdlet not supporting wildcards, I would expect NOT to have an error here.
image

Edit:
Also, escaping the wildcard (like if it was a wildcard function) will not produce the error but will be passed down literally.

Related Issue:
TylerLeonhardt/SecretManagement.LastPass#5

Designdoc - Verbose implementation.

I was looking at the changelog and saw something was done for verbose.

Can't the flag be passed down normally through the calling cmdlet ?
For instance,

Get-Secret -Name 'SuperSecret' -AsPlainText -vault MyVault -Verbose

I would expect that verbose flag to be passed down to the implementing module, not a flag that need to be defined in VaultParameters. (And following that logic, it should be possible to pass the -debug flag in the same manner).

With #66 , it seems that I need to unregister the vault, then register it again with verbose, then unregister / re-register it again when done. It feels like we're going the long way around.

Extensions should be able to hook into `Unregister-SecretVault`

Would like a way to have my extension called when Unregister-SecretVault is used with my extension. The scenario is I may want to clean up all the secrets stored via my vault from PowerShell.

KeyChain, for example, allows me to create a new keychain just for my extension that wouldn't be mixed with other secrets already stored in keychain for a user. If they unregister my extension, it would mean they no longer need to use it so I should clean out their secrets.

We would also need to decide if the user opts in or out of cleaning out those secrets by default.

Register-SecretVault should import a module with -Force

I'm working on crafting an extension and what's kinda annoying is having to create a new PowerShell session every time I make a change to my implmenting script extension.

I wish I could just run this:

Unregister-SecretVault -Name foo
Register-SecretVault -Name foo -ModuleName .

And be ready to go with out a restart of my PowerShell session.

TypeInitializationException

If I try this Module on a Windows 2016 Server, I'm always getting the following error.

Test-SecretVault

Test-SecretVault : Der Typeninitialisierer für "Microsoft.PowerShell.SecretManagement.RegisteredVaultCache" hat eine
Ausnahme verursacht.
In Zeile:1 Zeichen:1

  • Test-SecretVault
  •   + CategoryInfo          : NotSpecified: (:) [Test-SecretVault], TypeInitializationException
      + FullyQualifiedErrorId : System.TypeInitializationException,Microsoft.PowerShell.SecretManagement.TestSecretVault
     Command
    
    

Maybe it could be interesting for the further development. If you need any Information about this error, feel free to contact me.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.