{"id":533,"date":"2021-10-25T08:45:51","date_gmt":"2021-10-25T15:45:51","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/powershell-community\/?p=533"},"modified":"2021-10-25T08:45:51","modified_gmt":"2021-10-25T15:45:51","slug":"how-to-use-the-secret-modules","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell-community\/how-to-use-the-secret-modules\/","title":{"rendered":"How to use the Secret modules"},"content":{"rendered":"<p><strong>Q:<\/strong> I have a bunch of scripts we use in production that make use of Windows credentials. In some cases, these scripts have an actual password in plain text, while others read the password from an XML file. Is there a better way?<\/p>\n<p><strong>A:<\/strong> Scripts with high-privilege account passwords in plain text is not a good idea. There are several methods you can use to improve the security of credentials handling. One great way is to use the SecretManagement and SecretStore modules from the PowerShell Gallery.<\/p>\n<h2>What are Secrets?<\/h2>\n<p>Secrets are, in general, passwords you need to access some resource. It might be the password for a domain administrator that you use to run a command on a remote host. You want to keep secrets secret, yet you want a great way to use them as needed.<\/p>\n<p>In my PowerShell books, I use a domain (Reskit.Org) for all my examples. The password for this mythical domain&#8217;s Enterprise and Domain administrator is &#8220;Pa$$W0rd&#8221;. I am not too worried about exposing this password as it is only the password to a few dozen VMs. This means many of the scripts from my books contain the password in clear text. While great for books, this is not a best practice in production.<\/p>\n<p>Over the years there have been numerous attempts at handling secrets. You could store the secrets in an XML file and import the file when you needed those secrets. Or, you could force the user to just retype the password every time they want to use it. Speaking personally &#8211; I get tired real fast of typing a long, complex, password time and time again!<\/p>\n<h2>What are the Secret modules?<\/h2>\n<p>The developers of this module recognized the challenge that users wanted consistency in managing secrets with flexibility over which secret store to use. The solution involves separating secrets management from secrets storage. So there there are <em>two<\/em> modules involved:<\/p>\n<ul>\n<li>SecretManagement &#8211; you use this module in your scripts to make use of secrets.<\/li>\n<li>SecretStore &#8211; this module contains the commands to manage a specific secret storage.<\/li>\n<\/ul>\n<p>You also need a vault-specific module which the SecretsStore module accesses. This layered approach allows you to use any secret store you wish, manage the secrets independently of the physical storage mechanism. You could, in theory, change the secret store and not need to change your scripts that use the secrets.<\/p>\n<h2>Installing the Modules<\/h2>\n<p>If you want to follow along with the code and do not fancy cut\/paste, I have created a GitHub Gist for the code you see in this article. You can find it <a href=\"https:\/\/gist.github.com\/doctordns\/b1a06f7002675ec2bf8f710d3c066182\">here<\/a>.<\/p>\n<pre><code class=\"language-powershell-console\">PS&gt; # 1. Discover the modules\nPS&gt; Find-Module -Name 'Microsoft.PowerShell.Secret*' |\n      Format-Table -Wrap -AutoSize\n\nVersion Name                                  Repository Description\n------- ----                                  ---------- -----------\n1.1.0   Microsoft.PowerShell.SecretManagement PSGallery  This module provides a convenient way for a user\n                                                         to store and retrieve secrets. The secrets are\n                                                         stored in registered extension vaults. An \n                                                         extension vault can store secrets locally or remotely.\n                                                         SecretManagement coordinates access to the secrets\n                                                         through the registered vaults.\n                                                         Go to GitHub for more information about the module\n                                                         and to submit issues:https:\/\/github.com\/powershell\/SecretManagement\n\n1.0.4   Microsoft.PowerShell.SecretStore      PSGallery  This PowerShell module is an extension vault for the\n                                                         PowerShell SecretManagement module.\n                                                         As an extension vault, this module stores secrets to the local\n                                                         machine based on the current user account context. \n                                                         The secrets are encrypted on file using .NETCrypto APIs. \n                                                         A password is required in the default configuration. \n                                                         The configuration can be changed with the provided cmdlets.\n                                                         Go to GitHub for more information about this module \n                                                         and to submit issues: https:\/\/\/\/github.com\/\/powershell\/\/SecretStore\n\nPS&gt; # 2. Install both modules\nPS&gt; Install-Module -Name $Names -Force -AllowClobber<\/code><\/pre>\n<p>When you install the module using <code>Install-Module<\/code> you see no output (unless you use the <code>-Verbose<\/code> switch). You can always use <code>Get-Module<\/code> to check that you have installed these new (to you) modules.<\/p>\n<h2>Discovering the commands available to you<\/h2>\n<p>Once you have thess two modules installed, you can discover the commands in each module:<\/p>\n<pre><code class=\"language-powershell-console\">\nPS&gt; # 3. Examine them\nPS&gt;PS&gt; Get-Module -Name Microsoft*.Secret* -ListAvailable |\n       Format-Table -Property ModuleType, Version, Name, ExportedCmdlets\n\nModuleType Version Name                                  ExportedCmdlets\n---------- ------- ----                                  ---------------\n    Binary 1.1.0   Microsoft.PowerShell.SecretManagement {[Register-SecretVault, Register-SecretVault],\n                                                         [Unregister-SecretVault, Unregister-SecretVault], [Get-SecretVault,   \n                                                         Get-SecretVault], [Set-SecretVaultDefault, Set-SecretVaultDefault],   \n                                                         [Test-SecretVault, Test-SecretVault], [Set-Secret, Set-Secret],       \n                                                         [Set-SecretInfo, Set-SecretInfo], [Get-Secret, Get-Secret],\n                                                         [Get-SecretInfo, Get-SecretInfo], [Remove-Secret, Remove-Secret],\n                                                         [Unlock-SecretVault, Unlock-SecretVault]}\n    Binary 1.0.5   Microsoft.PowerShell.SecretStore      {[Unlock-SecretStore, Unlock-SecretStore], [Set-SecretStorePassword,  \n                                                         Set-SecretStorePassword], [Get-SecretStoreConfiguration,\n                                                         Get-SecretStoreConfiguration], [Set-SecretStoreConfiguration,\n                                                         Set-SecretStoreConfiguration], [Reset-SecretStore,\n                                                         Reset-SecretStore]}\n<\/code><\/pre>\n<p>As you can see, both modules have a number of commands you may need to use to manage secrets for your environment. Also &#8211; depending on your screen width you may find your output is slightly diffetrent although it should contain the same information.<\/p>\n<h2>Registering and viewing a secret vault<\/h2>\n<p>After you have the two modules installed, your next step is to register a secret vault. There are several vault options you can take advantage of, for this post, I&#8217;ll use the built-in default vault. You configure the default vault like this:<\/p>\n<pre><code class=\"language-powershell-console\">PS&gt; # 4. Register the default secrets provider\nPS&gt; $Mod = 'Microsoft.PowerShell.SecretStore'\nPS&gt; Register-SecretVault -Name RKSecrets -ModuleName $Mod -DefaultVault\nPS&gt; Get-SecretVault\n\nName      ModuleName                       IsDefaultVault\n----      ----------                       --------------\nRKSecrets Microsoft.PowerShell.SecretStore True<\/code><\/pre>\n<p>Like the previous step, registering the vault does not create any output by default. You can view the vault you just created by using the <code>Get-SecretVault<\/code> command.<\/p>\n<h2>Setting a secret<\/h2>\n<p>To create a new secret in your secret vault, you use the <code>Set-Secret<\/code> command, like this:<\/p>\n<pre><code class=\"language-powershell-console\">\nPS&gt; # 4. Register the default secrets provider\nPS&gt; Import-Module -Name 'Microsoft.PowerShell.SecretManagement'\nPS&gt; Import-Module -Name 'Microsoft.PowerShell.SecretStore'\nPS&gt; $Mod = 'Microsoft.PowerShell.SecretStore'\nPS&gt; Register-SecretVault -Name RKSecrets -ModuleName $Mod -DefaultVault\nPS&gt; # 5. View Secret vault\nPS&gt; Get-SecretVault\n\nName      ModuleName                       IsDefaultVault\n----      ----------                       --------------\nRKSecrets Microsoft.PowerShell.SecretStore True\n\nPS C:Foo&gt; # 6. Set the Admin password secret for Reskit forest\nPS C:Foo&gt; Set-Secret -Name ReskitAdmin -Secret 'Pa$$w0rd'\nCreating a new RKSecrets vault. A password is required by the current store configuration.\nEnter password:\n**********\nEnter password again for verification:\n**********<\/code><\/pre>\n<p>This code fragment explicitly loads both of the downloaded modules. If you use PowerShell module automatic loading, this is unnecessary.<\/p>\n<p>Also, the first time you use <code>Set-Secret<\/code> to create a secret, the cmdlet prompts for a vault password. Note this password isd NOT stored in the AD &#8211; so don&#8217;t forget it!!!<\/p>\n<p>As an aside &#8211; I hope you noticed the bad practice in the above code &#8211; using a clear text password in a script file. A better approach to this <em>for production coding<\/em> would be to use <code>Read-Host<\/code> to have the password passed in. In this case, you see the actual password I set, and later see that this password was indeed saved and retreived correctly.<\/p>\n<h2>Using secrets stored in your secret vault<\/h2>\n<p>Now that you have set a password in the RKSecrets vault, you can use the <code>Get-Secret<\/code> cmdlet to retrieve the secret. As you can see here, although you set a plain text password, <code>Get-Secret<\/code> returns the secret as a secure string.<\/p>\n<pre><code class=\"language-powershell-console\">PS&gt; # 7. Create a credential object using the secet\nPS&gt; $User = 'Reskit\\Administrator'\nPS&gt; $PwSS = Get-Secret ReskitAdmin\nPS&gt; $Cred = [System.Management.Automation.PSCredential]::New($User,$PwSS)\nPS&gt; # 8. Let's cheat and see what the password is first.\nPS&gt; $PW = $Cred.GetNetworkCredential().Password\nPS&gt; \"Password for this credential is [$PW]\"\nPassword for this credential is [Pa$$w0rd]\nPS&gt; # 9. Using the credential against DC1\nPS&gt; $Cmd = {hostname.exe}\nPS&gt; Invoke-Command -ComputerName DC1 -Credential $Cred -ScriptBlock $Cmd\nDC1<\/code><\/pre>\n<p>As you can see, it is straightforward to create a new credential object using a password retrieved from the vault. This code creates a new PSCredential object, because that is what PowerShell cmdlets use to authenticate remoting sessions. You can use the credential object&#8217;s <code>GetNetworkCredential()<\/code> method to retrieve the plain text password.<\/p>\n<p>If you are running this code, the first time you create a vault, the secrets module requires you to specify a vault password. Depending on what sequence of commands you enter and how quickly, you may be asked to re-enter your vault password.<\/p>\n<h2>Using Metadata<\/h2>\n<p>If you have a large numbers of secrets to manage, you can add additional metadata to help you keep track of the secrets you set. Metadata is a simple hash table containing the metadata you wish to apply to a secret. Each item in the hash table is a key-value pair. The keys can be anything you wish such as the purpose of the script and the script author. You use <code>Set-Secret<\/code> to add metadata to an existing (or new) secret. To set the metadata, you can use the <code>Get-SecretInfo<\/code> cmdlet. Creating and using metadata looks like this:<\/p>\n<pre><code class=\"language-powershell-console\">PS&gt; # 10. Setting metadata\nPS&gt; Set-Secret -Name ReskitAdmin -Secret 'Pa$$w0rd' -Metadata @{Purpose=\"Reskit.Org Enterprise\\Domain Admin PW\"}\nPS&gt; Get-SecretInfo -Name ReskitAdmin | Select-Object -Property Name, Metadata\n\nName        Metadata\n----        --------\nReskitAdmin {[Purpose, Reskit.Org Enterprise\/Domain Admin PW]}\n\nPS&gt; # 11. Updating the metadata\nPS&gt; Set-SecretInfo -Name ReskitAdmin -Metadata @{Author = 'DoctorDNS@Gmail.Com';\n                                             Purpose=\"Reskit.Org Enterprise\\Domain Admin PW\"}\nPS&gt; # 12. View secret information with metadata\nPS&gt; Get-SecretInfo -Name ReskitAdmin | Select-Object -Property Name, Metadata\n\nName        Metadata\n----        --------\nReskitAdmin {[Purpose, Reskit.Org Enterprise\\Domain Admin PW], \n             [Author, DoctorDNS@Gmail.Com]}<\/code><\/pre>\n<p>As noted, Metadata can be any key-value pair you wish to add to the secret. In this case, the code set two metadata items: the purpose of the secret and its author. Feel free to add whatever metadata makes sense to you and your organization.<\/p>\n<h2>Summary<\/h2>\n<p>The two secrets modules provide a great way to use secrets in your PowerShell scripts and keep the secrets secure. These two modules work both with Windows PowerShell and PowerShell 7. The default secrets vault works well enough for most cases, but you have options. If there is an interest, I can create a further blog post to look at using different secret vaults.<\/p>\n<p>So stop using plain text secrets in your PowerShell scripts and use the secrets modules.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Q: I have a bunch of scripts we use in production that make use of Windows credentials. In some cases, these scripts have an actual password in plain text, while others read the password from an XML file. Is there a better way? A: Scripts with high-privilege account passwords in plain text is not a [&hellip;]<\/p>\n","protected":false},"author":4034,"featured_media":77,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13],"tags":[56,55,54],"class_list":["post-533","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-credentials","tag-passwords","tag-secretmanagement"],"acf":[],"blog_post_summary":"<p>Q: I have a bunch of scripts we use in production that make use of Windows credentials. In some cases, these scripts have an actual password in plain text, while others read the password from an XML file. Is there a better way? A: Scripts with high-privilege account passwords in plain text is not a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts\/533","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/users\/4034"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/comments?post=533"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/posts\/533\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/media\/77"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/media?parent=533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/categories?post=533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell-community\/wp-json\/wp\/v2\/tags?post=533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}