├── .config └── tsaoptions.json ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── Bug_Report.yaml │ ├── Feature_Request.yaml │ └── config.yml ├── SECURITY.md └── workflows │ └── ci-test.yml ├── .gitignore ├── .pipelines └── SecretManagement-Official.yml ├── CHANGELOG.md ├── Directory.Build.props ├── Directory.Packages.props ├── Docs └── ARCHITECTURE.md ├── Ev2Specs └── ServiceGroupRoot │ ├── RolloutSpec.json │ ├── ScopeBindings.json │ ├── SecretManagementToACR.Rollout.json │ ├── ServiceModel.json │ ├── Shell │ └── Run │ │ └── Run.ps1 │ └── buildver.txt ├── ExtensionModules ├── AKVaultScript │ ├── AKVaultScript.Extension │ │ ├── AKVaultScript.Extension.psd1 │ │ └── AKVaultScript.Extension.psm1 │ └── AKVaultScript.psd1 ├── CredManStore │ ├── Microsoft.PowerShell.CredManStore.Extension │ │ └── Microsoft.PowerShell.CredManStore.Extension.psd1 │ ├── README.md │ ├── Tests │ │ └── Microsoft.PowerShell.CredManStore.Tests.ps1 │ ├── help │ │ └── en-US │ │ │ └── about_Microsoft.PowerShell.CredManStore.md │ └── src │ │ ├── Microsoft.PowerShell.CredManStore.psd1 │ │ └── code │ │ ├── CredManStore.cs │ │ ├── Microsoft.PowerShell.CredManStore.csproj │ │ └── Utils.cs └── TestLocalScript │ ├── TestLocalScript.Extension │ ├── TestLocalScript.Extension.psd1 │ └── TestLocalScript.Extension.psm1 │ └── TestLocalScript.psd1 ├── LICENSE ├── README.md ├── SecretManagement.build.ps1 ├── SecretManagement.sln ├── ThirdPartyNotices.txt ├── global.json ├── help ├── Get-Secret.md ├── Get-SecretInfo.md ├── Get-SecretVault.md ├── Register-SecretVault.md ├── Remove-Secret.md ├── Set-Secret.md ├── Set-SecretInfo.md ├── Set-SecretVaultDefault.md ├── Test-SecretVault.md ├── Unlock-SecretVault.md └── Unregister-SecretVault.md ├── nuget.config ├── src ├── Microsoft.PowerShell.SecretManagement.format.ps1xml ├── Microsoft.PowerShell.SecretManagement.psd1 └── code │ ├── Microsoft.PowerShell.SecretManagement.Library.nuspec │ ├── Microsoft.PowerShell.SecretManagement.csproj │ ├── SecretManagement.cs │ ├── Utils.cs │ └── images │ └── PowerShell_64.png ├── test └── Microsoft.PowerShell.SecretManagement.Tests.ps1 └── tools ├── installPSResources.ps1 └── updateVersion.ps1 /.config/tsaoptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "instanceUrl": "https://msazure.visualstudio.com", 3 | "projectName": "One", 4 | "areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell", 5 | "notificationAliases": [ "andschwa@microsoft.com", "slee@microsoft.com" ], 6 | "codebaseName": "PowerShell_SecretManagement_20240903", 7 | "tools": [ "CredScan", "PoliCheck", "BinSkim" ] 8 | } 9 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | - Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_Report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug report 🐛 2 | description: Report errors or unexpected behavior 🤔 3 | labels: 4 | - Needs-Triage 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: > 9 | This repository is **ONLY** for issues related to SecretManagement. 10 | - type: checkboxes 11 | attributes: 12 | label: Prerequisites 13 | options: 14 | - label: Write a descriptive title. 15 | required: true 16 | - label: Make sure you are able to repro it on the [latest released version](https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretManagement) 17 | required: true 18 | - label: Search the existing issues. 19 | required: true 20 | - type: textarea 21 | attributes: 22 | label: Steps to reproduce 23 | description: > 24 | List of steps, sample code, failing test or link to a project that reproduces the behavior. 25 | Make sure you place a stack trace inside a code (```) block to avoid linking unrelated issues. 26 | placeholder: > 27 | I am experiencing a problem with X. 28 | I think Y should be happening but Z is actually happening. 29 | validations: 30 | required: true 31 | - type: textarea 32 | attributes: 33 | label: Expected behavior 34 | render: console 35 | placeholder: | 36 | PS> 2 + 2 37 | 4 38 | validations: 39 | required: true 40 | - type: textarea 41 | attributes: 42 | label: Actual behavior 43 | render: console 44 | placeholder: | 45 | PS> 2 + 2 46 | 5 47 | validations: 48 | required: true 49 | - type: textarea 50 | attributes: 51 | label: Error details 52 | description: Paste verbatim output from `Get-Error` if PowerShell returns an error. 53 | render: console 54 | placeholder: PS> Get-Error 55 | - type: textarea 56 | attributes: 57 | label: Environment data 58 | description: Paste verbatim output from `$PSVersionTable` below. 59 | render: PowerShell 60 | placeholder: PS> $PSVersionTable 61 | validations: 62 | required: true 63 | - type: input 64 | validations: 65 | required: true 66 | attributes: 67 | label: Version 68 | description: Specify the version of Crescendo you are using. 69 | - type: textarea 70 | attributes: 71 | label: Visuals 72 | description: > 73 | Please upload images or animations that can be used to reproduce issues in the area below. 74 | Try the [Steps Recorder](https://support.microsoft.com/en-us/windows/record-steps-to-reproduce-a-problem-46582a9b-620f-2e36-00c9-04e25d784e47) 75 | on Windows or [Screenshot](https://support.apple.com/en-us/HT208721) on macOS. 76 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_Request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request / Idea 🚀 2 | description: Suggest a new feature or improvement (this does not mean you have to implement it) 3 | labels: 4 | - Issue-Enhancement 5 | - Needs-Triage 6 | body: 7 | - type: textarea 8 | attributes: 9 | label: Summary of the new feature / enhancement 10 | description: > 11 | A clear and concise description of what the problem is that the 12 | new feature would solve. Try formulating it in user story style 13 | (if applicable). 14 | placeholder: "'As a user I want X so that Y...' with X being the being the action and Y being the value of the action." 15 | validations: 16 | required: true 17 | - type: textarea 18 | attributes: 19 | label: Proposed technical implementation details (optional) 20 | placeholder: > 21 | A clear and concise description of what you want to happen. 22 | Consider providing an example experience with expected result. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: PowerShell Issues 4 | url: https://github.com/PowerShell/PowerShell/issues/new 5 | about: PowerShell issues or suggestions. 6 | - name: Windows PowerShell Issues 7 | url: https://support.microsoft.com/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332 8 | about: Windows PowerShell issues or suggestions. 9 | - name: Support 10 | url: https://github.com/PowerShell/PowerShell/blob/master/.github/SUPPORT.md 11 | about: PowerShell Support Questions/Help 12 | - name: Documentation Issue 13 | url: https://github.com/MicrosoftDocs/PowerShell-Docs-Modules/issues/new/choose 14 | about: Please open issues on documentation for SecretManagement here. 15 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin) and [PowerShell](https://github.com/PowerShell). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /.github/workflows/ci-test.yml: -------------------------------------------------------------------------------- 1 | name: CI Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | merge_group: 9 | types: [ checks_requested ] 10 | 11 | jobs: 12 | ci: 13 | name: pester 14 | strategy: 15 | matrix: 16 | os: [ windows-latest, macos-latest, ubuntu-latest ] 17 | runs-on: ${{ matrix.os }} 18 | env: 19 | DOTNET_NOLOGO: true 20 | DOTNET_GENERATE_ASPNET_CERTIFICATE: false 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Install dotnet 26 | uses: actions/setup-dotnet@v4 27 | with: 28 | cache: true 29 | cache-dependency-path: '**/*.csproj' 30 | dotnet-version: 8.x 31 | 32 | - name: Install PSResources 33 | run: ./tools/installPSResources.ps1 34 | shell: pwsh 35 | 36 | - name: Build and test 37 | run: Invoke-Build -Configuration Release -Task Build, Package, Test 38 | shell: pwsh 39 | 40 | - name: Test Windows PowerShell 41 | run: | 42 | Install-Module Pester -Scope CurrentUser -Force -SkipPublisherCheck 43 | Invoke-Pester Test 44 | if: matrix.os == 'windows-latest' 45 | shell: powershell 46 | 47 | - name: Upload build artifacts 48 | uses: actions/upload-artifact@v4 49 | if: always() 50 | with: 51 | name: SecretManagement-package-${{ matrix.os }} 52 | path: out/**/*.nupkg 53 | 54 | - name: Upload test results 55 | uses: actions/upload-artifact@v4 56 | if: always() 57 | with: 58 | name: SecretManagement-tests-${{ matrix.os }} 59 | path: testResults.xml 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | artifacts/ 2 | module/ 3 | bin/ 4 | obj/ 5 | out/ 6 | testResults.xml 7 | -------------------------------------------------------------------------------- /.pipelines/SecretManagement-Official.yml: -------------------------------------------------------------------------------- 1 | ################################################################################# 2 | # OneBranch Pipelines # 3 | # This pipeline was created by EasyStart from a sample located at: # 4 | # https://aka.ms/obpipelines/easystart/samples # 5 | # Documentation: https://aka.ms/obpipelines # 6 | # Yaml Schema: https://aka.ms/obpipelines/yaml/schema # 7 | # Retail Tasks: https://aka.ms/obpipelines/tasks # 8 | # Support: https://aka.ms/onebranchsup # 9 | ################################################################################# 10 | 11 | trigger: 12 | - main 13 | 14 | schedules: 15 | - cron: '40 18 * * 2' 16 | displayName: Weekly CodeQL 17 | branches: 18 | include: 19 | - main 20 | always: true 21 | 22 | parameters: 23 | - name: debug 24 | displayName: Enable debug output 25 | type: boolean 26 | default: false 27 | 28 | variables: 29 | - name: system.debug 30 | value: ${{ parameters.debug }} 31 | - name: BuildConfiguration 32 | value: Release 33 | - name: WindowsContainerImage 34 | value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest 35 | - name: DOTNET_NOLOGO 36 | value: true 37 | - name: DOTNET_GENERATE_ASPNET_CERTIFICATE 38 | value: false 39 | - group: SecretManagementAcr 40 | 41 | resources: 42 | repositories: 43 | - repository: templates 44 | type: git 45 | name: OneBranch.Pipelines/GovernedTemplates 46 | ref: refs/heads/main 47 | 48 | extends: 49 | # https://aka.ms/obpipelines/templates 50 | template: v2/OneBranch.Official.CrossPlat.yml@templates 51 | parameters: 52 | globalSdl: # https://aka.ms/obpipelines/sdl 53 | asyncSdl: 54 | enabled: true 55 | forStages: [build] 56 | featureFlags: 57 | EnableCDPxPAT: false 58 | WindowsHostVersion: 59 | Version: 2022 60 | Network: KS3 61 | stages: 62 | - stage: build 63 | jobs: 64 | - job: main 65 | displayName: Build package 66 | pool: 67 | type: windows 68 | variables: 69 | ob_outputDirectory: $(Build.SourcesDirectory)/out 70 | steps: 71 | - pwsh: | 72 | [xml]$xml = Get-Content Directory.Build.props 73 | $version = $xml.Project.PropertyGroup.ModuleVersion 74 | Write-Output "##vso[task.setvariable variable=version;isOutput=true]$version" 75 | name: package 76 | displayName: Get version from project properties 77 | - task: onebranch.pipeline.version@1 78 | displayName: Set OneBranch version 79 | inputs: 80 | system: Custom 81 | customVersion: $(package.version) 82 | - task: UseDotNet@2 83 | displayName: Use .NET SDK 84 | inputs: 85 | packageType: sdk 86 | useGlobalJson: true 87 | - pwsh: ./tools/installPSResources.ps1 -PSRepository CFS 88 | displayName: Install PSResources 89 | - pwsh: Invoke-Build -Configuration $(BuildConfiguration) -Task Build, Test 90 | displayName: Build 91 | - task: onebranch.pipeline.signing@1 92 | displayName: Sign 1st-party files in module 93 | inputs: 94 | command: sign 95 | signing_profile: external_distribution 96 | search_root: $(Build.SourcesDirectory)/module 97 | files_to_sign: | 98 | Microsoft.*.dll; 99 | Microsoft.*.psd1; 100 | Microsoft.*.ps1xml; 101 | - task: onebranch.pipeline.signing@1 102 | displayName: Sign 1st-party files in library 103 | inputs: 104 | command: sign 105 | signing_profile: external_distribution 106 | search_root: $(Build.SourcesDirectory)/artifacts 107 | files_to_sign: | 108 | publish/**/Microsoft.PowerShell.SecretManagement.dll; 109 | refs/**/Microsoft.PowerShell.SecretManagement.dll; 110 | - task: ArchiveFiles@2 111 | displayName: Zip module 112 | inputs: 113 | rootFolderOrFile: $(Build.SourcesDirectory)/module 114 | includeRootFolder: false 115 | archiveType: zip 116 | archiveFile: out/SecretManagement-v$(package.version).zip 117 | - pwsh: Invoke-Build -Configuration $(BuildConfiguration) Package 118 | displayName: Package module 119 | - task: onebranch.pipeline.signing@1 120 | displayName: Sign NuGet package 121 | inputs: 122 | command: sign 123 | signing_profile: external_distribution 124 | search_root: $(Build.SourcesDirectory)/out 125 | files_to_sign: | 126 | *.nupkg 127 | - stage: release 128 | dependsOn: build 129 | condition: eq(variables['Build.Reason'], 'Manual') 130 | variables: 131 | version: $[ stageDependencies.build.main.outputs['package.version'] ] 132 | drop: $(Pipeline.Workspace)/drop_build_main 133 | jobs: 134 | - job: github 135 | displayName: Publish draft to GitHub 136 | pool: 137 | type: windows 138 | variables: 139 | ob_outputDirectory: $(Build.SourcesDirectory)/out 140 | steps: 141 | - download: current 142 | displayName: Download artifacts 143 | - task: GitHubRelease@1 144 | displayName: Create GitHub release 145 | inputs: 146 | gitHubConnection: GitHub 147 | repositoryName: PowerShell/SecretManagement 148 | assets: | 149 | $(drop)/Microsoft.PowerShell.SecretManagement.$(version).nupkg 150 | $(drop)/Microsoft.PowerShell.SecretManagement.Library.$(version).nupkg 151 | $(drop)/SecretManagement-v$(version).zip 152 | tagSource: userSpecifiedTag 153 | tag: v$(version) 154 | isDraft: true 155 | addChangeLog: false 156 | releaseNotesSource: inline 157 | releaseNotesInline: "" 158 | - job: validation 159 | displayName: Manual validation 160 | pool: 161 | type: agentless 162 | timeoutInMinutes: 1440 163 | steps: 164 | - task: ManualValidation@0 165 | displayName: Wait 24 hours for validation 166 | inputs: 167 | notifyUsers: $(Build.RequestedForEmail) 168 | instructions: Please validate the release and then publish it! 169 | timeoutInMinutes: 1440 170 | - job: publish 171 | dependsOn: validation 172 | displayName: Publish to PowerShell Gallery 173 | pool: 174 | type: windows 175 | variables: 176 | ob_outputDirectory: $(Build.SourcesDirectory)/out 177 | steps: 178 | - download: current 179 | displayName: Download artifacts 180 | - task: NuGetCommand@2 181 | displayName: Publish module to PowerShell Gallery 182 | inputs: 183 | command: push 184 | packagesToPush: $(drop)/Microsoft.PowerShell.SecretManagement.$(version).nupkg 185 | nuGetFeedType: external 186 | publishFeedCredentials: PowerShellGallery 187 | - task: NuGetCommand@2 188 | displayName: Publish library to NuGet 189 | inputs: 190 | command: push 191 | packagesToPush: $(drop)/Microsoft.PowerShell.SecretManagement.Library.$(version).nupkg 192 | nuGetFeedType: external 193 | publishFeedCredentials: PowerShellNuGetOrgPush 194 | - stage: PrepForEv2 195 | condition: eq(variables['Build.Reason'], 'Manual') 196 | dependsOn: build 197 | variables: 198 | drop: $(Pipeline.Workspace)/drop_build_main 199 | version: $[ stageDependencies.build.main.outputs['package.version'] ] 200 | jobs: 201 | - job: CopyEv2FilesToArtifact 202 | displayName: Copy Ev2 Files To Artifact 203 | variables: 204 | - name: ob_outputDirectory 205 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 206 | pool: 207 | timeoutInMinutes: 30 208 | type: windows 209 | steps: 210 | - task: onebranch.pipeline.signing@1 211 | displayName: Sign 1st Party Files 212 | inputs: 213 | command: 'sign' 214 | signing_profile: external_distribution 215 | files_to_sign: '**\*.ps1' 216 | search_root: '$(Build.SourcesDirectory)/EV2Specs/ServiceGroupRoot/Shell' 217 | - download: current 218 | displayName: Download artifacts 219 | - task: CopyFiles@2 220 | inputs: 221 | SourceFolder: $(drop) 222 | Contents: Microsoft.PowerShell.SecretManagement.$(version).nupkg 223 | TargetFolder: $(Build.SourcesDirectory)/EV2Specs/ServiceGroupRoot/SrcFiles/ 224 | - task: ArchiveFiles@2 225 | inputs: 226 | rootFolderOrFile: $(Build.SourcesDirectory)/EV2Specs/ServiceGroupRoot/Shell/Run 227 | includeRootFolder: false 228 | archiveType: tar 229 | tarCompression: none 230 | archiveFile: $(Build.SourcesDirectory)/EV2Specs/ServiceGroupRoot/Shell/Run.tar 231 | displayName: Compress Run script into tar file as needed for EV2 Shell extension 232 | - pwsh: | 233 | $pathToJsonFile = '$(Build.SourcesDirectory)/EV2Specs/ServiceGroupRoot/SecretManagementToACR.Rollout.json' 234 | $content = Get-Content -Path $pathToJsonFile | ConvertFrom-Json 235 | 236 | $environmentVariables = @() 237 | 238 | $environmentVariables += [PSCustomObject]@{name="DESTINATION_ACR_NAME"; value='$(acr_name)'} 239 | $environmentVariables += [PSCustomObject]@{name="DESTINATION_ACR_URI"; value='$(acr_uri)'} 240 | $environmentVariables += [PSCustomObject]@{name="MI_NAME"; value='$(managed_identity_name)'} 241 | $environmentVariables += [PSCustomObject]@{name="MI_CLIENTID"; value='$(managed_identity_clientid)'} 242 | $environmentVariables += [PSCustomObject]@{name="SECRET_MANAGEMENT_VERSION"; value='$(version)'} 243 | $environmentVariables += [PSCustomObject]@{name="SECRET_MANAGEMENT_MODULE"; reference=[PSCustomObject]@{path="SrcFiles\\Microsoft.PowerShell.SecretManagement.$(version).nupkg"}} 244 | 245 | $content.shellExtensions.launch.environmentVariables = $environmentVariables 246 | 247 | $identityString = "/subscriptions/$(acr_subscription)/resourcegroups/$(acr_resource_group)/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$(managed_identity_name)" 248 | $content.shellExtensions.launch.identity.userAssignedIdentities[0] = $identityString 249 | 250 | Remove-Item -Path $pathToJsonFile 251 | $content | ConvertTo-Json -Depth 6 | Out-File $pathToJsonFile 252 | displayName: 'Replace values in SecretManagementToACR.Rollout.json file' 253 | - pwsh: | 254 | $pathToJsonFile = Join-Path -Path '$(Build.SourcesDirectory)/EV2Specs/ServiceGroupRoot' -ChildPath 'RolloutSpec.json' 255 | $content = Get-Content -Path $pathToJsonFile | ConvertFrom-Json 256 | $content.RolloutMetadata.Notification.Email.To = '$(email_address)' 257 | 258 | Remove-Item -Path $pathToJsonFile 259 | $content | ConvertTo-Json -Depth 4 | Out-File $pathToJsonFile 260 | 261 | displayName: 'Replace values in RolloutSpecPath.json' 262 | - pwsh: | 263 | $pathToJsonFile = Join-Path -Path '$(Build.SourcesDirectory)/EV2Specs/ServiceGroupRoot' -ChildPath 'ServiceModel.json' 264 | $content = Get-Content -Path $pathToJsonFile | ConvertFrom-Json 265 | $content.ServiceResourceGroups[0].AzureResourceGroupName = '$(acr_resource_group)' 266 | $content.ServiceResourceGroups[0].AzureSubscriptionId = '$(acr_subscription)' 267 | 268 | Remove-Item -Path $pathToJsonFile 269 | $content | ConvertTo-Json -Depth 9 | Out-File $pathToJsonFile 270 | 271 | displayName: 'Replace values in ServiceModel.json' 272 | - task: CopyFiles@2 273 | inputs: 274 | Contents: 'EV2Specs/**' 275 | TargetFolder: $(ob_outputDirectory) 276 | - stage: 'Prod_release' 277 | displayName: Deploy Images to ACR with EV2 278 | dependsOn: 279 | - PrepForEV2 280 | variables: 281 | - name: ob_release_environment 282 | value: "Production" 283 | - name: repoRoot 284 | value: $(Build.SourcesDirectory) 285 | jobs: 286 | - job: Prod_ReleaseJob 287 | pool: 288 | type: release 289 | steps: 290 | - download: current 291 | displayName: Download artifacts 292 | - task: vsrm-ev2.vss-services-ev2.adm-release-task.ExpressV2Internal@1 293 | displayName: 'Ev2: Push to ACR' 294 | inputs: 295 | UseServerMonitorTask: true 296 | EndpointProviderType: ApprovalService 297 | ApprovalServiceEnvironment: Production 298 | ServiceRootPath: '$(Pipeline.Workspace)/drop_PrepForEV2_CopyEv2FilesToArtifact/EV2Specs/ServiceGroupRoot' 299 | RolloutSpecPath: '$(Pipeline.Workspace)/drop_PrepForEV2_CopyEv2FilesToArtifact/EV2Specs/ServiceGroupRoot/RolloutSpec.json' 300 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 1.1.2 - 2022-01-27 4 | 5 | ### Fixes 6 | 7 | - Fix for type initialization error on WindowsPowerShell (Issue #89) 8 | 9 | ### Changes 10 | 11 | ### New Features 12 | 13 | ## 1.1.1 - 2021-10-8 14 | 15 | ### Fixes 16 | 17 | - Fix for disabled user interaction after tab completion (Issue #174) 18 | 19 | ### Changes 20 | 21 | ### New Features 22 | 23 | ## 1.1.0 - 2021-6-1 24 | 25 | ### Fixes 26 | 27 | - Extension vault data streams (Warning, Verbose, etc.) now honor cmdlet stream direction commands (Issue #151) 28 | 29 | ### Changes 30 | 31 | ### New Features 32 | 33 | ## 1.1.0-preview - 2021-5-24 34 | 35 | ### Fixes 36 | 37 | - Provide better error message when extension vault does not provide required functions (Issue #137) 38 | 39 | - Provide better error message when running under Windows built-in accounts (Issue #143) 40 | 41 | - SecretManagement now runs in ConstrainedLanguage mode (Issue #144) 42 | 43 | ### Changes 44 | 45 | - Set-SecretInfo now takes pipeline input (Issue #129). 46 | 47 | - Extension vaults are now loaded and run in a separate PowerShell runspace session (Issue #144) 48 | 49 | - Extension vaults can now optionally support a `Unlock-SecretVault` function (Issue #147) 50 | 51 | ### New Features 52 | 53 | - `Unlock-SecretVault` command added to SecretManagement (Issue #147) 54 | 55 | ## 1.0.0 - 2021-4-5 56 | 57 | ### Fixes 58 | 59 | - Fix manifest license link (Issue #112) 60 | 61 | - Fix help document md to xml file compilation (Issue #90, #106) 62 | 63 | - Remove unnecessary assert in registry write code (Issue #119) 64 | 65 | ### Changes 66 | 67 | - Add constructor for `SecretInformation` class that accepts metadata as a hash table (Issue #108) 68 | 69 | ### New Features 70 | 71 | ## 0.9.1 - 2021-3-1 72 | 73 | ### Fixes 74 | 75 | - `Get-Secret` and `Remove-Secret` cmdlets now honor the `VaultName` parameter from a piped in `SecretInformation` object (Issue #97) 76 | 77 | - Secret name and vault name autocompletion now correctly handles names with spaces (Issue #91) 78 | 79 | ### Changes 80 | 81 | - A warning is now displayed when secret cmdlets are used and no vaults are currently registered 82 | 83 | ### New Features 84 | 85 | - `SecretInformation` class now has a new `Metadata` property to support the new secret metadata support (Issue #46) 86 | 87 | - `Set-Secret` cmdlet now has a new optional `-Metadata` property to include additional non-sensitive data associated with a secret (Issue #46) 88 | 89 | - New `Set-SecretInfo` cmdlet that takes a `-Metadata` property which sets additional non-sensitive data to a secret (Issue #46) 90 | 91 | ## 0.9.0 - 2021-1-15 92 | 93 | ### Fixes 94 | 95 | - `Register-SecretVault` no longer emits error when strict language mode is set (Issue #81) 96 | 97 | ### Changes 98 | 99 | - `Set-DefaultVault` cmdlet has been renamed to `Set-SecretVaultDefault` (Issue #79) 100 | 101 | - ReadME.md document now includes installation information (Issue #86) 102 | 103 | ### New Features 104 | 105 | ## 0.5.5-Preview5 - 2020-11-16 106 | 107 | ### Fixes 108 | 109 | - Incompatibility with WindowsPowerShell 5.1 (Issue #73) 110 | 111 | ### Changes 112 | 113 | - The first extension vault added will automatically be designated the default vault (Issue #61) 114 | 115 | - `Unregister-SecretVault` `-Name` property now supports string[] type and wild cards (Issue #57,#58) 116 | 117 | - `Register-SecretVault` now checks `-VaultParameters` hashtable for reserved `Verbose` entry and throws error if found 118 | 119 | - `Set-DefaultVault` now has a `-ClearDefault` parameter that designates no registered vault as the default vault 120 | 121 | ### New Features 122 | 123 | - `Register-SecretVault` now supports a `-Description` parameter and registration information will include an optional extension vault description (Issue #46) 124 | 125 | ## 0.5.4-Preview5 - 2020-11-4 126 | 127 | ### Fixes 128 | 129 | ### Changes 130 | 131 | - `Get-Secret` `-Name` parameter now accepts arguments with wild card characters as literals (Issue #67) 132 | 133 | - The `Verbose` parameter switch is now passed to extension vault module functions as an `AdditionalParameters` name/value pair (Issue #66) 134 | 135 | - `Get-SecretVault` `-Name` parameter now takes a `string[]` type argument (Issue #59) 136 | 137 | - `Test-SecretVault` `-Name` parameter now takes a `string[]` type argument and accepts wild card characters (Issue #56) 138 | 139 | - `Register-SecretVault` now has a `-PassThru` parameter to return information on the secret vault just registered 140 | 141 | ### New Features 142 | 143 | - When an extension vault is unregistered and if the vault provides a `Unregister-SecretVault` function, that extension vault function will be called before the extension vault is unregistered (Issue #60) 144 | 145 | - Vault name and Secret name completers have been added to the cmdlets (Issue #35) 146 | 147 | ## 0.5.3-Preview4 - 2020-09-24 148 | 149 | ### Fixes 150 | 151 | - Windows PowerShell cannot register extension vaults (Error: Cannot bind argument to parameter 'Path' ...) 152 | 153 | ### Changes 154 | 155 | - Change SecretVaultInfo `VaultName` property to `Name`, for consistency 156 | 157 | - `Test-SecretVault` `-Vault` parameter changed to `-Name` for consistency 158 | 159 | ### New Features 160 | 161 | - Add `-AllowClobber` parameter switch to `Register-SecretVault`, to allow overwriting existing vault 162 | 163 | - `Register-SecretVault` `-Name` parameter is now optional, and will use module name if not provided 164 | 165 | - `Unregister-SecretVault` now supports `Name` parameter argument from pipeline 166 | 167 | - `Set-DefaultVault` now supports `Name` and `SecretVaultVaultInfo` parameter arguments from pipeline 168 | 169 | - `Set-Secret` now supports `SecretInfo` objects from the pipeline 170 | 171 | - Add `WhatIf` support to `Secret-Secret` 172 | 173 | - Add `WhatIf` support to `Remove-Secret` 174 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 1.1.2 6 | true 7 | 8 | 9 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Docs/ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # PowerShell SecretManagement Module Architecture 2 | 3 | ## Description 4 | 5 | The purpose of the SecretManagement module is to provide secure storage and access of secrets, through registered extension vaults. 6 | The registered extension vaults are PowerShell modules that conform to SecretManagement module requirements. 7 | The extension vaults perform the actual work of authentication, and securely storing and retrieving secrets. 8 | An extension vault can store secrets locally or remotely for a cloud based store. 9 | Extension vault registration registers vaults for the current user context. 10 | So, the SecretManagement module is essentially an orchestrator of extension vaults. 11 | 12 | The SecretManagement module provides commands for registering vault extensions, and accessing vault secrets. 13 | This greatly reduces the temptation to hard code secrets directly into production source code, and instead use the SecretManagement module to dynamically retrieve secrets at runtime. 14 | 15 | ### Using SecretManagement 16 | 17 | ```powershell 18 | Import-Module Microsoft.PowerShell.SecretManagement 19 | 20 | # See what extension vaults are registered for current user 21 | Get-SecretVault 22 | 23 | VaultName ModuleName IsDefaultVault 24 | --------- ---------- -------------- 25 | CredMan Microsoft.PowerShell.CredManStore False 26 | LocalStore Microsoft.PowerShell.SecretStore True 27 | 28 | # Publish a module to the PowerShell Gallery, using a key from the SecretManagement default extension vault 29 | Publish-Module -Path C:\Modules\Publish\MyNewModule -NuGetApiKey (Get-Secret NuGetApiKey -AsPlainText) 30 | 31 | # Run management script on multiple machines, using stored credentials from the default extension vault 32 | Invoke-Command -Cn $machines -FilePath .\MyMgmtScript.ps1 -Credential (Get-Secret MgmtCred) 33 | ``` 34 | 35 | The design is based on the [SecretManagement RFC](https://github.com/PowerShell/PowerShell-RFC/pull/208). 36 | 37 | ## Supported secret types 38 | 39 | Secret objects supported by this module are currently limited to: 40 | 41 | - byte[] - Blob secret 42 | 43 | - string - String secret 44 | 45 | - SecureString - Secure string secret 46 | 47 | - PSCredential - PowerShell credential secret 48 | 49 | - Hashtable - Hash table of name value pairs, where values are restricted to the above secret types. 50 | 51 | ## Secret metadata 52 | 53 | Extension vaults can optionally support storing and retrieving additional secret metadata, that is data associated with the secret. 54 | Secret metadata is not security sensitive and does not need to be stored securely in the extension vault. 55 | 56 | Secret metadata can be included by using the `-Metadata` parameter: `Set-Secret -Metadata @{ Name=Value }`. 57 | The `-Metadata` parameter takes a `Hashtable` type argument consisting of name/value pairs. 58 | Extension vaults should at minimum support the following value types: 59 | 60 | - string 61 | 62 | - int 63 | 64 | - DateTime 65 | 66 | The secret metadata is included in the `SecretInformation` type object returned by `Get-SecretInfo`, in a `Metadata` property. 67 | The `Metadata` property is a `ReadonlyDictionary` type. 68 | 69 | ## Vault extension hosting 70 | 71 | Extension vault modules are hosted in a separate PowerShell runspace session that is separate from the current user PowerShell session. 72 | This provides a layer of isolation between the user and extension vault operations. 73 | But it also means that extension vaults cannot depend on any shared state with the user session, such as variables or loaded modules. 74 | An extension vault must ensure all module dependencies are listed in its manifest, and any required information is passed in explicitly to the module. 75 | 76 | There is one shared component between the extension vault session and the current user session, and that is the PowerShell host (PSHost). 77 | The PSHost is the object used by PowerShell to communicate with a user in an interactive session. 78 | When the extension vault runspace session is created, the current user session PSHost object is transferred to the vault session. 79 | This provides extension vaults with the ability to write error, warning, verbose, etc. information to the host for the user, including directly prompting the user interactively (for example prompting the user for a password). 80 | 81 | Vault extension modules were originally hosted in the same session as the user session. 82 | However, this prevented SecretManagement from running in a constrained language mode session. 83 | Consequently, starting with v1.1, the extension vaults are hosted in a separate runspace process. 84 | 85 | ## Vault extension registration 86 | 87 | Extension vaults are registered to the current user context. 88 | Information about the extension vault is collected via a registration cmdlet and information is stored as a json file in a user context based location. 89 | 90 | Example: 91 | 92 | ``` json 93 | { 94 | "Vaults": { 95 | "CredMan": { 96 | "VaultParameters": {}, 97 | "ModulePath": "C:\\Modules\\Microsoft.PowerShell.CredManStore", 98 | "ModuleName": "Microsoft.PowerShell.CredManStore" 99 | }, 100 | "LocalStore": { 101 | "VaultParameters": { 102 | "DefaultLocation": "None" 103 | }, 104 | "ModulePath": "C:\\Modules\\Microsoft.PowerShell.SecretStore", 105 | "ModuleName": "Microsoft.PowerShell.SecretStore" 106 | } 107 | }, 108 | "DefaultVaultName": "LocalStore" 109 | } 110 | ``` 111 | 112 | Validation checks are performed on each module before being registered. 113 | 114 | ## Extension vaults 115 | 116 | Vault extensions are PowerShell modules that provide five required functions, and one optional function 117 | 118 | ### Extension vault module required functions 119 | 120 | #### Set-Secret 121 | 122 | Adds a secret to the vault 123 | 124 | #### Set-SecretInfo 125 | 126 | Adds or replaces additional secret metadata to an existing secret in the vault. 127 | Metadata is not stored securely. 128 | 129 | #### Get-Secret 130 | 131 | Retrieves a secret from the vault 132 | 133 | #### Remove-Secret 134 | 135 | Removes a secret from the vault 136 | 137 | #### Get-SecretInfo 138 | 139 | Returns information about one or more secrets (but not the secret itself) 140 | 141 | #### Test-SecretVault 142 | 143 | Tests that extension vault functions and returns True or diagnostic errors 144 | 145 | #### Unregister-SecretVault 146 | 147 | This function is called if provided by the extension vault, to allow the extension vault to perform an clean up tasks before the vault extension is unregistered 148 | 149 | ### Unlock-SecretVault 150 | 151 | Unlocks the extension vault through a passed in SecureString password 152 | 153 | #### Verbose and AdditionalParameters 154 | 155 | Each extension vault function takes a set of parameter arguments that includes an `AdditionalParameters` hash table. 156 | The values of the hash table come from the `VaultParameters` field of the registered vault, which are additional parameters an extension vault implementation might need beyond the specific parameters of a particular function. 157 | The `AdditionalParameters` hash table can also include an automatic `Verbose` boolean parameter. 158 | If the extension vault function is being called with the `-Verbose` common parameter, then the `Verbose` boolean parameter will be included in the `AdditionalParameters` hash table. 159 | The `Verbose` parameter is reserved and provided automatically by SecretManagement when verbose output is specified. 160 | It cannot be included in `VaultParameters`. 161 | 162 | Example: 163 | 164 | ```powershell 165 | function Get-Secret 166 | { 167 | [CmdletBinding()] 168 | param ( 169 | [string] $Name, 170 | [string] $VaultName, 171 | [hashtable] $AdditionalParameters 172 | ) 173 | 174 | # Enable verbose output if directed 175 | if ($AdditionalParameters.ContainsKey('Verbose') -and ($AdditionalParameters['Verbose'] -eq $true)) { 176 | $VerbosePreference = 'Continue' 177 | } 178 | 179 | ... 180 | } 181 | ``` 182 | 183 | ### Script module vault extension example 184 | 185 | This is a minimal vault extension example to demonstrate the directory structure and functional requirements of an extension vault module. 186 | The extension vault module name is 'TestVault'. 187 | 188 | #### Module directory structure 189 | 190 | ./TestVault 191 | ./TestVault/TestVault.psd1 192 | ./TestVault/TestStoreImplementation.dll 193 | ./TestVault/TestVault.Extension 194 | ./TestVault/TestVault.Extension/TestVault.Extension.psd1 195 | ./TestVault/TestVault.Extension/TestVault.Extension.psm1 196 | 197 | #### TestVault.psd1 file 198 | 199 | ```powershell 200 | @{ 201 | ModuleVersion = '1.0' 202 | RootModule = '.\TestStoreImplementation.dll' 203 | NestedModules = @('.\TestVault.Extension') 204 | CmdletsToExport = @('Set-TestStoreConfiguration','Get-TestStoreConfiguration') 205 | } 206 | ``` 207 | 208 | 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. 209 | It also declares the required nested module (TestVault.Extension) that exports the five functions required by SecretManagement registration. 210 | 211 | Note that the nested module conforms to the required naming format: 212 | '[ModuleName].Extension' 213 | 214 | 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. 215 | The 'RootModule' and 'CmdletsToExport' entries are only for configuring the TestStore in this specific case, and are not needed in general. 216 | 217 | #### TestVault.Extension.psd1 file 218 | 219 | ```powershell 220 | @{ 221 | ModuleVersion = '1.0' 222 | RootModule = '.\TestVault.Extension.psm1' 223 | RequiredAssemblies = '..\TestStoreImplementation.dll' 224 | FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault') 225 | } 226 | ``` 227 | 228 | This nested module implements and exports the five functions required by SecretManagement. 229 | It also specifies the TestStoreImplementation.dll binary as a 'RequiredAssemblies' because the five exported functions depend on it. 230 | 231 | #### TestVault.Extension.psm1 file 232 | 233 | ```powershell 234 | function Get-Secret 235 | { 236 | [CmdletBinding()] 237 | param ( 238 | [string] $Name, 239 | [string] $VaultName, 240 | [hashtable] $AdditionalParameters 241 | ) 242 | 243 | return [TestStore]::GetItem($Name, $AdditionalParameters) 244 | } 245 | 246 | function Get-SecretInfo 247 | { 248 | [CmdletBinding()] 249 | param ( 250 | [string] $Filter, 251 | [string] $VaultName, 252 | [hashtable] $AdditionalParameters 253 | ) 254 | 255 | # return [TestStore]::GetItemInfo($Filter, $AdditionalParameters) 256 | return @(,[Microsoft.PowerShell.SecretManagement.SecretInformation]::new( 257 | "Name", # Name of secret 258 | "String", # Secret data type [Microsoft.PowerShell.SecretManagement.SecretType] 259 | $VaultName, # Name of vault 260 | $Metadata) # Optional ReadonlyDictionary secret metadata 261 | } 262 | 263 | function Set-Secret 264 | { 265 | [CmdletBinding()] 266 | param ( 267 | [string] $Name, 268 | [object] $Secret, 269 | [string] $VaultName, 270 | [hashtable] $AdditionalParameters, 271 | [hashtable] $Metadata # Optional metadata parameter 272 | ) 273 | 274 | [TestStore]::SetItem($Name, $Secret) 275 | } 276 | 277 | # Optional function 278 | function Set-SecretInfo 279 | { 280 | [CmdletBinding()] 281 | param ( 282 | [string] $Name, 283 | [hashtable] $Metadata, 284 | [string] $VaultName, 285 | [hashtable] $AdditionalParameters 286 | ) 287 | 288 | [TestStore]::SetItemMetadata($Name, $Metadata) 289 | } 290 | 291 | function Remove-Secret 292 | { 293 | [CmdletBinding()] 294 | param ( 295 | [string] $Name, 296 | [string] $VaultName, 297 | [hashtable] $AdditionalParameters 298 | ) 299 | 300 | [TestStore]::RemoveItem($Name) 301 | } 302 | 303 | function Test-SecretVault 304 | { 305 | [CmdletBinding()] 306 | param ( 307 | [string] $VaultName, 308 | [hashtable] $AdditionalParameters 309 | ) 310 | 311 | return [TestStore]::TestVault() 312 | } 313 | 314 | # Optional function 315 | function Unregister-SecretVault 316 | { 317 | [CmdletBinding()] 318 | param ( 319 | [string] $VaultName, 320 | [hashtable] $AdditionalParameters 321 | ) 322 | 323 | # Perform optional work to extension vault before it is unregistered 324 | [TestStore]::RunUnregisterCleanup() 325 | } 326 | 327 | # Optional function 328 | function Unlock-SecretVault 329 | { 330 | [CmdletBinding()] 331 | param ( 332 | [SecureString] $Password, 333 | [string] $VaultName, 334 | [hashtable] $AdditionalParameters 335 | ) 336 | 337 | [TestStore]::UnlockVault($Password) 338 | } 339 | ``` 340 | 341 | This module script implements the five required functions, as cmdlets. 342 | It also implements an optional `Unregister-SecretVault` function that is called during vault extension un-registration. 343 | It also implements an optional function `Set-SecretInfo` function that sets secret metadata to a specific secret in the vault. 344 | It also implements an optional `Unlock-SecretVault` function that unlocks the vault for the current session based on a provided password. 345 | 346 | 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. 347 | 348 | The `Get-Secret` cmdlet writes the retrieved secret value to the output pipeline on return, or null if no secret was found. 349 | It should write an error only if an abnormal condition occurs. 350 | 351 | 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. 352 | 353 | The `Test-SecretVault` cmdlet should write all errors that occur during the test. 354 | But only a single true/false boolean should be written the the output pipeline indicating success. 355 | 356 | The `Unlock-SecretVault` cmdlet is optional and will be called on the extension vault if available. 357 | 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. 358 | 359 | In general, these cmdlets should write to the error stream only for abnormal conditions that prevent successful completion. 360 | And write to the output stream only the data as indicated above, and expected by SecretManagement. 361 | 362 | In addition, these cmdlets should perform proper authentication and provide errors, and instructions to authenticate, as appropriate. 363 | Or prompt the user if needed, for example if a passphrase is required. 364 | 365 | A vault extension doesn't need to provide full implementation of all required functions. 366 | 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. 367 | If a vault extension doesn't support some functionality, then it should write an appropriate error with a meaningful message. 368 | 369 | ## Vault registration cmdlets 370 | 371 | The following cmdlets are provided for vault extension registration. 372 | 373 | ```powershell 374 | Register-SecretVault 375 | Get-SecretVault 376 | Unregister-SecretVault 377 | Set-SecretVaultDefault 378 | Test-SecretVault 379 | ``` 380 | 381 | ### Register-SecretVault 382 | 383 | Registers a PowerShell module as an extension vault for the current user context. 384 | Validation is performed to ensure the module provides the required functions. 385 | 386 | ### Get-SecretVault 387 | 388 | Returns a list of extension vaults currently registered in the user context. 389 | 390 | ### Unregister-SecretVault 391 | 392 | Un-registers an extension vault. 393 | 394 | ### Set-SecretVaultDefault 395 | 396 | Sets one registered extension vault as the default vault. 397 | 398 | ### Test-SecretVault 399 | 400 | Runs an extension vault 'Test-SecretVault' function and returns the test result. 401 | 402 | ### Unlock-SecretVault 403 | 404 | Unlocks the extension vault through a passed in SecureString password. 405 | 406 | ## Secrets cmdlets 407 | 408 | The following cmdlets are provided for manipulating secrets. 409 | 410 | ```powershell 411 | Set-Secret 412 | Get-Secret 413 | Get-SecretInfo 414 | Remove-Secret 415 | ``` 416 | 417 | ### Set-Secret 418 | 419 | Adds a secret to a specified vault. 420 | Returns True or False to indicate success. 421 | 422 | ### Get-Secret 423 | 424 | Returns a single secret for a provided name. 425 | 426 | ### Get-SecretInfo 427 | 428 | Returns information about each secret stored in all registered vaults, but not the secret itself. 429 | The information is returned in a `Microsoft.PowerShell.SecretManagement.SecretInformation` type. 430 | 431 | ### Remove-Secret 432 | 433 | Removes a secret by name from a given vault. 434 | Returns True or False to indicate success. 435 | 436 | ## Security 437 | 438 | ### Extension vault security 439 | 440 | All extension vault module implementations are responsible for working securely. 441 | In addition each extension vault module is responsible for authentication within the current user context, and to provide appropriate informational and error messages to the user, including instructions for authenticating. 442 | Extension vaults are also responsible for prompting the user for a passphrase, if needed, in interactive sessions. 443 | If an extension vault supports optional secret metadata, the metadata is not sensitive information and does not need to be stored securely. 444 | If an extension vault requires a password before use, it can support the optional `Unlock-SecretVault` function to allow the vault to be unlocked from unattended script, without any user interaction. 445 | 446 | ### Intermediate secret objects 447 | 448 | The SecretManagement module makes a best effort to zero out any intermediate secret objects not returned to the user. 449 | For example byte arrays used in storing and retrieving SecureString blobs are zeroed out after use. 450 | But strings are immutable in C# and cannot be easily or reliably altered, and so must rely on the CLR garbage collection. 451 | 452 | ### Plain text secrets 453 | 454 | The SecretManagement module supports both `string` and `SecureString` secret data types. 455 | To avoid inadvertently exposing these secrets as plain text, either in a command shell or logs, these secret types are always returned by `Get-Secret` as `SecureString` objects by default. 456 | `Get-Secret` will return these data types as strings only if the `-AsPlainText` parameter switch is used. 457 | 458 | This behavior also extends to `hashtable` secret types. 459 | Since hashtable entries can include `string` and `SecureString` data types, these appear in the hashtable as `SecureString` unless `-AsPlainText` switch is used. 460 | 461 | On Windows platform, `SecureString` types contain an encrypted version of the text data, using Windows DPAPI, that is keyed to the current user context and local machine. 462 | But for all other platforms (Linux, macOS) this kind of default encryption is not possible so the dotNet `SecureString` type contains an unencrypted blob of the text. 463 | However, `SecureString` still serves a purpose on non-Windows platforms, since it will not expose the plain text directly, and another dotNet API is needed to return the string contents in plain text. 464 | So on non-Windows platforms `SecureString` still provides some security through obscurity (but not encryption). 465 | 466 | ### Extension vault module cross talk 467 | 468 | SecretManagement extension vault modules all run in a single PowerShell runspace session that is separate from the current user PowerShell session, although both sessions run within the same process. 469 | This means there is a layer of isolation between extension vault modules and the user session, but no significant isolation between each loaded extension vault. 470 | Since authentication is usually based on current session or user context, this means a malicious vault extension could harvest secrets from other registered vaults. 471 | 472 | This can easily be done by directly calling each registered vault secret management cmdlets. 473 | 474 | One possible mitigation is for an extension vault to require a passphrase. 475 | But usually the passphrase remains valid for a period of time (e.g., sudo), and the malicious extension vault can obtain secrets during that time. 476 | 477 | The best defense is to use known secure extension vaults from reputable sources, that are signed certified. 478 | 479 | ## Extension vault registry file location 480 | 481 | SecretManagement is designed to be installed and run within a user account on both Windows and non-Windows platforms. 482 | The extension vault registry file is located in a user account protected directory. 483 | 484 | For Windows platforms the location is: 485 | %LOCALAPPDATA%\Microsoft\PowerShell\secretmanagement 486 | 487 | For non-Windows platforms the location: 488 | $HOME/.secretmanagement 489 | 490 | ## Windows Managed Accounts 491 | 492 | SecretManagement does not currently work for Windows managed accounts. 493 | 494 | SecretManagement depends on both %LOCALAPPDATA% folders to store registry information, and Data Protection APIs for safely handling secrets with the .Net `SecureString` type. 495 | However, Windows managed accounts do not have profiles or %LOCALAPPDATA% folders, and Windows Data Protection APIs do not work for managed accounts. 496 | Consequently, SecretManagement will not run under managed accounts. 497 | -------------------------------------------------------------------------------- /Ev2Specs/ServiceGroupRoot/RolloutSpec.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/rolloutSpecification.json", 3 | "contentVersion": "1.0.0.0", 4 | "RolloutMetadata": { 5 | "ServiceModelPath": "ServiceModel.json", 6 | "ScopeBindingsPath": "ScopeBindings.json", 7 | "Name": "OneBranch-Demo-Container-Deployment", 8 | "RolloutType": "Major", 9 | "BuildSource": { 10 | "Parameters": { 11 | "VersionFile": "buildver.txt" 12 | } 13 | }, 14 | "Notification": { 15 | "Email": { 16 | "To": "default" 17 | } 18 | } 19 | }, 20 | "OrchestratedSteps": [ 21 | { 22 | "Name": "UploadSecretManagementToACR", 23 | "TargetType": "ServiceResource", 24 | "TargetName": "SecretManagementToACR", 25 | "Actions": ["Shell/Run"] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /Ev2Specs/ServiceGroupRoot/ScopeBindings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/scopeBindings.json", 3 | "contentVersion": "0.0.0.1", 4 | "scopeBindings": [ 5 | { 6 | "scopeTagName": "Global", 7 | "bindings": [ 8 | { 9 | "find": "__SUBSCRIPTION_ID__", 10 | "replaceWith": "$azureSubscriptionId()" 11 | }, 12 | { 13 | "find": "__RESOURCE_GROUP__", 14 | "replaceWith": "$azureResourceGroup()" 15 | }, 16 | { 17 | "find": "__BUILD_VERSION__", 18 | "replaceWith": "$buildVersion()" 19 | } 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /Ev2Specs/ServiceGroupRoot/SecretManagementToACR.Rollout.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/rolloutParameters.json", 3 | "contentVersion": "1.0.0.0", 4 | "shellExtensions": [ 5 | { 6 | "name": "Run", 7 | "type": "Run", 8 | "properties": { 9 | "maxExecutionTime": "PT2H" 10 | }, 11 | "package": { 12 | "reference": { 13 | "path": "Shell/Run.tar" 14 | } 15 | }, 16 | "launch": { 17 | "command": [ 18 | "/bin/bash", 19 | "-c", 20 | "pwsh ./Run.ps1" 21 | ], 22 | "environmentVariables": [ 23 | { 24 | "name": "SECRET_MANAGEMENT_MODULE", 25 | "reference": 26 | { 27 | "path": "SrcFiles\\Microsoft.PowerShell.SecretManagement.nupkg" 28 | } 29 | }, 30 | { 31 | "name": "DESTINATION_ACR_NAME", 32 | "value": "default" 33 | }, 34 | { 35 | "name": "MI_NAME", 36 | "value": "default" 37 | }, 38 | { 39 | "name": "MI_CLIENTID", 40 | "value": "default" 41 | }, 42 | { 43 | "name": "SECRET_MANAGEMENT_VERSION", 44 | "value": "default" 45 | }, 46 | { 47 | "name": "DESTINATION_ACR_URI", 48 | "value": "default" 49 | } 50 | ], 51 | "identity": { 52 | "type": "userAssigned", 53 | "userAssignedIdentities": [ 54 | "default" 55 | ] 56 | } 57 | } 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /Ev2Specs/ServiceGroupRoot/ServiceModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/serviceModel.json", 3 | "contentVersion": "1.0.0.0", 4 | "ServiceMetadata": { 5 | "ServiceGroup": "OneBranch-SecretManagement", 6 | "Environment": "Test" 7 | }, 8 | "ServiceResourceGroupDefinitions": [ 9 | { 10 | "Name": "OneBranch-SecretManagement-RGDef", 11 | "ServiceResourceDefinitions": [ 12 | { 13 | "Name": "OneBranch-SecretManagement.Shell-SRDef", 14 | "composedOf": { 15 | "extension": { 16 | "shell": [ 17 | { 18 | "type": "Run", 19 | "properties": { 20 | "imageName": "adm-mariner-20-l", 21 | "imageVersion": "v5" 22 | } 23 | } 24 | ] 25 | } 26 | } 27 | } 28 | ] 29 | } 30 | ], 31 | "ServiceResourceGroups": [ 32 | { 33 | "AzureResourceGroupName": "default", 34 | "Location": "East US", 35 | "InstanceOf": "OneBranch-SecretManagement-RGDef", 36 | "AzureSubscriptionId": "default", 37 | "scopeTags": [ 38 | { 39 | "name": "Global" 40 | } 41 | ], 42 | "ServiceResources": [ 43 | { 44 | "Name": "SecretManagementToACR", 45 | "InstanceOf": "OneBranch-SecretManagement.Shell-SRDef", 46 | "RolloutParametersPath": "SecretManagementToACR.Rollout.json" 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /Ev2Specs/ServiceGroupRoot/Shell/Run/Run.ps1: -------------------------------------------------------------------------------- 1 | # ensure SAS variables were passed in 2 | if ($env:SECRET_MANAGEMENT_MODULE -eq $null) 3 | { 4 | Write-Verbose -Verbose "SECRET_MANAGEMENT_MODULE variable didn't get passed correctly" 5 | return 1 6 | } 7 | 8 | if ($env:SECRET_MANAGEMENT_VERSION -eq $null) 9 | { 10 | Write-Verbose -Verbose "SECRET_MANAGEMENT_VERSION variable didn't get passed correctly" 11 | return 1 12 | } 13 | 14 | if ($env:DESTINATION_ACR_NAME -eq $null) 15 | { 16 | Write-Verbose -Verbose "DESTINATION_ACR_NAME variable didn't get passed correctly" 17 | return 1 18 | } 19 | 20 | if ($env:DESTINATION_ACR_URI -eq $null) 21 | { 22 | Write-Verbose -Verbose "DESTINATION_ACR_URI variable didn't get passed correctly" 23 | return 1 24 | } 25 | 26 | if ($env:MI_CLIENTID -eq $null) 27 | { 28 | Write-Verbose -Verbose "MI_CLIENTID variable didn't get passed correctly" 29 | return 1 30 | } 31 | 32 | 33 | try { 34 | Write-Verbose -Verbose "SecretManagement: $env:SECRET_MANAGEMENT_MODULE" 35 | Write-Verbose -Verbose "Version: $env:SECRET_MANAGEMENT_VERSION" 36 | Write-Verbose -Verbose "acrname: $env:DESTINATION_ACR_NAME" 37 | Write-Verbose -Verbose "acruri: $env:DESTINATION_ACR_URI" 38 | Write-Verbose -Verbose "MI client Id: $env:MI_CLIENTID" 39 | 40 | $secretManagementFileName = "Microsoft.PowerShell.SecretManagement.$($env:SECRET_MANAGEMENT_VERSION).nupkg" 41 | 42 | Write-Verbose -Verbose "Download files" 43 | Invoke-WebRequest -Uri $env:SECRET_MANAGEMENT_MODULE -OutFile $secretManagementFileName 44 | 45 | $moduleExists = Test-Path $secretManagementFileName 46 | Write-Verbose -Verbose "Module $secretManagementFileName exists: $moduleExists" 47 | 48 | # Install PSResourceGet 1.1.1 49 | Write-Verbose "Download PSResourceGet version 1.1.1" 50 | Register-PSRepository -Name CFS -SourceLocation "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v2" -InstallationPolicy Trusted 51 | Install-Module -Repository CFS -Name Microsoft.PowerShell.PSResourceGet -RequiredVersion '1.1.1' -Verbose 52 | Import-Module Microsoft.PowerShell.PSResourceGet 53 | Get-Module 54 | 55 | # Login to Azure CLI using Managed Identity 56 | Write-Verbose -Verbose "Login cli using managed identity" 57 | az login --identity --username $env:MI_CLIENTID 58 | 59 | # Register the target ACR as a PSResourceGet repository 60 | Write-Verbose -Verbose "Register ARC as a PSResourceGet reposirory" 61 | Register-PSResourceRepository -Uri $env:DESTINATION_ACR_URI -Name $env:DESTINATION_ACR_NAME -Trusted -Verbose 62 | 63 | Get-PSResourceRepository 64 | 65 | #Publish SecretManagement to ACR 66 | Write-Verbose -Verbose "Publish SecretManagement $secretManagementFileName to ACR $env:DESTINATION_ACR_NAME" 67 | 68 | # public 69 | $prefix = "public/psresource" 70 | Publish-PSResource -Repository $env:DESTINATION_ACR_NAME -NupkgPath $secretManagementFileName -ModulePrefix $prefix -Confirm:$false 71 | 72 | # unlisted 73 | $prefix = "unlisted/psresource" 74 | Publish-PSResource -Repository $env:DESTINATION_ACR_NAME -NupkgPath $secretManagementFileName -ModulePrefix $prefix -Confirm:$false 75 | } 76 | catch { 77 | 78 | $_.Exception | Format-List -Force 79 | 80 | return 1 81 | } 82 | 83 | return 0 84 | -------------------------------------------------------------------------------- /Ev2Specs/ServiceGroupRoot/buildver.txt: -------------------------------------------------------------------------------- 1 | 2.0.0 -------------------------------------------------------------------------------- /ExtensionModules/AKVaultScript/AKVaultScript.Extension/AKVaultScript.Extension.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ModuleVersion = '1.0' 3 | RootModule = '.\AKVaultScript.Extension.psm1' 4 | FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault') 5 | } 6 | -------------------------------------------------------------------------------- /ExtensionModules/AKVaultScript/AKVaultScript.Extension/AKVaultScript.Extension.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | function Check-SubscriptionLogIn 5 | { 6 | param ( 7 | [string] $SubscriptionId, 8 | [string] $AzKVaultName 9 | ) 10 | 11 | Import-Module -Name Az.Accounts 12 | 13 | $azContext = Az.Accounts\Get-AzContext 14 | if (($azContext -eq $null) -or ($azContext.Subscription.Id -ne $SubscriptionId)) 15 | { 16 | throw "To use ${AzKVaultName} Azure vault, the current user must be logged into Azure account subscription ${SubscriptionId}. Run 'Connect-AzAccount -SubscriptionId ${SubscriptionId}'." 17 | } 18 | } 19 | 20 | function Get-Secret 21 | { 22 | param ( 23 | [string] $Name, 24 | [string] $VaultName, 25 | [hashtable] $AdditionalParameters 26 | ) 27 | 28 | Check-SubscriptionLogIn $AdditionalParameters.SubscriptionId $AdditionalParameters.AZKVaultName 29 | 30 | Import-Module -Name Az.KeyVault 31 | 32 | $secret = Az.KeyVault\Get-AzKeyVaultSecret -Name $Name -VaultName $AdditionalParameters.AZKVaultName 33 | if ($secret -ne $null) 34 | { 35 | return $secret.SecretValue 36 | } 37 | } 38 | 39 | function Set-Secret 40 | { 41 | param ( 42 | [string] $Name, 43 | [object] $Secret, 44 | [string] $VaultName, 45 | [hashtable] $Metadata, 46 | [hashtable] $AdditionalParameters 47 | ) 48 | 49 | Check-SubscriptionLogIn $AdditionalParameters.SubscriptionId $AdditionalParameters.AZKVaultName 50 | 51 | Import-Module -Name Az.KeyVault 52 | 53 | $null = Az.KeyVault\Set-AzKeyVaultSecret -Name $Name -SecretValue $Secret -VaultName $AdditionalParameters.AZKVaultName -Tag $Metadata 54 | return $? 55 | } 56 | 57 | function Remove-Secret 58 | { 59 | param ( 60 | [string] $Name, 61 | [string] $VaultName, 62 | [hashtable] $AdditionalParameters 63 | ) 64 | 65 | Check-SubscriptionLogIn $AdditionalParameters.SubscriptionId $AdditionalParameters.AZKVaultName 66 | 67 | Import-Module -Name Az.KeyVault 68 | 69 | $null = Az.KeyVault\Remove-AzKeyVaultSecret -Name $Name -VaultName $AdditionalParameters.AZKVaultName -Force 70 | return $? 71 | } 72 | 73 | function Get-SecretInfo 74 | { 75 | param ( 76 | [string] $Filter, 77 | [string] $VaultName, 78 | [hashtable] $AdditionalParameters 79 | ) 80 | 81 | Check-SubscriptionLogIn $AdditionalParameters.SubscriptionId $AdditionalParameters.AZKVaultName 82 | 83 | Import-Module -Name Az.KeyVault 84 | 85 | if ([string]::IsNullOrEmpty($Filter)) 86 | { 87 | $Filter = "*" 88 | } 89 | 90 | $pattern = [WildcardPattern]::new($Filter) 91 | $vaultSecretInfos = Az.KeyVault\Get-AzKeyVaultSecret -VaultName $AdditionalParameters.AZKVaultName 92 | foreach ($vaultSecretInfo in $vaultSecretInfos) 93 | { 94 | if ($pattern.IsMatch($vaultSecretInfo.Name)) 95 | { 96 | Write-Output ( 97 | [Microsoft.PowerShell.SecretManagement.SecretInformation]::new( 98 | $vaultSecretInfo.Name, 99 | [Microsoft.PowerShell.SecretManagement.SecretType]::SecureString, 100 | $VaultName, 101 | $vaultSecretInfo.Tags) 102 | ) 103 | } 104 | } 105 | } 106 | 107 | function Test-SecretVault 108 | { 109 | param ( 110 | [string] $VaultName, 111 | [hashtable] $AdditionalParameters 112 | ) 113 | 114 | try 115 | { 116 | Check-SubscriptionLogIn $AdditionalParameters.SubscriptionId $AdditionalParameters.AZKVaultName 117 | } 118 | catch 119 | { 120 | Write-Error $_ 121 | return $false 122 | } 123 | 124 | return $true 125 | } 126 | -------------------------------------------------------------------------------- /ExtensionModules/AKVaultScript/AKVaultScript.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ModuleVersion = '1.0' 3 | NestedModules = ('.\AKVaultScript.Extension') 4 | CmdletsToExport = @() 5 | FunctionsToExport = @() 6 | } 7 | -------------------------------------------------------------------------------- /ExtensionModules/CredManStore/Microsoft.PowerShell.CredManStore.Extension/Microsoft.PowerShell.CredManStore.Extension.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ModuleVersion = '1.0' 3 | RootModule = './Microsoft.PowerShell.CredManStore.dll' 4 | FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault') 5 | } 6 | -------------------------------------------------------------------------------- /ExtensionModules/CredManStore/README.md: -------------------------------------------------------------------------------- 1 | # PowerShell Secret Store module 2 | 3 | This module is an extension vault module for the PowerShell SecretManagement module. 4 | It stores secrets locally using Windows Credential Manager for the current interactive user account context. 5 | This module works only for Windows platforms. 6 | -------------------------------------------------------------------------------- /ExtensionModules/CredManStore/Tests/Microsoft.PowerShell.CredManStore.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Test Microsoft.PowerShell.CredManStore module" -Skip:(-Not $IsWindows) { 5 | BeforeAll { 6 | $ProjectRoot = Split-Path $PSScriptRoot | Split-Path | Split-Path 7 | $ModuleRoot = Join-Path $ProjectRoot "artifacts/publish/Microsoft.PowerShell.CredManStore/release" 8 | Import-Module -Force -Name $ProjectRoot/module/Microsoft.PowerShell.SecretManagement.psd1 9 | Import-Module -Force -Name $ModuleRoot/Microsoft.PowerShell.CredManStore.psd1 10 | } 11 | 12 | AfterAll { 13 | Remove-Module -Name Microsoft.PowerShell.CredManStore -Force -ErrorAction Ignore 14 | Remove-Module -Name Microsoft.PowerShell.SecretManagement -Force -ErrorAction Ignore 15 | } 16 | 17 | Context "CredMan Store Vault Byte[] type" { 18 | BeforeAll { 19 | $secretName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) 20 | $bytesToWrite = [System.Text.Encoding]::UTF8.GetBytes('TestStringForBytes') 21 | $errorCode = 0 22 | } 23 | 24 | It "Verifies byte[] write to store" { 25 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::WriteObject( 26 | $secretName, 27 | $bytesToWrite, 28 | [ref] $errorCode) 29 | 30 | $success | Should -BeTrue 31 | $errorCode | Should -Be 0 32 | } 33 | 34 | It "Verifes byte[] read from store" { 35 | $outBytes = $null 36 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::ReadObject( 37 | $secretName, 38 | [ref] $outBytes, 39 | [ref] $errorCode) 40 | 41 | $success | Should -BeTrue 42 | $errorCode | Should -Be 0 43 | [System.Text.Encoding]::UTF8.GetString($outBytes) | Should -BeExactly 'TestStringForBytes' 44 | } 45 | 46 | It "Verifies byte[] enumeration from store" { 47 | $outInfo = $null 48 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::EnumerateObjectInfo( 49 | $secretName, 50 | [ref] $outInfo, 51 | [ref] $errorCode) 52 | 53 | $success | Should -BeTrue 54 | $errorCode | Should -Be 0 55 | $outInfo.Key | Should -BeExactly $secretName 56 | $outInfo.Value | Should -BeExactly 'ByteArray' 57 | } 58 | 59 | It "Verifies Remove byte[] secret from store" { 60 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::DeleteObject( 61 | $secretName, 62 | [ref] $errorCode) 63 | 64 | $success | Should -BeTrue 65 | $errorCode | Should -Be 0 66 | } 67 | } 68 | 69 | Context "CredMan Store Vault String type" { 70 | BeforeAll { 71 | $secretName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) 72 | $stringToWrite = 'TestStringForString' 73 | $errorCode = 0 74 | } 75 | 76 | It "Verifes string write to store" { 77 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::WriteObject( 78 | $secretName, 79 | $stringToWrite, 80 | [ref] $errorCode) 81 | 82 | $success | Should -BeTrue 83 | $errorCode | Should -Be 0 84 | } 85 | 86 | It "Verifies string read from store" { 87 | $outString = $null 88 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::ReadObject( 89 | $secretName, 90 | [ref] $outString, 91 | [ref] $errorCode) 92 | 93 | $success | Should -BeTrue 94 | $errorCode | Should -Be 0 95 | $outString | Should -BeExactly 'TestStringForString' 96 | } 97 | 98 | It "Verifies string enumeration from store" { 99 | $outInfo = $null 100 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::EnumerateObjectInfo( 101 | $secretName, 102 | [ref] $outInfo, 103 | [ref] $errorCode) 104 | 105 | $success | Should -BeTrue 106 | $errorCode | Should -Be 0 107 | $outInfo.Key | Should -BeExactly $secretName 108 | $outInfo.Value | Should -BeExactly 'String' 109 | } 110 | 111 | It "Verifies string remove from store" { 112 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::DeleteObject( 113 | $secretName, 114 | [ref] $errorCode) 115 | 116 | $success | Should -BeTrue 117 | $errorCode | Should -Be 0 118 | } 119 | } 120 | 121 | Context "CredMan Store Vault SecureString type" { 122 | BeforeAll { 123 | $secretName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) 124 | $randomSecret = [System.IO.Path]::GetRandomFileName() 125 | $secureStringToWrite = ConvertTo-SecureString -String $randomSecret -AsPlainText -Force 126 | $errorCode = 0 127 | } 128 | 129 | It "Verifies SecureString write to store" { 130 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::WriteObject( 131 | $secretName, 132 | $secureStringToWrite, 133 | [ref] $errorCode) 134 | 135 | $success | Should -BeTrue 136 | $errorCode | Should -Be 0 137 | } 138 | 139 | It "Verifies SecureString read from store" { 140 | $outSecureString = $null 141 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::ReadObject( 142 | $secretName, 143 | [ref] $outSecureString, 144 | [ref] $errorCode) 145 | 146 | $success | Should -BeTrue 147 | $errorCode | Should -Be 0 148 | [System.Net.NetworkCredential]::new('',$outSecureString).Password | Should -BeExactly $randomSecret 149 | } 150 | 151 | It "Verifies SecureString enumeration from store" { 152 | $outInfo = $null 153 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::EnumerateObjectInfo( 154 | $secretName, 155 | [ref] $outInfo, 156 | [ref] $errorCode) 157 | 158 | $success | Should -BeTrue 159 | $errorCode | Should -Be 0 160 | $outInfo.Key | Should -BeExactly $secretName 161 | $outInfo.Value | Should -BeExactly 'SecureString' 162 | } 163 | 164 | It "Verifies SecureString remove from store" { 165 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::DeleteObject( 166 | $secretName, 167 | [ref] $errorCode) 168 | 169 | $success | Should -BeTrue 170 | $errorCode | Should -Be 0 171 | } 172 | } 173 | 174 | Context "CredMan Store Vault PSCredential type" { 175 | BeforeAll { 176 | $secretName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) 177 | $randomSecret = [System.IO.Path]::GetRandomFileName() 178 | $errorCode = 0 179 | } 180 | 181 | It "Verifies PSCredential type write to store" { 182 | $cred = [pscredential]::new('UserL', (ConvertTo-SecureString $randomSecret -AsPlainText -Force)) 183 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::WriteObject( 184 | $secretName, 185 | $cred, 186 | [ref] $errorCode) 187 | 188 | $success | Should -BeTrue 189 | $errorCode | Should -Be 0 190 | } 191 | 192 | It "Verifies PSCredential read from store" { 193 | $outCred = $null 194 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::ReadObject( 195 | $secretName, 196 | [ref] $outCred, 197 | [ref] $errorCode) 198 | 199 | $success | Should -BeTrue 200 | $errorCode | Should -Be 0 201 | $outCred.UserName | Should -BeExactly "UserL" 202 | [System.Net.NetworkCredential]::new('', ($outCred.Password)).Password | Should -BeExactly $randomSecret 203 | } 204 | 205 | It "Verifies PSCredential enumeration from store" { 206 | $outInfo = $null 207 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::EnumerateObjectInfo( 208 | $secretName, 209 | [ref] $outInfo, 210 | [ref] $errorCode) 211 | 212 | $success | Should -BeTrue 213 | $errorCode | Should -Be 0 214 | $outInfo.Key | Should -BeExactly $secretName 215 | $outInfo.Value | Should -BeExactly 'PSCredential' 216 | } 217 | 218 | It "Verifies PSCredential remove from store" { 219 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::DeleteObject( 220 | $secretName, 221 | [ref] $errorCode) 222 | 223 | $success | Should -BeTrue 224 | $errorCode | Should -Be 0 225 | } 226 | } 227 | 228 | Context "CredMan Store Vault Hashtable type" { 229 | BeforeAll { 230 | $secretName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetRandomFileName()) 231 | $randomSecretA = [System.IO.Path]::GetRandomFileName() 232 | $randomSecretB = [System.IO.Path]::GetRandomFileName() 233 | $errorCode = 0 234 | } 235 | 236 | It "Verifies Hashtable type write to store" { 237 | $ht = @{ 238 | Blob = ([byte[]] @(1,2)) 239 | Str = "TestStoreString" 240 | SecureString = (ConvertTo-SecureString $randomSecretA -AsPlainText -Force) 241 | Cred = ([pscredential]::New("UserA", (ConvertTo-SecureString $randomSecretB -AsPlainText -Force))) 242 | } 243 | 244 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::WriteObject( 245 | $secretName, 246 | $ht, 247 | [ref] $errorCode) 248 | 249 | $success | Should -BeTrue 250 | $errorCode | Should -Be 0 251 | } 252 | 253 | It "Verifies Hashtable read from store" { 254 | $outHT = $null 255 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::ReadObject( 256 | $secretName, 257 | [ref] $outHT, 258 | [ref] $errorCode) 259 | 260 | $success | Should -BeTrue 261 | $errorCode | Should -Be 0 262 | $outHT.Blob.Count | Should -Be 2 263 | $outHT.Str | Should -BeExactly 'TestStoreString' 264 | [System.Net.NetworkCredential]::new('', ($outHT.SecureString)).Password | Should -BeExactly $randomSecretA 265 | $outHT.Cred.UserName | Should -BeExactly 'UserA' 266 | [System.Net.NetworkCredential]::New('', ($outHT.Cred.Password)).Password | Should -BeExactly $randomSecretB 267 | } 268 | 269 | It "Verifies Hashtable enumeration from store" { 270 | $outInfo = $null 271 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::EnumerateObjectInfo( 272 | $secretName, 273 | [ref] $outInfo, 274 | [ref] $errorCode) 275 | 276 | $success | Should -BeTrue 277 | $errorCode | Should -Be 0 278 | $outInfo.Key | Should -BeExactly $secretName 279 | $outInfo.Value | Should -BeExactly 'Hashtable' 280 | } 281 | 282 | It "Verifies Hashtable remove from store" { 283 | $success = [Microsoft.PowerShell.CredManStore.LocalCredManStore]::DeleteObject( 284 | $secretName, 285 | [ref] $errorCode) 286 | 287 | $success | Should -BeTrue 288 | $errorCode | Should -Be 0 289 | } 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /ExtensionModules/CredManStore/help/en-US/about_Microsoft.PowerShell.CredManStore.md: -------------------------------------------------------------------------------- 1 | # Secret Management local store extension vault module 2 | ## Microsoft.PowerShell.CredManStore 3 | 4 | # SHORT DESCRIPTION 5 | This is an extension vault module for the Microsoft.PowerShell.SecretManagement module. 6 | It stores secrets on the local machine in the current user account context using Windows Credential Manager. 7 | This extension vault module can be used only on Windows platforms. 8 | 9 | ``` 10 | ABOUT TOPIC NOTE: 11 | About topics can be no longer than 80 characters wide when rendered to text. 12 | Any topics greater than 80 characters will be automatically wrapped. 13 | The generated about topic will be encoded UTF-8. 14 | ``` 15 | 16 | # LONG DESCRIPTION 17 | {{ Long Description Placeholder }} 18 | 19 | ## Optional Subtopics 20 | {{ Optional Subtopic Placeholder }} 21 | 22 | # EXAMPLES 23 | {{ Code or descriptive examples of how to leverage the functions described. }} 24 | 25 | # NOTE 26 | {{ Note Placeholder - Additional information that a user needs to know.}} 27 | 28 | # TROUBLESHOOTING NOTE 29 | {{ Troubleshooting Placeholder - Warns users of bugs}} 30 | 31 | {{ Explains behavior that is likely to change with fixes }} 32 | 33 | # SEE ALSO 34 | {{ See also placeholder }} 35 | 36 | {{ You can also list related articles, blogs, and video URLs. }} 37 | 38 | # KEYWORDS 39 | {{List alternate names or titles for this topic that readers might use.}} 40 | 41 | - {{ Keyword Placeholder }} 42 | - {{ Keyword Placeholder }} 43 | - {{ Keyword Placeholder }} 44 | - {{ Keyword Placeholder }} 45 | -------------------------------------------------------------------------------- /ExtensionModules/CredManStore/src/Microsoft.PowerShell.CredManStore.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | 6 | NestedModules = @('.\Microsoft.PowerShell.CredManStore.Extension') 7 | 8 | # Version number of this module. 9 | ModuleVersion = '1.0.0' 10 | 11 | # Supported PSEditions 12 | CompatiblePSEditions = @('Desktop', 'Core') 13 | 14 | # ID used to uniquely identify this module 15 | GUID = '4b4bc3ec-190a-493f-a869-5ebdb239895d' 16 | 17 | # Author of this module 18 | Author = 'Microsoft Corporation' 19 | 20 | # Company or vendor of this module 21 | CompanyName = 'Microsoft Corporation' 22 | 23 | # Copyright statement for this module 24 | Copyright = '(c) Microsoft Corporation. All rights reserved.' 25 | 26 | # Description of the functionality provided by this module 27 | Description = " 28 | This PowerShell module is an extension vault module for the PowerShell SecretManagement module. 29 | As an extension vault, this module uses Windows Credential Manager to store secrets to the local machine based on the current interactive user account context. 30 | This extension vault module will run only on Windows platforms. 31 | " 32 | 33 | # Minimum version of the PowerShell engine required by this module 34 | PowerShellVersion = '5.1' 35 | 36 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 37 | CmdletsToExport = @() 38 | 39 | # This is critical to ensure no nested module functions are exposed publicly. 40 | FunctionsToExport = @() 41 | 42 | # HelpInfo URI of this module 43 | # HelpInfoURI = '' 44 | 45 | } 46 | -------------------------------------------------------------------------------- /ExtensionModules/CredManStore/src/code/CredManStore.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using Microsoft.PowerShell.SecretManagement; 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using System.Management.Automation; 10 | 11 | namespace Microsoft.PowerShell.CredManStore 12 | { 13 | #region Get-Secret 14 | 15 | [Cmdlet(VerbsCommon.Get, "Secret")] 16 | public sealed class GetSecretCommand : PSCmdlet 17 | { 18 | #region Parameters 19 | 20 | [Parameter] 21 | public string Name { get; set; } 22 | 23 | [Parameter] 24 | public string VaultName { get; set; } 25 | 26 | [Parameter] 27 | public Hashtable AdditionalParameters { get; set; } 28 | 29 | #endregion 30 | 31 | #region Overrides 32 | 33 | protected override void EndProcessing() 34 | { 35 | if (LocalCredManStore.ReadObject( 36 | name: Name, 37 | outObject: out object outObject, 38 | out int errorCode)) 39 | { 40 | WriteObject( 41 | sendToPipeline: outObject, 42 | enumerateCollection: false); 43 | 44 | return; 45 | } 46 | 47 | if (errorCode > 0 && errorCode != NativeUtils.ERROR_NOT_FOUND) 48 | { 49 | var message = string.Format(CultureInfo.InvariantCulture, 50 | @"Error while retrieving secret from vault {0} : {1}", 51 | VaultName, 52 | LocalCredManStore.GetErrorMessage(errorCode)); 53 | 54 | WriteError( 55 | new ErrorRecord( 56 | new PSInvalidOperationException(message), 57 | "CredManVaultGetError", 58 | ErrorCategory.InvalidOperation, 59 | this)); 60 | } 61 | } 62 | 63 | #endregion 64 | } 65 | 66 | #endregion 67 | 68 | #region Get-SecretInfo 69 | 70 | [Cmdlet(VerbsCommon.Get, "SecretInfo")] 71 | public sealed class GetSecretInfoCommand : PSCmdlet 72 | { 73 | #region Parameters 74 | 75 | [Parameter] 76 | public string Filter { get; set; } 77 | 78 | [Parameter] 79 | public string VaultName { get; set; } 80 | 81 | [Parameter] 82 | public Hashtable AdditionalParameters { get; set; } 83 | 84 | #endregion 85 | 86 | #region Overrides 87 | 88 | protected override void EndProcessing() 89 | { 90 | if (LocalCredManStore.EnumerateObjectInfo( 91 | filter: Filter, 92 | out KeyValuePair[] outObjectInfos, 93 | out int errorCode)) 94 | { 95 | var secretInfoList = new List(outObjectInfos.Length); 96 | foreach (var item in outObjectInfos) 97 | { 98 | secretInfoList.Add( 99 | new SecretInformation( 100 | name: item.Key, 101 | type: item.Value, 102 | vaultName: VaultName)); 103 | } 104 | 105 | WriteObject( 106 | sendToPipeline: secretInfoList.ToArray(), 107 | enumerateCollection: false); 108 | 109 | return; 110 | } 111 | 112 | if (errorCode > 0 && errorCode != NativeUtils.ERROR_NOT_FOUND) 113 | { 114 | var message = string.Format(CultureInfo.InvariantCulture, 115 | @"Error while retrieving secret information from vault {0} : {1}", 116 | VaultName, 117 | LocalCredManStore.GetErrorMessage(errorCode)); 118 | 119 | WriteError( 120 | new ErrorRecord( 121 | new PSInvalidOperationException(message), 122 | "CredManVaultGetInfoError", 123 | ErrorCategory.InvalidOperation, 124 | this)); 125 | } 126 | } 127 | 128 | #endregion 129 | } 130 | 131 | #endregion 132 | 133 | #region Set-Secret 134 | 135 | [Cmdlet(VerbsCommon.Set, "Secret")] 136 | public sealed class SetSecretCommand : PSCmdlet 137 | { 138 | #region Parameters 139 | 140 | [Parameter] 141 | public string Name { get; set; } 142 | 143 | [Parameter] 144 | public object Secret { get; set; } 145 | 146 | [Parameter] 147 | public string VaultName { get; set; } 148 | 149 | [Parameter] 150 | public Hashtable AdditionalParameters { get; set; } 151 | 152 | #endregion 153 | 154 | #region Overrides 155 | 156 | protected override void EndProcessing() 157 | { 158 | if (!LocalCredManStore.WriteObject( 159 | name: Name, 160 | objectToWrite: Secret, 161 | out int errorCode)) 162 | { 163 | var message = string.Format(CultureInfo.InvariantCulture, 164 | @"Error while writing secret to vault {0} : {1}", 165 | VaultName, 166 | LocalCredManStore.GetErrorMessage(errorCode)); 167 | 168 | WriteError( 169 | new ErrorRecord( 170 | new PSInvalidOperationException(message), 171 | "CredManVaultWriteError", 172 | ErrorCategory.InvalidOperation, 173 | this)); 174 | } 175 | } 176 | 177 | #endregion 178 | } 179 | 180 | #endregion 181 | 182 | #region Remove-Secret 183 | 184 | [Cmdlet(VerbsCommon.Remove, "Secret")] 185 | public sealed class RemoveSecretCommand : PSCmdlet 186 | { 187 | #region Parameters 188 | 189 | [Parameter] 190 | public string Name { get; set; } 191 | 192 | [Parameter] 193 | public string VaultName { get; set; } 194 | 195 | [Parameter] 196 | public Hashtable AdditionalParameters { get; set; } 197 | 198 | #endregion 199 | 200 | #region Overrides 201 | 202 | protected override void EndProcessing() 203 | { 204 | if (!LocalCredManStore.DeleteObject( 205 | name: Name, 206 | out int errorCode)) 207 | { 208 | var message = string.Format(CultureInfo.InvariantCulture, 209 | @"Error while deleting secret from vault {0} : {1}", 210 | VaultName, 211 | LocalCredManStore.GetErrorMessage(errorCode)); 212 | 213 | WriteError( 214 | new ErrorRecord( 215 | new PSInvalidOperationException(message), 216 | "CredManVaultWriteError", 217 | ErrorCategory.InvalidOperation, 218 | this)); 219 | } 220 | } 221 | 222 | #endregion 223 | } 224 | 225 | #endregion 226 | 227 | #region Test-SecretVault 228 | 229 | [Cmdlet(VerbsDiagnostic.Test, "SecretVault")] 230 | public sealed class TestSecretVaultCommand : PSCmdlet 231 | { 232 | #region Parameters 233 | 234 | [Parameter] 235 | public string VaultName { get; set; } 236 | 237 | [Parameter] 238 | public Hashtable AdditionalParameters { get; set; } 239 | 240 | #endregion 241 | 242 | #region Overrides 243 | 244 | protected override void EndProcessing() 245 | { 246 | var secretName = System.IO.Path.GetRandomFileName(); 247 | var secret = System.IO.Path.GetRandomFileName(); 248 | 249 | // Setting a secret 250 | var success = LocalCredManStore.WriteObject( 251 | name: secretName, 252 | objectToWrite: secret, 253 | out int errorCode); 254 | if (!success) 255 | { 256 | var message = string.Format(CultureInfo.InvariantCulture, 257 | @"Test-SecretVault failed to write secret on vault {0} with error: {1}", 258 | VaultName, LocalCredManStore.GetErrorMessage(errorCode)); 259 | WriteError( 260 | new ErrorRecord( 261 | new PSInvalidOperationException(message), 262 | errorId: "CredManVaultTestFailWrite", 263 | errorCategory: ErrorCategory.InvalidOperation, 264 | this)); 265 | 266 | WriteObject(success); 267 | return; 268 | } 269 | 270 | // Getting secret info 271 | success = LocalCredManStore.EnumerateObjectInfo( 272 | filter: secretName, 273 | out KeyValuePair[] outObjectInfos, 274 | out errorCode); 275 | if (!success) 276 | { 277 | var message = string.Format(CultureInfo.InvariantCulture, 278 | @"Test-SecretVault failed to get secret info on vault {0} with error: {1}", 279 | VaultName, LocalCredManStore.GetErrorMessage(errorCode)); 280 | WriteError( 281 | new ErrorRecord( 282 | new PSInvalidOperationException(message), 283 | errorId: "CredManVaultTestFailReadInfo", 284 | errorCategory: ErrorCategory.InvalidOperation, 285 | this)); 286 | } 287 | 288 | // Getting secret value 289 | success = LocalCredManStore.ReadObject( 290 | name: secretName, 291 | outObject: out object outObject, 292 | out errorCode); 293 | if (!success) 294 | { 295 | var message = string.Format(CultureInfo.InvariantCulture, 296 | @"Test-SecretVault failed to get secret value on vault {0} with error: {1}", 297 | VaultName, LocalCredManStore.GetErrorMessage(errorCode)); 298 | WriteError( 299 | new ErrorRecord( 300 | new PSInvalidOperationException(message), 301 | errorId: "CredManVaultTestFailRead", 302 | errorCategory: ErrorCategory.InvalidOperation, 303 | this)); 304 | } 305 | 306 | // Removing secret 307 | success = LocalCredManStore.DeleteObject( 308 | name: secretName, 309 | out errorCode); 310 | if (!success) 311 | { 312 | var message = string.Format(CultureInfo.InvariantCulture, 313 | @"Test-SecretVault failed to remove secret on vault {0} with error: {1}", 314 | VaultName, LocalCredManStore.GetErrorMessage(errorCode)); 315 | WriteError( 316 | new ErrorRecord( 317 | new PSInvalidOperationException(message), 318 | errorId: "CredManVaultTestFailDelete", 319 | errorCategory: ErrorCategory.InvalidOperation, 320 | this)); 321 | } 322 | 323 | WriteObject(success); 324 | } 325 | 326 | #endregion 327 | 328 | } 329 | 330 | #endregion 331 | } 332 | -------------------------------------------------------------------------------- /ExtensionModules/CredManStore/src/code/Microsoft.PowerShell.CredManStore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Library 6 | Microsoft.PowerShell.CredManStore 7 | Microsoft.PowerShell.CredManStore 8 | 1.0.0.0 9 | 1.0.0 10 | 1.0.0 11 | netstandard2.0 12 | 13 | 14 | 15 | $(DefineConstants);UNIX 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ExtensionModules/TestLocalScript/TestLocalScript.Extension/TestLocalScript.Extension.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ModuleVersion = '1.0' 3 | RootModule = '.\TestLocalScript.Extension.psm1' 4 | FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault','Unregister-SecretVault') 5 | } 6 | -------------------------------------------------------------------------------- /ExtensionModules/TestLocalScript/TestLocalScript.Extension/TestLocalScript.Extension.psm1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | function Get-Path 5 | { 6 | param ( 7 | [string] $VaultName 8 | ) 9 | 10 | $path = Join-Path $env:TEMP $VaultName 11 | if (! (Test-Path -Path $path)) 12 | { 13 | [System.IO.Directory]::CreateDirectory($path) 14 | } 15 | 16 | return $path 17 | } 18 | 19 | function Get-Secret 20 | { 21 | param ( 22 | [string] $Name, 23 | [string] $VaultName, 24 | [hashtable] $AdditionalParameters 25 | ) 26 | 27 | if ([WildcardPattern]::ContainsWildcardCharacters($Name)) 28 | { 29 | throw "The Name parameter cannot contain wild card characters." 30 | } 31 | 32 | $filePath = Join-Path -Path (Get-Path $VaultName) -ChildPath "${Name}.xml" 33 | if (! (Test-Path -Path $filePath)) 34 | { 35 | return 36 | } 37 | 38 | $secret = Import-Clixml -Path $filePath 39 | 40 | if ($secret.GetType().IsArray) 41 | { 42 | return @(,[byte[]] $secret) 43 | } 44 | 45 | $verboseEnabled = $AdditionalParameters.ContainsKey('Verbose') -and ($AdditionalParameters['Verbose'] -eq $true) 46 | Write-Verbose "[TestLocalScript.Extension]:Get-SecretVault successfully called for vault: $VaultName" -Verbose:$verboseEnabled 47 | 48 | return $secret 49 | } 50 | 51 | function Set-Secret 52 | { 53 | param ( 54 | [string] $Name, 55 | [object] $Secret, 56 | [string] $VaultName, 57 | [hashtable] $AdditionalParameters 58 | ) 59 | 60 | $filePath = Join-Path -Path (Get-Path $VaultName) -ChildPath "${Name}.xml" 61 | $Secret | Export-Clixml -Path $filePath -Force 62 | 63 | $verboseEnabled = $AdditionalParameters.ContainsKey('Verbose') -and ($AdditionalParameters['Verbose'] -eq $true) 64 | Write-Verbose "[TestLocalScript.Extension]:Set-SecretVault successfully called for vault: $VaultName" -Verbose:$verboseEnabled 65 | 66 | return $true 67 | } 68 | 69 | function Remove-Secret 70 | { 71 | param ( 72 | [string] $Name, 73 | [string] $VaultName, 74 | [hashtable] $AdditionalParameters 75 | ) 76 | 77 | $filePath = Join-Path -Path (Get-Path $VaultName) -ChildPath "${Name}.xml" 78 | if (! (Test-Path -Path $filePath)) 79 | { 80 | Write-Error "The secret, $Name, does not exist." 81 | return $false 82 | } 83 | 84 | Remove-Item -Path $filePath 85 | 86 | $verboseEnabled = $AdditionalParameters.ContainsKey('Verbose') -and ($AdditionalParameters['Verbose'] -eq $true) 87 | Write-Verbose "[TestLocalScript.Extension]:Remove-SecretVault successfully called for vault: $VaultName" -Verbose:$verboseEnabled 88 | 89 | return $true 90 | } 91 | 92 | function Get-SecretInfo 93 | { 94 | param( 95 | [string] $Filter, 96 | [string] $VaultName, 97 | [hashtable] $AdditionalParameters 98 | ) 99 | 100 | if ([string]::IsNullOrEmpty($Filter)) { $Filter = "*" } 101 | 102 | $files = Get-ChildItem -Path (Join-Path -Path (Get-Path $VaultName) -ChildPath "${Filter}.xml") 2>$null 103 | 104 | foreach ($file in $files) 105 | { 106 | $secretName = [System.IO.Path]::GetFileNameWithoutExtension((Split-Path -Path $file -Leaf)) 107 | $secret = Import-Clixml -Path $file.FullName 108 | $type = if ($secret.gettype().IsArray) { [Microsoft.PowerShell.SecretManagement.SecretType]::ByteArray } 109 | elseif ($secret -is [string]) { [Microsoft.PowerShell.SecretManagement.SecretType]::String } 110 | elseif ($secret -is [securestring]) { [Microsoft.PowerShell.SecretManagement.SecretType]::SecureString } 111 | elseif ($secret -is [PSCredential]) { [Microsoft.PowerShell.SecretManagement.SecretType]::PSCredential } 112 | elseif ($secret -is [hashtable]) { [Microsoft.PowerShell.SecretManagement.SecretType]::Hashtable } 113 | else { [Microsoft.PowerShell.SecretManagement.SecretType]::Unknown } 114 | 115 | Write-Output ( 116 | [Microsoft.PowerShell.SecretManagement.SecretInformation]::new( 117 | $secretName, 118 | $type, 119 | $VaultName) 120 | ) 121 | } 122 | 123 | $verboseEnabled = $AdditionalParameters.ContainsKey('Verbose') -and ($AdditionalParameters['Verbose'] -eq $true) 124 | Write-Verbose "[TestLocalScript.Extension]:Get-SecretInfo successfully called for vault: $VaultName" -Verbose:$verboseEnabled 125 | Write-Warning "[TestLocalScript.Extension]::Get-SecretInfo bogus warning for vault: $VaultName" 126 | } 127 | 128 | function Test-SecretVault 129 | { 130 | param ( 131 | [string] $VaultName, 132 | [hashtable] $AdditionalParameters 133 | ) 134 | 135 | $verboseEnabled = $AdditionalParameters.ContainsKey('Verbose') -and ($AdditionalParameters['Verbose'] -eq $true) 136 | Write-Verbose "[TestLocalScript.Extension]:Test-SecretVault successfully called for vault: $VaultName" -Verbose:$verboseEnabled 137 | 138 | return $true 139 | } 140 | 141 | function Unregister-SecretVault 142 | { 143 | param ( 144 | [string] $VaultName, 145 | [hashtable] $AdditionalParameters 146 | ) 147 | 148 | $verboseEnabled = $AdditionalParameters.ContainsKey('Verbose') -and ($AdditionalParameters['Verbose'] -eq $true) 149 | Write-Verbose "[TestLocalScript.Extension]:Unregister-SecretVault successfully called for vault: $VaultName" -Verbose:$verboseEnabled 150 | } 151 | -------------------------------------------------------------------------------- /ExtensionModules/TestLocalScript/TestLocalScript.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ModuleVersion = '1.0' 3 | NestedModules = @('.\TestLocalScript.Extension') 4 | CmdletsToExport = @() 5 | FunctionsToExport = @() 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Microsoft Corporation. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell SecretManagement module 2 | 3 | PowerShell SecretManagement module provides a convenient way for a user to store and retrieve secrets. 4 | The secrets are stored in SecretManagement extension vaults. 5 | An extension vault is a PowerShell module that has been registered to SecretManagement, and exports five module functions required by SecretManagement. 6 | An extension vault can store secrets locally or remotely. 7 | 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). 8 | 9 | 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. 10 | Error and informational messages common to all extension vaults are provided by SecretManagement. 11 | 12 | PowerShell SecretManagement module is essentially an orchestrator for extension vaults which perform the actual secret storage and encryption. 13 | The extension vault may implement its own store, or wrap an existing secure store solution, such as Azure KeyVault, KeePass, Keyring, etc. 14 | 15 | PowerShell SecretManagement supports the following secret data types: 16 | 17 | - byte[] 18 | - string 19 | - SecureString 20 | - PSCredential 21 | - Hashtable 22 | 23 | All extension vault implementations must also support these data types. 24 | 25 | PowerShell SecretManagement module provides cmdlets for for accessing and manipulating secrets, and also cmdlets for registering and manipulating vault extensions. 26 | 27 | ## Installation 28 | 29 | You can install the `Microsoft.PowerShell.SecretManagement` module from the [PowerShell Gallery](https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretManagement). 30 | 31 | ```powershell 32 | Install-Module -Name Microsoft.PowerShell.SecretManagement -Repository PSGallery 33 | ``` 34 | 35 | ## Secret metadata 36 | 37 | Extension vaults can optionally support storing and retrieving additional secret metadata, that is data associated with the secret. 38 | Secret metadata is not security sensitive and does not need to be stored securely in the extension vault. 39 | 40 | Secret metadata can be included by using the `-Metadata` parameter: `Set-Secret -Metadata @{ Name=Value }`. 41 | The `-Metadata` parameter takes a `Hashtable` type argument consisting of name/value pairs. 42 | Extension vaults should at minimum support the following value types: 43 | 44 | - string 45 | 46 | - int 47 | 48 | - DateTime 49 | 50 | The secret metadata is included in the `SecretInformation` type object returned by `Get-SecretInfo`, in a `Metadata` property. 51 | The `Metadata` property is a `ReadonlyDictionary` type. 52 | 53 | ## Vault extension registration cmdlets 54 | 55 | ### Register-SecretVault 56 | 57 | Registers a single extension vault to the current user 58 | 59 | ### Get-SecretVault 60 | 61 | Retrieves information about one or more registered extension vaults 62 | 63 | ### Unregister-SecretVault 64 | 65 | Unregisters a single extension vault 66 | 67 | ### Set-SecretVaultDefault 68 | 69 | Sets one registered extension vault as the default vault 70 | 71 | ### Test-SecretVault 72 | 73 | Runs the Test-SecretVault function provided by the extension vault 74 | 75 | ### Unlock-SecretVault 76 | 77 | Unlocks the extension vault through a passed in SecureString password 78 | 79 | ## Accessing secrets cmdlets 80 | 81 | ### Set-Secret 82 | 83 | Adds a secret to a specific extension vault, or to the default vault if no vault is specified 84 | 85 | ### Set-SecretInfo 86 | 87 | Adds or replaces additional secret metadata to an existing secret in the vault. 88 | Metadata is not stored securely. 89 | 90 | ### Get-Secret 91 | 92 | Retrieves a secret from a specific extension vault, or first found over all vaults 93 | 94 | ### Get-SecretInfo 95 | 96 | Retrieves information about one or more secrets, but not the secret itself 97 | 98 | ### Remove-Secret 99 | 100 | Removes a secret from a specific vault 101 | 102 | ## Vault extension registration 103 | 104 | Vault extensions are registered to the current user context. 105 | They are registered as PowerShell modules that expose required functions used by SecretManagement to manipulate secrets. 106 | The required functions are provided as PowerShell cmdlets, and can be implemented as script or binary cmdlets. 107 | 108 | 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. 109 | 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. 110 | This nested module must have a name that is the parent module name with '.Extension' appended to it. 111 | 112 | It is recommended that the parent module manifest file (.psd1) include the 'SecretManagement' tag in its PrivateData section. 113 | This allows [PowerShellGallery](https://www.powershellgallery.com) to associate it with the SecretManagement module. 114 | 115 | ### Example: Script module vault extension 116 | 117 | This is a minimal vault extension example to demonstrate the directory structure and functional requirements of an extension vault module. 118 | The extension vault module name is 'TestVault'. 119 | 120 | #### Module directory structure 121 | 122 | ./TestVault 123 | ./TestVault/TestVault.psd1 124 | ./TestVault/TestStoreImplementation.dll 125 | ./TestVault/TestVault.Extension 126 | ./TestVault/TestVault.Extension/TestVault.Extension.psd1 127 | ./TestVault/TestVault.Extension/TestVault.Extension.psm1 128 | 129 | #### TestVault.psd1 file 130 | 131 | ```powershell 132 | @{ 133 | ModuleVersion = '1.0' 134 | RootModule = '.\TestStoreImplementation.dll' 135 | NestedModules = @('.\TestVault.Extension') 136 | CmdletsToExport = @('Set-TestStoreConfiguration','Get-TestStoreConfiguration') 137 | PrivateData = @{ 138 | PSData = @{ 139 | Tags = @('SecretManagement') 140 | } 141 | } 142 | } 143 | ``` 144 | 145 | 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. 146 | It also declares the required nested module (TestVault.Extension) that exports the five functions required by SecretManagement registration. 147 | 148 | Note that the nested module conforms to the required naming format: 149 | '[ModuleName].Extension' 150 | 151 | 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. 152 | The 'RootModule' and 'CmdletsToExport' entries are only for configuring the TestStore in this specific case, and are not needed in general. 153 | 154 | #### TestVault.Extension.psd1 file 155 | 156 | ```powershell 157 | @{ 158 | ModuleVersion = '1.0' 159 | RootModule = '.\TestVault.Extension.psm1' 160 | RequiredAssemblies = '..\TestStoreImplementation.dll' 161 | FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault') 162 | } 163 | ``` 164 | 165 | This nested module implements and exports the five functions required by SecretManagement. 166 | It also specifies the TestStoreImplementation.dll binary as a 'RequiredAssemblies' because the five exported functions depend on it. 167 | 168 | #### TestVault.Extension.psm1 file 169 | 170 | ```powershell 171 | function Get-Secret 172 | { 173 | [CmdletBinding()] 174 | param ( 175 | [string] $Name, 176 | [string] $VaultName, 177 | [hashtable] $AdditionalParameters 178 | ) 179 | 180 | return [TestStore]::GetItem($Name, $AdditionalParameters) 181 | } 182 | 183 | function Get-SecretInfo 184 | { 185 | [CmdletBinding()] 186 | param ( 187 | [string] $Filter, 188 | [string] $VaultName, 189 | [hashtable] $AdditionalParameters 190 | ) 191 | 192 | return @(,[Microsoft.PowerShell.SecretManagement.SecretInformation]::new( 193 | "Name", # Name of secret 194 | "String", # Secret data type [Microsoft.PowerShell.SecretManagement.SecretType] 195 | $VaultName, # Name of vault 196 | $Metadata)) # Optional Metadata parameter 197 | } 198 | 199 | function Set-Secret 200 | { 201 | [CmdletBinding()] 202 | param ( 203 | [string] $Name, 204 | [object] $Secret, 205 | [string] $VaultName, 206 | [hashtable] $AdditionalParameters 207 | ) 208 | 209 | [TestStore]::SetItem($Name, $Secret) 210 | } 211 | 212 | # Optional function 213 | function Set-SecretInfo 214 | { 215 | [CmdletBinding()] 216 | param ( 217 | [string] $Name, 218 | [hashtable] $Metadata, 219 | [string] $VaultName, 220 | [hashtable] $AdditionalParameters 221 | ) 222 | 223 | [TestStore]::SetItemMetadata($Name, $Metadata) 224 | } 225 | 226 | function Remove-Secret 227 | { 228 | [CmdletBinding()] 229 | param ( 230 | [string] $Name, 231 | [string] $VaultName, 232 | [hashtable] $AdditionalParameters 233 | ) 234 | 235 | [TestStore]::RemoveItem($Name) 236 | } 237 | 238 | function Test-SecretVault 239 | { 240 | [CmdletBinding()] 241 | param ( 242 | [string] $VaultName, 243 | [hashtable] $AdditionalParameters 244 | ) 245 | 246 | return [TestStore]::TestVault() 247 | } 248 | 249 | # Optional function 250 | function Unregister-SecretVault 251 | { 252 | [CmdletBinding()] 253 | param ( 254 | [string] $VaultName, 255 | [hashtable] $AdditionalParameters 256 | ) 257 | 258 | [TestStore]::RunUnregisterCleanup() 259 | } 260 | 261 | # Optional function 262 | function Unlock-SecretVault 263 | { 264 | [CmdletBinding()] 265 | param ( 266 | [SecureString] $Password, 267 | [string] $VaultName, 268 | [hashtable] $AdditionalParameters 269 | ) 270 | 271 | [TestStore]::UnlockVault($Password) 272 | } 273 | ``` 274 | 275 | This module script implements the five functions, as cmdlets, required by SecretManagement, plus some optional functions. 276 | It also implements an optional `Unregister-SecretVault` function that is called during vault extension un-registration. 277 | It also implements an optional `Set-SecretInfo` function that sets secret metadata to a specific secret in the vault. 278 | It also implements an optional `Unlock-SecretVault` function that unlocks the vault for the current session based on a provided password. 279 | 280 | 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. 281 | 282 | The `Get-Secret` cmdlet writes the retrieved secret value to the output pipeline on return, or null if no secret was found. 283 | It should write an error only if an abnormal condition occurs. 284 | 285 | 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. 286 | 287 | The `Test-SecretVault` cmdlet should write all errors that occur during the test. 288 | But only a single true/false boolean should be written the the output pipeline indicating success. 289 | 290 | The `Unregister-SecretVault` cmdlet is optional and will be called on the extension vault if available. 291 | It is called before the extension vault is unregistered to allow it to perform any needed clean up work. 292 | 293 | The `Unlock-SecretVault` cmdlet is optional and will be called on the extension vault if available. 294 | 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. 295 | 296 | In general, these cmdlets should write to the error stream only for abnormal conditions that prevent successful completion. 297 | And write to the output stream only the data as indicated above, and expected by SecretManagement. 298 | 299 | In addition, these cmdlets should perform proper authentication and provide errors, and instructions to authenticate, as appropriate. 300 | Or prompt the user if needed, for example if a passphrase is required. 301 | 302 | A vault extension doesn't need to provide full implementation of all required functions. 303 | 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. 304 | If a vault extension doesn't support some functionality, then it should write an appropriate error with a meaningful message. 305 | 306 | 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). 307 | 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. 308 | 309 | ### Registering the vault 310 | 311 | Once the TestVault module is created, it is registered as follows: 312 | 313 | ```powershell 314 | Register-SecretVault -Name LocalStore -ModuleName ./TestVault -VaultParameters @{ None="ReallyNeeded" } -DefaultVault 315 | 316 | Get-SecretVault 317 | 318 | VaultName ModuleName IsDefaultVault 319 | --------- ---------- -------------- 320 | LocalStore TestVault True 321 | 322 | ``` 323 | 324 | ## Extension vault registry file location 325 | 326 | SecretManagement is designed to be installed and run within a user account on both Windows and non-Windows platforms. 327 | The extension vault registry file is located in a user account protected directory. 328 | 329 | For Windows platforms the location is: `%LOCALAPPDATA%\Microsoft\PowerShell\secretmanagement` 330 | 331 | For non-Windows platforms the location: `$HOME/.secretmanagement` 332 | 333 | ## Windows Managed Accounts 334 | 335 | SecretManagement does not currently work for Windows managed accounts. 336 | 337 | SecretManagement depends on both `%LOCALAPPDATA%` folders to store registry information, and Data Protection APIs for safely handling secrets with the .Net `SecureString` type. 338 | However, Windows managed accounts do not have profiles or `%LOCALAPPDATA%` folders, and Windows Data Protection APIs do not work for managed accounts. 339 | Consequently, SecretManagement will not run under managed accounts. 340 | 341 | ## Code of Conduct 342 | 343 | Please see our [Code of Conduct](.github/CODE_OF_CONDUCT.md) before participating in this project. 344 | 345 | ## Security Policy 346 | 347 | For any security issues, please see our [Security Policy](.github/SECURITY.md). 348 | -------------------------------------------------------------------------------- /SecretManagement.build.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | [CmdletBinding()] 4 | param( 5 | [ValidateSet("Debug", "Release")] 6 | [string]$Configuration = "Debug" 7 | ) 8 | 9 | #Requires -Modules @{ ModuleName = "InvokeBuild"; ModuleVersion = "5.0.0" } 10 | 11 | task FindDotNet -Before Clean, Build { 12 | Assert (Get-Command dotnet -ErrorAction SilentlyContinue) "The dotnet CLI was not found, please install it: https://aka.ms/dotnet-cli" 13 | $DotnetVersion = dotnet --version 14 | Assert ($?) "The required .NET SDK was not found, please install it: https://aka.ms/dotnet-cli" 15 | Write-Host "Using dotnet $DotnetVersion at path $((Get-Command dotnet).Source)" -ForegroundColor Green 16 | } 17 | 18 | task Clean { 19 | Remove-BuildItem ./artifacts, ./module, ./out 20 | Invoke-BuildExec { dotnet clean ./src/code } 21 | } 22 | 23 | task BuildDocs -If { Test-Path -LiteralPath ./help } { 24 | New-ExternalHelp -Path ./help -OutputPath ./module/en-US 25 | } 26 | 27 | task BuildModule { 28 | New-Item -ItemType Directory -Force ./module | Out-Null 29 | 30 | Invoke-BuildExec { dotnet publish ./src/code -c $Configuration } 31 | 32 | # Hard code building this in release config since we aren't actually developing it, 33 | # it's only for tests. The tests also hard code the path assuming release config. 34 | Invoke-BuildExec { dotnet publish ./ExtensionModules/CredManStore/src/code -c Release } 35 | 36 | $FullModuleName = "Microsoft.PowerShell.SecretManagement" 37 | 38 | $CSharpArtifacts = @( 39 | "$FullModuleName.dll", 40 | "$FullModuleName.pdb", 41 | "$FullModuleName.xml", 42 | "System.Runtime.InteropServices.RuntimeInformation.dll") 43 | 44 | $CSharpArtifacts | ForEach-Object { 45 | $item = "./artifacts/publish/$FullModuleName/$($Configuration.ToLower())/$_" 46 | Copy-Item -Force -LiteralPath $item -Destination ./module 47 | } 48 | 49 | $BaseArtifacts = @( 50 | "src/$FullModuleName.format.ps1xml", 51 | "README.md", 52 | "LICENSE", 53 | "ThirdPartyNotices.txt") 54 | 55 | $BaseArtifacts | ForEach-Object { 56 | $itemToCopy = Join-Path $PSScriptRoot $_ 57 | Copy-Item -Force -LiteralPath $itemToCopy -Destination ./module 58 | } 59 | 60 | [xml]$xml = Get-Content Directory.Build.props 61 | $moduleVersion = $xml.Project.PropertyGroup.ModuleVersion 62 | $manifestContent = Get-Content -LiteralPath "./src/$FullModuleName.psd1" -Raw 63 | $newManifestContent = $manifestContent -replace '{{ModuleVersion}}', $moduleVersion 64 | Set-Content -LiteralPath "./module/$FullModuleName.psd1" -Encoding utf8 -Value $newManifestContent 65 | } 66 | 67 | task PackageModule { 68 | New-Item -ItemType Directory -Force ./out | Out-Null 69 | 70 | try { 71 | Register-PSResourceRepository -Name SecretManagement -Uri ./out -ErrorAction Stop 72 | Publish-PSResource -Path ./module -Repository SecretManagement -Verbose 73 | } finally { 74 | Unregister-PSResourceRepository -Name SecretManagement 75 | } 76 | } 77 | 78 | # AKA Microsoft.PowerShell.SecretManagement.Library 79 | task PackageLibrary -If { $Configuration -eq "Release" } { 80 | Invoke-BuildExec { dotnet pack ./src/code --no-build -c $Configuration -o ./out } 81 | } 82 | 83 | task Test { 84 | Invoke-Pester -CI 85 | } 86 | 87 | task Build BuildModule, BuildDocs 88 | 89 | task Package PackageModule, PackageLibrary 90 | 91 | task . Clean, Build 92 | -------------------------------------------------------------------------------- /SecretManagement.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E65CF2A7-C44A-491E-989D-8EAFAC4497C8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.PowerShell.SecretManagement", "src\code\Microsoft.PowerShell.SecretManagement.csproj", "{90AF98C9-E305-4B37-945E-D79E14E1E8B3}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(SolutionProperties) = preSolution 16 | HideSolutionNode = FALSE 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {90AF98C9-E305-4B37-945E-D79E14E1E8B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {90AF98C9-E305-4B37-945E-D79E14E1E8B3}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {90AF98C9-E305-4B37-945E-D79E14E1E8B3}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {90AF98C9-E305-4B37-945E-D79E14E1E8B3}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(NestedProjects) = preSolution 25 | {90AF98C9-E305-4B37-945E-D79E14E1E8B3} = {E65CF2A7-C44A-491E-989D-8EAFAC4497C8} 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | NOTICES AND INFORMATION 2 | Do Not Translate or Localize 3 | 4 | This software incorporates material from third parties. 5 | Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, 6 | or you may send a check or money order for US $5.00, including the product name, 7 | the open source component name, platform, and version number, to: 8 | 9 | Source Code Compliance Team 10 | Microsoft Corporation 11 | One Microsoft Way 12 | Redmond, WA 98052 13 | USA 14 | 15 | Notwithstanding any other terms, you may reverse engineer this software to the extent 16 | required to debug changes to any libraries licensed under the GNU Lesser General Public License. 17 | 18 | --------------------------------------------------------- 19 | 20 | PowerShellStandard.Library 5.1.0 - MIT 21 | 22 | 23 | (c) 2008 VeriSign, Inc. 24 | Copyright (c) Microsoft Corporation. All rights reserved. 25 | 26 | MIT License 27 | 28 | Copyright (c) Microsoft Corporation. 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 31 | 32 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 33 | 34 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 35 | 36 | --------------------------------------------------------- 37 | 38 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.304", 4 | "rollForward": "latestFeature", 5 | "allowPrerelease": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /help/Get-Secret.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-Secret 9 | 10 | ## SYNOPSIS 11 | Finds and returns a secret by name from registered vaults. 12 | 13 | ## SYNTAX 14 | 15 | ### NameParameterSet (Default) 16 | ``` 17 | Get-Secret [-Name] [[-Vault] ] [-AsPlainText] [] 18 | ``` 19 | 20 | ### InfoParameterSet 21 | ``` 22 | Get-Secret [-InputObject] [-AsPlainText] [] 23 | ``` 24 | 25 | ## DESCRIPTION 26 | This cmdlet finds and returns the first secret that matches the provided name. 27 | If a vault name is specified, then only that vault will be searched. 28 | Otherwise, all vaults are searched and the first found result is returned. 29 | If a 'Default' vault is specified, then that vault is searched before any other registered vault. 30 | Secrets that are string or SecureString types are returned as SecureString objects by default. 31 | Unless the '-AsPlainText' parameter switch is used, in which case the secret is returned as a String type in plain text. 32 | 33 | ## EXAMPLES 34 | 35 | ### Example 1 36 | ``` 37 | PS C:\> Get-Secret -Name Secret1 -Vault CredMan 38 | System.Security.SecureString 39 | 40 | PS C:\> Get-Secret -Name Secret1 -Vault CredMan -AsPlainText 41 | PlainTextSecretString 42 | ``` 43 | 44 | This example searches for a secret with the name 'Secret1', which is a String type secret. 45 | The first time returns the secret as a SecureString object. 46 | The second time uses the '-AsPlainText' and so the secret string is returned as a string object, and is displayed in plain text. 47 | 48 | ### Example 2 49 | ``` 50 | PS C:\> Get-SecretInfo -Name Secret2 -Vault SecretStore | Get-Secret -AsPlainText 51 | ``` 52 | 53 | This example retrieves secret information for the secret named 'Secret2' and then pipe the result to 'Get-Secret'. 54 | The secret is then looked up in the SecretStore vault and returned as plain text. 55 | 56 | ## PARAMETERS 57 | 58 | ### -AsPlainText 59 | Switch parameter that when used returns either a string or SecureString secret type as a String type (in plain text). 60 | If the secret being retrieved is not of string or SecureString type, this switch parameter has no effect. 61 | 62 | ```yaml 63 | Type: SwitchParameter 64 | Parameter Sets: (All) 65 | Aliases: 66 | 67 | Required: False 68 | Position: Named 69 | Default value: False 70 | Accept pipeline input: False 71 | Accept wildcard characters: False 72 | ``` 73 | 74 | ### -InputObject 75 | SecretInformation object that describes a vault secret. 76 | 77 | ```yaml 78 | Type: SecretInformation 79 | Parameter Sets: InfoParameterSet 80 | Aliases: 81 | 82 | Required: True 83 | Position: 0 84 | Default value: None 85 | Accept pipeline input: True (ByValue) 86 | Accept wildcard characters: False 87 | ``` 88 | 89 | ### -Name 90 | Name of the secret to be retrieved. 91 | Wild card characters are not allowed. 92 | 93 | ```yaml 94 | Type: String 95 | Parameter Sets: NameParameterSet 96 | Aliases: 97 | 98 | Required: True 99 | Position: 0 100 | Default value: None 101 | Accept pipeline input: True (ByValue) 102 | Accept wildcard characters: False 103 | ``` 104 | 105 | ### -Vault 106 | Optional name of the registered vault to retrieve the secret from. 107 | If no vault name is specified, then all registered vaults are searched. 108 | 109 | ```yaml 110 | Type: String 111 | Parameter Sets: NameParameterSet 112 | Aliases: 113 | 114 | Required: False 115 | Position: 1 116 | Default value: None 117 | Accept pipeline input: False 118 | Accept wildcard characters: False 119 | ``` 120 | 121 | ### CommonParameters 122 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 123 | 124 | ## INPUTS 125 | 126 | ### System.String 127 | ### Microsoft.PowerShell.SecretManagement.SecretInformation 128 | ## OUTPUTS 129 | 130 | ### System.Object 131 | ## NOTES 132 | 133 | ## RELATED LINKS 134 | -------------------------------------------------------------------------------- /help/Get-SecretInfo.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-SecretInfo 9 | 10 | ## SYNOPSIS 11 | Finds and returns secret metadata information of one or more secrets. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-SecretInfo [[-Name] ] [[-Vault] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | This cmdlet finds and returns secret metadata for secrets with names that match the provided 'Name'. 21 | The 'Name' parameter argument can include wildcards for the search. 22 | If no 'Name' parameter argument is provided then metadata for all secrets is returned. 23 | The search is performed over all registered vaults, unless a specific vault name is specified. 24 | Secret metadata consists of the secret name, secret type, vault name, and optional additional user data if supported by the extension vault. 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1 29 | ```powershell 30 | PS C:\> Get-SecretInfo -Name * 31 | 32 | Name Type VaultName 33 | ---- ---- --------- 34 | Secret1 String LocalStore 35 | Secret2 ByteArray LocalStore 36 | Secret3 SecureString LocalStore 37 | Secret4 PSCredential LocalStore 38 | Secret5 Hashtable LocalStore 39 | Secret6 ByteArray CredMan 40 | ``` 41 | 42 | This example runs the command with the 'Name' parameter argument being a single wildcard character. 43 | So all metadata for all stored secrets is returned. 44 | There are two registered vaults, LocalStore and CredMan. 45 | There are six secrets metadata information returned over the two vaults. 46 | 47 | ### Example 2 48 | ```powershell 49 | PS C:\> Get-SecretInfo -Name APIKey | Select-Object Name,VaultName,Metadata 50 | 51 | Name VaultName Metadata 52 | ---- --------- -------- 53 | APIKey SecretStore {[Target, PSGallery]} 54 | ``` 55 | 56 | This example runs the command for a single secret name and displays the secret name, the vault name, and any metadata associated with the secret. 57 | 58 | ## PARAMETERS 59 | 60 | ### -Name 61 | This parameter takes a String argument, including wildcard characters. 62 | It is used to filter the search results that match on secret names the provided name pattern. 63 | If no 'Name' parameter argument is provided, then all stored secret metadata is returned. 64 | 65 | ```yaml 66 | Type: String 67 | Parameter Sets: (All) 68 | Aliases: 69 | 70 | Required: False 71 | Position: 0 72 | Default value: None 73 | Accept pipeline input: False 74 | Accept wildcard characters: True 75 | ``` 76 | 77 | ### -Vault 78 | Optional parameter which takes a String argument that specifies a single vault to search. 79 | 80 | ```yaml 81 | Type: String 82 | Parameter Sets: (All) 83 | Aliases: 84 | 85 | Required: False 86 | Position: 1 87 | Default value: None 88 | Accept pipeline input: False 89 | Accept wildcard characters: False 90 | ``` 91 | 92 | ### CommonParameters 93 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 94 | 95 | ## INPUTS 96 | 97 | ### None 98 | ## OUTPUTS 99 | 100 | ### Microsoft.PowerShell.SecretManagement.SecretInformation 101 | ## NOTES 102 | 103 | ## RELATED LINKS 104 | -------------------------------------------------------------------------------- /help/Get-SecretVault.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-SecretVault 9 | 10 | ## SYNOPSIS 11 | Finds and returns registered vault information. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-SecretVault [[-Name] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | This cmdlet finds and returns information of registered vaults. 21 | It takes an array of vault name strings, which can contain wildcard characters. 22 | If no 'Name' parameter is specified, all registered vault information is returned. 23 | The registered vault information includes the vault name, vault implementing module name, and optional default parameters. 24 | 25 | ## EXAMPLES 26 | 27 | ### Example 1 28 | ``` 29 | PS C:\> Get-SecretVault 30 | 31 | VaultName ModuleName IsDefaultVault 32 | --------- ---------- -------------- 33 | CredMan Microsoft.PowerShell.CredManStore False 34 | LocalStore Microsoft.PowerShell.SecretStore True 35 | ``` 36 | 37 | This example runs the command without any parameter arguments, and so returns information on all registered vaults. 38 | The 'LocalStore' vault is shown to be set as the default vault. 39 | 40 | ## PARAMETERS 41 | 42 | ### -Name 43 | This parameter takes a String argument, including wildcard characters. 44 | It is used to filter the search results on vault names that match the provided name pattern. 45 | 46 | ```yaml 47 | Type: String[] 48 | Parameter Sets: (All) 49 | Aliases: 50 | 51 | Required: False 52 | Position: 0 53 | Default value: None 54 | Accept pipeline input: False 55 | Accept wildcard characters: True 56 | ``` 57 | 58 | ### CommonParameters 59 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 60 | 61 | ## INPUTS 62 | 63 | ### None 64 | ## OUTPUTS 65 | 66 | ### Microsoft.PowerShell.SecretManagement.SecretVaultInfo 67 | ## NOTES 68 | 69 | ## RELATED LINKS 70 | -------------------------------------------------------------------------------- /help/Register-SecretVault.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Register-SecretVault 9 | 10 | ## SYNOPSIS 11 | Registers a SecretManagement extension vault module for the current user. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Register-SecretVault [-ModuleName] [[-Name] ] [-VaultParameters ] [-DefaultVault] 17 | [-AllowClobber] [-PassThru] [-Description ] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | This cmdlet adds a provided SecretManagement extension vault module to the current user vault registry. 22 | An extension vault module is a PowerShell module that conforms to the required extension vault format. 23 | This cmdlet will first verify that the provided module meets conformance requirements, and then add it to the extension vault registry. 24 | Extension vaults are registered to the current user and do not affect other user vault registrations. 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1 29 | ```powershell 30 | PS C:\> Register-SecretVault -Name LocalStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault 31 | PS C:\> Get-SecretVault 32 | 33 | VaultName ModuleName IsDefaultVault 34 | --------- ---------- -------------- 35 | CredMan Microsoft.PowerShell.CredManStore False 36 | LocalStore Microsoft.PowerShell.SecretStore True 37 | ``` 38 | 39 | This example registers the Microsoft.PowerShell.SecretStore extension vault module for the current user. 40 | The 'Microsoft.PowerShell.SecretStore' is installed in a known PowerShell module path, so just the module name is needed. 41 | It uses the 'DefaultVault' parameter switch to make it the default module for the user. 42 | The 'Get-SecretVault' command is run next to list all registered vaults for the user, and verifies the vault was registered and set as the default vault. 43 | 44 | ## PARAMETERS 45 | 46 | ### -AllowClobber 47 | When used this parameter will overwrite an existing registered extension vault with the same name. 48 | 49 | ```yaml 50 | Type: SwitchParameter 51 | Parameter Sets: (All) 52 | Aliases: 53 | 54 | Required: False 55 | Position: Named 56 | Default value: False 57 | Accept pipeline input: False 58 | Accept wildcard characters: False 59 | ``` 60 | 61 | ### -DefaultVault 62 | This parameter switch makes the new extension vault the default vault for the current user. 63 | 64 | ```yaml 65 | Type: SwitchParameter 66 | Parameter Sets: (All) 67 | Aliases: 68 | 69 | Required: False 70 | Position: Named 71 | Default value: False 72 | Accept pipeline input: False 73 | Accept wildcard characters: False 74 | ``` 75 | 76 | ### -Description 77 | This parameter takes a description string that is included in the vault registry information. 78 | 79 | ```yaml 80 | Type: String 81 | Parameter Sets: (All) 82 | Aliases: 83 | 84 | Required: False 85 | Position: Named 86 | Default value: None 87 | Accept pipeline input: False 88 | Accept wildcard characters: False 89 | ``` 90 | 91 | ### -ModuleName 92 | Name of the PowerShell module that implements the extension vault. 93 | It can be a simple name, in which case PowerShell will search for it in its known module paths. 94 | Alternatively, a pathname can be provided and PowerShell will look in the specific path for the module. 95 | 96 | ```yaml 97 | Type: String 98 | Parameter Sets: (All) 99 | Aliases: 100 | 101 | Required: True 102 | Position: 1 103 | Default value: None 104 | Accept pipeline input: False 105 | Accept wildcard characters: False 106 | ``` 107 | 108 | ### -Name 109 | Name of the extension vault to be registered. 110 | If no name is provide, the module name will be used. 111 | 112 | ```yaml 113 | Type: String 114 | Parameter Sets: (All) 115 | Aliases: 116 | 117 | Required: False 118 | Position: 0 119 | Default value: None 120 | Accept pipeline input: False 121 | Accept wildcard characters: False 122 | ``` 123 | 124 | ### -PassThru 125 | When used this parameter will return the SecretVaultInfo object for the successfully registered extension vault. 126 | 127 | ```yaml 128 | Type: SwitchParameter 129 | Parameter Sets: (All) 130 | Aliases: 131 | 132 | Required: False 133 | Position: Named 134 | Default value: False 135 | Accept pipeline input: False 136 | Accept wildcard characters: False 137 | ``` 138 | 139 | ### -VaultParameters 140 | This takes a hashtable object that contains optional parameter name-value pairs needed by the extension vault. 141 | These optional parameters are provided to the extension vault when invoked. 142 | 143 | ```yaml 144 | Type: Hashtable 145 | Parameter Sets: (All) 146 | Aliases: 147 | 148 | Required: False 149 | Position: Named 150 | Default value: None 151 | Accept pipeline input: False 152 | Accept wildcard characters: False 153 | ``` 154 | 155 | ### -Confirm 156 | Prompts you for confirmation before running the cmdlet. 157 | 158 | ```yaml 159 | Type: SwitchParameter 160 | Parameter Sets: (All) 161 | Aliases: cf 162 | 163 | Required: False 164 | Position: Named 165 | Default value: False 166 | Accept pipeline input: False 167 | Accept wildcard characters: False 168 | ``` 169 | 170 | ### -WhatIf 171 | Shows what would happen if the cmdlet runs. 172 | The cmdlet is not run. 173 | 174 | ```yaml 175 | Type: SwitchParameter 176 | Parameter Sets: (All) 177 | Aliases: wi 178 | 179 | Required: False 180 | Position: Named 181 | Default value: False 182 | Accept pipeline input: False 183 | Accept wildcard characters: False 184 | ``` 185 | 186 | ### CommonParameters 187 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 188 | 189 | ## INPUTS 190 | 191 | ### None 192 | ## OUTPUTS 193 | 194 | ## NOTES 195 | 196 | ## RELATED LINKS 197 | -------------------------------------------------------------------------------- /help/Remove-Secret.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Remove-Secret 9 | 10 | ## SYNOPSIS 11 | Removes a secret from a specified registered extension vault. 12 | 13 | ## SYNTAX 14 | 15 | ### NameParameterSet 16 | ``` 17 | Remove-Secret [-Name] [-Vault] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ### InfoParameterSet 21 | ``` 22 | Remove-Secret [-InputObject] [-WhatIf] [-Confirm] [] 23 | ``` 24 | 25 | ## DESCRIPTION 26 | This cmdlet will remove a secret by name from a registered extension vault. 27 | Both the secret name and extension vault name must be provided. 28 | 29 | ## EXAMPLES 30 | 31 | ### Example 1 32 | ```powershell 33 | PS C:\> Remove-Secret -Name secretTest -Vault CredMan 34 | PS C:\> Get-Secret -Name secretTest -Vault CredMan 35 | Get-Secret: The secret secretTest was not found. 36 | ``` 37 | 38 | This example runs the command to remove the secret 'secretTest' from the CredMan vault. 39 | The 'Get-Secret' command is next run to verify the secret no longer exists in the vault. 40 | 41 | ### Example 2 42 | ``` 43 | PS C:\> Get-SecretInfo -Name Secret2 -Vault CredMan | Remove-Secret 44 | PS C:\> Get-Secret -Name Secret2 -Vault CredMan 45 | Get-Secret: The secret Secret2 was not found. 46 | ``` 47 | 48 | This example first obtains secret information for the 'Secret2' secret and pipes the results to this command. 49 | Remove-Secret then removes the secret from the vault using the piped in secret information. 50 | 51 | ## PARAMETERS 52 | 53 | ### -InputObject 54 | SecretInformation object that describes a vault secret. 55 | 56 | ```yaml 57 | Type: SecretInformation 58 | Parameter Sets: InfoParameterSet 59 | Aliases: 60 | 61 | Required: True 62 | Position: 0 63 | Default value: None 64 | Accept pipeline input: True (ByValue) 65 | Accept wildcard characters: False 66 | ``` 67 | 68 | ### -Name 69 | Name of the secret to remove. 70 | 71 | ```yaml 72 | Type: String 73 | Parameter Sets: NameParameterSet 74 | Aliases: 75 | 76 | Required: True 77 | Position: 0 78 | Default value: None 79 | Accept pipeline input: True (ByValue) 80 | Accept wildcard characters: False 81 | ``` 82 | 83 | ### -Vault 84 | Name of the vault from which the secret is to be removed. 85 | 86 | ```yaml 87 | Type: String 88 | Parameter Sets: NameParameterSet 89 | Aliases: 90 | 91 | Required: True 92 | Position: 1 93 | Default value: None 94 | Accept pipeline input: False 95 | Accept wildcard characters: False 96 | ``` 97 | 98 | ### -Confirm 99 | Prompts you for confirmation before running the cmdlet. 100 | 101 | ```yaml 102 | Type: SwitchParameter 103 | Parameter Sets: (All) 104 | Aliases: cf 105 | 106 | Required: False 107 | Position: Named 108 | Default value: False 109 | Accept pipeline input: False 110 | Accept wildcard characters: False 111 | ``` 112 | 113 | ### -WhatIf 114 | Shows what would happen if the cmdlet runs. 115 | The cmdlet is not run. 116 | 117 | ```yaml 118 | Type: SwitchParameter 119 | Parameter Sets: (All) 120 | Aliases: wi 121 | 122 | Required: False 123 | Position: Named 124 | Default value: False 125 | Accept pipeline input: False 126 | Accept wildcard characters: False 127 | ``` 128 | 129 | ### CommonParameters 130 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 131 | 132 | ## INPUTS 133 | 134 | ### System.String 135 | ### Microsoft.PowerShell.SecretManagement.SecretInformation 136 | ## OUTPUTS 137 | 138 | ## NOTES 139 | 140 | ## RELATED LINKS 141 | -------------------------------------------------------------------------------- /help/Set-Secret.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-Secret 9 | 10 | ## SYNOPSIS 11 | Adds a secret to a SecretManagement registered vault. 12 | 13 | ## SYNTAX 14 | 15 | ### SecureStringParameterSet (Default) 16 | ``` 17 | Set-Secret [-Name] -SecureStringSecret [[-Vault] ] [[-Metadata] ] 18 | [-NoClobber] [-WhatIf] [-Confirm] [] 19 | ``` 20 | 21 | ### ObjectParameterSet 22 | ``` 23 | Set-Secret [-Name] -Secret [[-Vault] ] [[-Metadata] ] [-NoClobber] 24 | [-WhatIf] [-Confirm] [] 25 | ``` 26 | 27 | ### SecretInfoParameterSet 28 | ``` 29 | Set-Secret -SecretInfo [-Vault] [-NoClobber] [-WhatIf] [-Confirm] 30 | [] 31 | ``` 32 | 33 | ## DESCRIPTION 34 | This cmdlet adds a secret value by name to SecretManagement. 35 | If no vault name is specified, then the secret will be added to the default vault. 36 | If an existing secret by the same name exists, it will be overwritten with the new value unless the 'NoClobber' parameter switch is used. 37 | Additional data can be included with the secret through the '-Metadata' parameter, if supported by the extension vault. 38 | If the extension vault does not support metadata then an error will be generated and the operation will fail. 39 | Metadata is not required to be stored securely, and should not contain sensitive information. 40 | The secret value must be one of five supported types: 41 | 42 | - byte[] 43 | - String 44 | - SecureString 45 | - PSCredential 46 | - Hashtable 47 | 48 | The default parameter set takes a SecureString object. 49 | So if the command is run without specifying the secret value, the user will be safely prompted to enter a SecureString which cannot be seen on the console. 50 | 51 | ## EXAMPLES 52 | 53 | ### Example 1 54 | ```powershell 55 | PS C:\> Set-Secret -Name Secret1 -Secret "SecretValue" 56 | PS C:\> Get-Secret -Name Secret1 57 | System.Security.SecureString 58 | ``` 59 | 60 | This example adds a secret named 'Secret1' with a plain text value of 'SecretValue'. 61 | Since no vault name was specified, the secret is added to the current default vault. 62 | Next, the 'Get-Secret' command is run to verify the added secret. 63 | 64 | ### Example 2 65 | ```powershell 66 | PS C:\> Set-Secret -Name Secret2 -Vault LocalStore 67 | 68 | cmdlet Set-Secret at command pipeline position 1 69 | Supply values for the following parameters: 70 | SecureStringSecret: *********** 71 | 72 | PS C:\> Get-Secret -Name Secret2 73 | System.Security.SecureString 74 | ``` 75 | 76 | This example adds a secret named 'Secret2' to the LocalStore vault. 77 | Since no secret value was provided, the user is prompted for a SecureString value. 78 | The console hides the string value as it is typed. 79 | Next, the 'Get-Secret' command is run to verify the secret was added. 80 | 81 | ### Example 3 82 | ```powershell 83 | PS C:\> Set-Secret -Name TargetSecret -Secret $targetToken -Vault LocalStore -Metadata @{ Expiration = ([datetime]::new(2022, 5, 1)) } 84 | PS C:\> Get-SecretInfo -Name TargetSecret | Select-Object Name,Metadata 85 | 86 | Name Metadata 87 | ---- -------- 88 | TargetSecret {[Expiration, 5/1/2022 12:00:00 AM]} 89 | ``` 90 | 91 | This example adds a secret named 'TargetSecret' to the LocalStore vault, along with extra metadata indicating the secret expiration date. 92 | The metadata is retrieved using the 'Get-SecretInfo' cmdlet. 93 | 94 | ### Example 4 95 | ```powershell 96 | PS C:\> Set-Secret -Name PublishSecret -Secret $targetToken -Vault LocalStore2 -Metadata @{ Expiration = ([datetime]::new(2022, 5, 1)) } 97 | Set-Secret: Cannot store secret PublishSecret. Vault LocalStore2 does not support secret metadata. 98 | ``` 99 | 100 | This example adds a secret named 'PublishSecret' to the LocalStore2 vault, along with extra metadata. 101 | However, vault LocalStore2 does not support secret metadata and the operation fails with error. 102 | 103 | ## PARAMETERS 104 | 105 | ### -Metadata 106 | Hashtable containing Name/Value pair that are stored in the vault. 107 | The specified extension vault may not support secret metadata, in which case the operation will fail. 108 | The metadata Name/Value value type must be one of the following: 109 | - string 110 | - int 111 | - DateTime 112 | 113 | ```yaml 114 | Type: Hashtable 115 | Parameter Sets: SecureStringParameterSet, ObjectParameterSet 116 | Aliases: 117 | 118 | Required: False 119 | Position: 1 120 | Default value: None 121 | Accept pipeline input: False 122 | Accept wildcard characters: False 123 | ``` 124 | 125 | ### -Name 126 | Name of secret for which the metadata is added 127 | 128 | ```yaml 129 | Type: String 130 | Parameter Sets: SecureStringParameterSet, ObjectParameterSet 131 | Aliases: 132 | 133 | Required: True 134 | Position: 0 135 | Default value: None 136 | Accept pipeline input: False 137 | Accept wildcard characters: False 138 | ``` 139 | 140 | ### -NoClobber 141 | When true, this command will not overwrite an existing secret and emit an error instead. 142 | 143 | ```yaml 144 | Type: SwitchParameter 145 | Parameter Sets: (All) 146 | Aliases: 147 | 148 | Required: False 149 | Position: Named 150 | Default value: False 151 | Accept pipeline input: False 152 | Accept wildcard characters: False 153 | ``` 154 | 155 | ### -Secret 156 | A secret value to be added. 157 | The object type must be one of the supported types. 158 | 159 | ```yaml 160 | Type: Object 161 | Parameter Sets: ObjectParameterSet 162 | Aliases: 163 | 164 | Required: True 165 | Position: Named 166 | Default value: None 167 | Accept pipeline input: True (ByValue) 168 | Accept wildcard characters: False 169 | ``` 170 | 171 | ### -SecretInfo 172 | A SecretInformation object describing a stored secret returned by 'Get-SecretInfo'. 173 | This allows moving secrets from one extension vault to another. 174 | 175 | ```yaml 176 | Type: SecretInformation 177 | Parameter Sets: SecretInfoParameterSet 178 | Aliases: 179 | 180 | Required: True 181 | Position: Named 182 | Default value: None 183 | Accept pipeline input: True (ByValue) 184 | Accept wildcard characters: False 185 | ``` 186 | 187 | ### -SecureStringSecret 188 | A secret SecretString object to be added. 189 | 190 | ```yaml 191 | Type: SecureString 192 | Parameter Sets: SecureStringParameterSet 193 | Aliases: 194 | 195 | Required: True 196 | Position: Named 197 | Default value: None 198 | Accept pipeline input: True (ByValue) 199 | Accept wildcard characters: False 200 | ``` 201 | 202 | ### -Vault 203 | Optional name of vault to which the secret is added. 204 | If omitted, the secret will be added to the default vault. 205 | 206 | ```yaml 207 | Type: String 208 | Parameter Sets: SecureStringParameterSet, ObjectParameterSet 209 | Aliases: 210 | 211 | Required: False 212 | Position: 2 213 | Default value: None 214 | Accept pipeline input: False 215 | Accept wildcard characters: False 216 | ``` 217 | 218 | ```yaml 219 | Type: String 220 | Parameter Sets: SecretInfoParameterSet 221 | Aliases: 222 | 223 | Required: True 224 | Position: 2 225 | Default value: None 226 | Accept pipeline input: False 227 | Accept wildcard characters: False 228 | ``` 229 | 230 | ### -Confirm 231 | Prompts you for confirmation before running the cmdlet. 232 | 233 | ```yaml 234 | Type: SwitchParameter 235 | Parameter Sets: (All) 236 | Aliases: cf 237 | 238 | Required: False 239 | Position: Named 240 | Default value: False 241 | Accept pipeline input: False 242 | Accept wildcard characters: False 243 | ``` 244 | 245 | ### -WhatIf 246 | Shows what would happen if the cmdlet runs. 247 | The cmdlet is not run. 248 | 249 | ```yaml 250 | Type: SwitchParameter 251 | Parameter Sets: (All) 252 | Aliases: wi 253 | 254 | Required: False 255 | Position: Named 256 | Default value: False 257 | Accept pipeline input: False 258 | Accept wildcard characters: False 259 | ``` 260 | 261 | ### CommonParameters 262 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 263 | 264 | ## INPUTS 265 | 266 | ### System.Collections.Hashtable 267 | ## OUTPUTS 268 | 269 | ## NOTES 270 | 271 | ## RELATED LINKS 272 | -------------------------------------------------------------------------------- /help/Set-SecretInfo.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-SecretInfo 9 | 10 | ## SYNOPSIS 11 | Adds or replaces additional secret metadata to a secret currently stored in a vault. 12 | 13 | ## SYNTAX 14 | 15 | ### NameParameterSet (Default) 16 | ``` 17 | Set-SecretInfo [-Name] [-Metadata] [[-Vault] ] [-WhatIf] [-Confirm] 18 | [] 19 | ``` 20 | 21 | ### InfoParameterSet 22 | ``` 23 | Set-SecretInfo [-Metadata] -InputObject [-WhatIf] [-Confirm] 24 | [] 25 | ``` 26 | 27 | ## DESCRIPTION 28 | This cmdlet adds additional secret metadata to an existing secret. 29 | Metadata support is an optional feature for an extension vault. 30 | An error will be thrown if a vault does not support secret metadata. 31 | Metadata is a Hashtable object containing Name/Value pairs. 32 | The value type is restricted to the following: 33 | 34 | - string 35 | - int 36 | - DateTime 37 | 38 | Metadata is not stored securely in a vault. 39 | Metadata should not contain sensitive information. 40 | 41 | ## EXAMPLES 42 | 43 | ### Example 1 44 | ```powershell 45 | PS C:\> Set-SecretInfo -Name Secret1 -Vault Vault1 -Metadata @{ Expiration = ([datetime]::new(2022, 5, 1)) } 46 | PS C:\> Get-SecretInfo -Name Secret1 -Vault Vault1 | Select-Object Name,Metadata 47 | 48 | Name Metadata 49 | ---- -------- 50 | Secret1 {[Expiration, 5/1/2022 12:00:00 AM]} 51 | ``` 52 | 53 | This example adds metadata to the 'Secret1' secret stored in 'Vault1' vault. 54 | The metadata is then retrieved for 'Secret1' using the 'Get-SecretInfo' command. 55 | 56 | ### Example 2 57 | ```powershell 58 | PS C:\> Set-SecretInfo -Name Secret2 -Vault Vault2 -Metadata @{ Expiration = ([datetime]::new(2022, 5, 1)) } 59 | Set-SecretInfo: Cannot set secret metadata Secret2. Vault Vault2 does not support secret metadata. 60 | ``` 61 | 62 | This example adds metadata to the 'Secret2' secret stored in 'Vault2' vault. 63 | However, Vault2 does not support metadata and an error is generated. 64 | 65 | ### Example 3 66 | ```powershell 67 | PS C:\> Get-SecretInfo -Name Secret3 | Set-SecretInfo -Metadata @{ Created = (Get-Date) } 68 | ``` 69 | 70 | This example pipes a SecretInformation object to the 'Set-SecretInfo' command and adds metadata to the associated secret. 71 | 72 | ## PARAMETERS 73 | 74 | ### -InputObject 75 | This parameter takes a SecretInformation object that defines the secret to be updated. 76 | 77 | ```yaml 78 | Type: SecretInformation 79 | Parameter Sets: InfoParameterSet 80 | Aliases: 81 | 82 | Required: True 83 | Position: Named 84 | Default value: None 85 | Accept pipeline input: True (ByValue) 86 | Accept wildcard characters: False 87 | ``` 88 | 89 | ### -Metadata 90 | Hashtable containing Name/Value pair that are stored in the vault. 91 | The specified extension vault may not support secret metadata, in which case the operation will fail. 92 | The metadata Name/Value value type must be one of the following: 93 | - string 94 | - int 95 | - DateTime 96 | 97 | ```yaml 98 | Type: Hashtable 99 | Parameter Sets: NameParameterSet 100 | Aliases: 101 | 102 | Required: True 103 | Position: 1 104 | Default value: None 105 | Accept pipeline input: True (ByValue) 106 | Accept wildcard characters: False 107 | ``` 108 | 109 | ```yaml 110 | Type: Hashtable 111 | Parameter Sets: InfoParameterSet 112 | Aliases: 113 | 114 | Required: True 115 | Position: 1 116 | Default value: None 117 | Accept pipeline input: True (ByValue) 118 | Accept wildcard characters: False 119 | ``` 120 | 121 | ### -Name 122 | Name of secret for which the metadata is added. 123 | 124 | ```yaml 125 | Type: String 126 | Parameter Sets: NameParameterSet 127 | Aliases: 128 | 129 | Required: True 130 | Position: 0 131 | Default value: None 132 | Accept pipeline input: False 133 | Accept wildcard characters: False 134 | ``` 135 | 136 | ### -Vault 137 | Optional name of vault to which the secret is added. 138 | If omitted, the secret will be added to the default vault. 139 | 140 | ```yaml 141 | Type: String 142 | Parameter Sets: NameParameterSet 143 | Aliases: 144 | 145 | Required: False 146 | Position: 2 147 | Default value: None 148 | Accept pipeline input: False 149 | Accept wildcard characters: False 150 | ``` 151 | 152 | ### -Confirm 153 | Prompts you for confirmation before running the cmdlet. 154 | 155 | ```yaml 156 | Type: SwitchParameter 157 | Parameter Sets: (All) 158 | Aliases: cf 159 | 160 | Required: False 161 | Position: Named 162 | Default value: False 163 | Accept pipeline input: False 164 | Accept wildcard characters: False 165 | ``` 166 | 167 | ### -WhatIf 168 | Shows what would happen if the cmdlet runs. 169 | The cmdlet is not run. 170 | 171 | ```yaml 172 | Type: SwitchParameter 173 | Parameter Sets: (All) 174 | Aliases: wi 175 | 176 | Required: False 177 | Position: Named 178 | Default value: False 179 | Accept pipeline input: False 180 | Accept wildcard characters: False 181 | ``` 182 | 183 | ### CommonParameters 184 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 185 | 186 | ## INPUTS 187 | 188 | ### System.Collections.Hashtable 189 | ## OUTPUTS 190 | 191 | ### System.Object 192 | ## NOTES 193 | 194 | ## RELATED LINKS 195 | -------------------------------------------------------------------------------- /help/Set-SecretVaultDefault.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-SecretVaultDefault 9 | 10 | ## SYNOPSIS 11 | Sets the provided vault name as the default vault for the current user. 12 | 13 | ## SYNTAX 14 | 15 | ### NameParameterSet (Default) 16 | ``` 17 | Set-SecretVaultDefault [-Name] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ### SecretVaultParameterSet 21 | ``` 22 | Set-SecretVaultDefault [-SecretVault] [-WhatIf] [-Confirm] [] 23 | ``` 24 | 25 | ### ClearParameterSet 26 | ``` 27 | Set-SecretVaultDefault [-ClearDefault] [-WhatIf] [-Confirm] [] 28 | ``` 29 | 30 | ## DESCRIPTION 31 | This cmdlet updates the vault registry to designate the provided vault name as the default vault. 32 | Only one registered vault can be the default vault. 33 | If this cmdlet is run without specifying the 'Name' parameter, then no registered vault is designated as the default vault. 34 | 35 | ## EXAMPLES 36 | 37 | ### Example 1 38 | ```powershell 39 | PS C:\> Get-SecretVault 40 | 41 | VaultName ModuleName IsDefaultVault 42 | --------- ---------- -------------- 43 | CredMan Microsoft.PowerShell.CredManStore False 44 | LocalStore Microsoft.PowerShell.SecretStore True 45 | 46 | PS C:\> Set-SecretVaultDefault -Name CredMan 47 | PS C:\> Get-SecretVault 48 | 49 | VaultName ModuleName IsDefaultVault 50 | --------- ---------- -------------- 51 | CredMan Microsoft.PowerShell.CredManStore True 52 | LocalStore Microsoft.PowerShell.SecretStore False 53 | 54 | PS C:\> Set-SecretVaultDefault 55 | PS C:\> Get-SecretVault 56 | 57 | VaultName ModuleName IsDefaultVault 58 | --------- ---------- -------------- 59 | CredMan Microsoft.PowerShell.CredManStore False 60 | LocalStore Microsoft.PowerShell.SecretStore False 61 | ``` 62 | 63 | This cmdlet first runs 'Get-SecretVault' command to get all registered vault information, and shows that the 'LocalStore' is currently the default vault for the user. 64 | Next, the 'Set-SecretVaultDefault' command is run to make the 'CredMan' vault the default vault. 65 | The 'Get-SecretVault' command is run a second time to verify 'CredMan' vault is now default, and 'LocalStore' vault is no longer default. 66 | Finally, the 'Set-SecretVaultDefault' command is run with no 'Name' parameter, to remove the default designation from any registered vault. 67 | The 'Get-SecretVault' is run once again to verify there is no default vault. 68 | 69 | ## PARAMETERS 70 | 71 | ### -ClearDefault 72 | Makes no registered vault the default vault. 73 | 74 | ```yaml 75 | Type: SwitchParameter 76 | Parameter Sets: ClearParameterSet 77 | Aliases: 78 | 79 | Required: False 80 | Position: 0 81 | Default value: False 82 | Accept pipeline input: False 83 | Accept wildcard characters: False 84 | ``` 85 | 86 | ### -Name 87 | Name of registered vault to be made the default vault. 88 | 89 | ```yaml 90 | Type: String 91 | Parameter Sets: NameParameterSet 92 | Aliases: 93 | 94 | Required: True 95 | Position: 0 96 | Default value: None 97 | Accept pipeline input: True (ByValue) 98 | Accept wildcard characters: False 99 | ``` 100 | 101 | ### -SecretVault 102 | A SecretVaultInfo object that represents the registered vault to be made the default vault. 103 | 104 | ```yaml 105 | Type: SecretVaultInfo 106 | Parameter Sets: SecretVaultParameterSet 107 | Aliases: 108 | 109 | Required: True 110 | Position: 0 111 | Default value: None 112 | Accept pipeline input: True (ByPropertyName, ByValue) 113 | Accept wildcard characters: False 114 | ``` 115 | 116 | ### -Confirm 117 | Prompts you for confirmation before running the cmdlet. 118 | 119 | ```yaml 120 | Type: SwitchParameter 121 | Parameter Sets: (All) 122 | Aliases: cf 123 | 124 | Required: False 125 | Position: Named 126 | Default value: False 127 | Accept pipeline input: False 128 | Accept wildcard characters: False 129 | ``` 130 | 131 | ### -WhatIf 132 | Shows what would happen if the cmdlet runs. 133 | The cmdlet is not run. 134 | 135 | ```yaml 136 | Type: SwitchParameter 137 | Parameter Sets: (All) 138 | Aliases: wi 139 | 140 | Required: False 141 | Position: Named 142 | Default value: False 143 | Accept pipeline input: False 144 | Accept wildcard characters: False 145 | ``` 146 | 147 | ### CommonParameters 148 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 149 | 150 | ## INPUTS 151 | 152 | ### None 153 | ## OUTPUTS 154 | 155 | ## NOTES 156 | 157 | ## RELATED LINKS 158 | -------------------------------------------------------------------------------- /help/Test-SecretVault.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Test-SecretVault 9 | 10 | ## SYNOPSIS 11 | Runs an extension vault self test. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Test-SecretVault [[-Name] ] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | This cmdlet runs an extension vault self test, by running the internal vault 'Test-SecretVault' command. 21 | It will return 'True' if all tests succeeded, and 'False' otherwise. 22 | Information on failing tests will be written to the error stream as error records. 23 | For more information during the test run use the -Verbose command switch. 24 | 25 | ## EXAMPLES 26 | 27 | ### Example 1 28 | ```powershell 29 | PS C:\> Test-SecretVault -Name CredMan -Verbose 30 | VERBOSE: Invoking command Test-SecretVault on module Microsoft.PowerShell.CredManStore.Extension 31 | VERBOSE: Vault CredMan succeeded validation test 32 | True 33 | ``` 34 | 35 | This example runs self tests on the 'CredMan' extension vault. 36 | All tests succeeded so no errors are written and 'True' is returned. 37 | 38 | ## PARAMETERS 39 | 40 | ### -Name 41 | Name of vault to run self tests on. 42 | 43 | ```yaml 44 | Type: String[] 45 | Parameter Sets: (All) 46 | Aliases: 47 | 48 | Required: False 49 | Position: 1 50 | Default value: None 51 | Accept pipeline input: True (ByPropertyName, ByValue) 52 | Accept wildcard characters: True 53 | ``` 54 | 55 | ### CommonParameters 56 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 57 | 58 | ## INPUTS 59 | 60 | ### None 61 | ## OUTPUTS 62 | 63 | ### System.Boolean 64 | ## NOTES 65 | 66 | ## RELATED LINKS 67 | -------------------------------------------------------------------------------- /help/Unlock-SecretVault.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Unlock-SecretVault 9 | 10 | ## SYNOPSIS 11 | Unlocks an extension vault so that it can be access in the current session. 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Unlock-SecretVault [-Name] [-Password] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | This cmdlet unlocks an extension vault using the provided Password parameter argument. 21 | This allows a vault that requires password authentication to operate without first having to prompt 22 | the user. 23 | Not all extension vaults require password authentication, in which case this command has no effect. 24 | A warning will be emitted if the extension vault does not support unlocking via password. 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1 29 | ```powershell 30 | PS C:\> Unlock-SecretVault -Name SecretStore -Password $SecurePassword 31 | PS C:\> Get-SecretInfo -Vault SecretStore 32 | 33 | Name Type VaultName 34 | ---- ---- --------- 35 | Secret1 SecureString SecretStore 36 | Secret2 SecureString SecretStore 37 | ``` 38 | 39 | This example uses the command to unlock the SecretStore vault. 40 | It then runs the 'Get-SecretInfo' command on the vault without being prompted for the vault 41 | password. 42 | 43 | ### Example 2 44 | ```powershell 45 | PS C:\> Unlock-SecretVault -Name CredMan -Password $SecurePassword 46 | WARNING: Cannot unlock extension vault 'CredMan': The vault does not support the Unlock-SecretVault function. 47 | PS C:\> 48 | ``` 49 | 50 | This example uses the command to unlock the CredMan vault. 51 | But the vault does not support unlocking so the command has no effect. 52 | A warning is displayed informing that CredMan vault does not support unlocking. 53 | 54 | ## PARAMETERS 55 | 56 | ### -Name 57 | Name of the vault to unlock. 58 | 59 | ```yaml 60 | Type: String 61 | Parameter Sets: (All) 62 | Aliases: 63 | 64 | Required: True 65 | Position: 0 66 | Default value: None 67 | Accept pipeline input: False 68 | Accept wildcard characters: False 69 | ``` 70 | 71 | ### -Password 72 | Password used to unlock the vault. 73 | 74 | ```yaml 75 | Type: SecureString 76 | Parameter Sets: (All) 77 | Aliases: 78 | 79 | Required: True 80 | Position: 1 81 | Default value: None 82 | Accept pipeline input: False 83 | Accept wildcard characters: False 84 | ``` 85 | 86 | ### CommonParameters 87 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 88 | 89 | ## INPUTS 90 | 91 | ### None 92 | 93 | ## OUTPUTS 94 | 95 | ### System.Object 96 | ## NOTES 97 | 98 | ## RELATED LINKS 99 | -------------------------------------------------------------------------------- /help/Unregister-SecretVault.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Microsoft.PowerShell.SecretManagement.dll-Help.xml 3 | Module Name: Microsoft.PowerShell.SecretManagement 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Unregister-SecretVault 9 | 10 | ## SYNOPSIS 11 | Un-registers an extension vault from SecretManagement for the current user. 12 | 13 | ## SYNTAX 14 | 15 | ### NameParameterSet (Default) 16 | ``` 17 | Unregister-SecretVault [-Name] [-WhatIf] [-Confirm] [] 18 | ``` 19 | 20 | ### SecretVaultParameterSet 21 | ``` 22 | Unregister-SecretVault [-SecretVault] [-WhatIf] [-Confirm] [] 23 | ``` 24 | 25 | ## DESCRIPTION 26 | This cmdlet un-registers the specified extension vault. 27 | Once un-registered, the vault is no longer available to SecretManagement, for the current user. 28 | 29 | ## EXAMPLES 30 | 31 | ### Example 1 32 | ```powershell 33 | PS C:\> Get-SecretVault 34 | 35 | VaultName ModuleName IsDefaultVault 36 | --------- ---------- -------------- 37 | CredMan Microsoft.PowerShell.CredManStore False 38 | LocalStore Microsoft.PowerShell.SecretStore True 39 | 40 | PS C:\> Unregister-SecretVault LocalStore 41 | PS C:\> Get-SecretVault 42 | 43 | VaultName ModuleName IsDefaultVault 44 | --------- ---------- -------------- 45 | CredMan Microsoft.PowerShell.CredManStore False 46 | 47 | PS C:\> Get-Secret -Name Secret5 48 | Get-Secret: The secret Secret5 was not found. 49 | 50 | PS C:\> Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault 51 | PS C:\> Get-SecretVault 52 | 53 | VaultName ModuleName IsDefaultVault 54 | --------- ---------- -------------- 55 | CredMan Microsoft.PowerShell.CredManStore False 56 | SecretStore Microsoft.PowerShell.SecretStore True 57 | 58 | PS C:\> Get-Secret -Name Secret5 59 | System.Security.SecureString 60 | ``` 61 | 62 | In this example, 'Get-SecretVault' command is run to see what vaults are registered for the current user. 63 | Next, the 'LocalStore' vault is un-registered. 64 | 'Get-SecretVault' command is run again to verify the vault no longer appears in the registry. 65 | An attempt is made to retrieve 'Secret5', but it is not found since its vault was un-registered. 66 | The vault is re-registered, under a different name, and set to be the default vault. 67 | 'Get-SecretVault' is run again to verify the newly registered vault. 68 | Finally, the 'Secret5' secret is retrieved successfully from the new default vault. 69 | 70 | ### Example 2 71 | ```powershell 72 | PS C:\> Get-SecretVault | Unregister-SecretVault 73 | PS C:\> Get-SecretVault 74 | PS C:\> 75 | ``` 76 | 77 | In this example, 'Get-SecretVault' output is piped to this 'Unregister-SecretVault' cmdlet to un-register all extension vaults for the current user. 78 | Next, 'Get-SecretVault' is run again to show that no vaults are registered. 79 | 80 | ## PARAMETERS 81 | 82 | ### -Name 83 | Name of the vault to un-register. 84 | 85 | ```yaml 86 | Type: String[] 87 | Parameter Sets: NameParameterSet 88 | Aliases: 89 | 90 | Required: True 91 | Position: 0 92 | Default value: None 93 | Accept pipeline input: True (ByValue) 94 | Accept wildcard characters: True 95 | ``` 96 | 97 | ### -SecretVault 98 | SecretVaultInfo object, returned by 'Get-SecretVault' cmdlet. 99 | This can alternately be used to indicate a vault to be un-registered. 100 | 101 | ```yaml 102 | Type: SecretVaultInfo 103 | Parameter Sets: SecretVaultParameterSet 104 | Aliases: 105 | 106 | Required: True 107 | Position: 0 108 | Default value: None 109 | Accept pipeline input: True (ByPropertyName, ByValue) 110 | Accept wildcard characters: False 111 | ``` 112 | 113 | ### -Confirm 114 | Prompts you for confirmation before running the cmdlet. 115 | 116 | ```yaml 117 | Type: SwitchParameter 118 | Parameter Sets: (All) 119 | Aliases: cf 120 | 121 | Required: False 122 | Position: Named 123 | Default value: False 124 | Accept pipeline input: False 125 | Accept wildcard characters: False 126 | ``` 127 | 128 | ### -WhatIf 129 | Shows what would happen if the cmdlet runs. 130 | The cmdlet is not run. 131 | 132 | ```yaml 133 | Type: SwitchParameter 134 | Parameter Sets: (All) 135 | Aliases: wi 136 | 137 | Required: False 138 | Position: Named 139 | Default value: False 140 | Accept pipeline input: False 141 | Accept wildcard characters: False 142 | ``` 143 | 144 | ### CommonParameters 145 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 146 | 147 | ## INPUTS 148 | 149 | ### Microsoft.PowerShell.SecretManagement.SecretVaultInfo 150 | ## OUTPUTS 151 | 152 | ## NOTES 153 | 154 | ## RELATED LINKS 155 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.SecretManagement.format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VaultInfo 5 | 6 | Microsoft.PowerShell.SecretManagement.SecretVaultInfo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Name 26 | 27 | 28 | ModuleName 29 | 30 | 31 | IsDefault 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | SecretInfo 40 | 41 | Microsoft.PowerShell.SecretManagement.SecretInformation 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Name 61 | 62 | 63 | Type 64 | 65 | 66 | VaultName 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/Microsoft.PowerShell.SecretManagement.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | 6 | # Script module or binary module file associated with this manifest. 7 | RootModule = 'Microsoft.PowerShell.SecretManagement.dll' 8 | 9 | # Version number of this module. 10 | ModuleVersion = '{{ModuleVersion}}' 11 | 12 | # Supported PSEditions 13 | CompatiblePSEditions = @('Desktop', 'Core') 14 | 15 | # ID used to uniquely identify this module 16 | GUID = 'a5c858f6-4a8e-41f1-b1ee-0ff8f6ad69d3' 17 | 18 | # Author of this module 19 | Author = 'Microsoft Corporation' 20 | 21 | # Company or vendor of this module 22 | CompanyName = 'Microsoft Corporation' 23 | 24 | # Copyright statement for this module 25 | Copyright = '(c) Microsoft Corporation. All rights reserved.' 26 | 27 | # Description of the functionality provided by this module 28 | Description = " 29 | This module provides a convenient way for a user to store and retrieve secrets. The secrets are 30 | stored in registered extension vaults. An extension vault can store secrets locally or remotely. 31 | SecretManagement coordinates access to the secrets through the registered vaults. 32 | 33 | Go to GitHub for more information about the module and to submit issues: 34 | https://github.com/PowerShell/SecretManagement 35 | " 36 | 37 | # Minimum version of the PowerShell engine required by this module 38 | PowerShellVersion = '5.1' 39 | DotNetFrameworkVersion = '4.6.1' 40 | CLRVersion = '4.0.0' 41 | 42 | # Format files (.ps1xml) to be loaded when importing this module 43 | FormatsToProcess = @('Microsoft.PowerShell.SecretManagement.format.ps1xml') 44 | 45 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 46 | FunctionsToExport = @() 47 | 48 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 49 | CmdletsToExport = @( 50 | 'Register-SecretVault','Unregister-SecretVault','Get-SecretVault','Set-SecretVaultDefault','Test-SecretVault', 51 | 'Set-Secret','Set-SecretInfo','Get-Secret','Get-SecretInfo','Remove-Secret','Unlock-SecretVault') 52 | 53 | # Variables to export from this module 54 | VariablesToExport = '*' 55 | 56 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 57 | AliasesToExport = @() 58 | 59 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 60 | PrivateData = @{ 61 | 62 | PSData = @{ 63 | 64 | # Tags applied to this module. These help with module discovery in online galleries. 65 | # Tags = @() 66 | 67 | # A URL to the license for this module. 68 | LicenseUri = 'https://github.com/PowerShell/SecretManagement/blob/main/LICENSE' 69 | 70 | # A URL to the main website for this project. 71 | ProjectUri = 'https://github.com/PowerShell/SecretManagement' 72 | 73 | # A URL to an icon representing this module. 74 | # IconUri = '' 75 | 76 | # ReleaseNotes of this module 77 | # ReleaseNotes = '' 78 | 79 | # Prerelease string of this module 80 | # Prerelease = '' 81 | 82 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 83 | # RequireLicenseAcceptance = $false 84 | 85 | # External dependent modules of this module 86 | # ExternalModuleDependencies = @() 87 | } # End of PSData hashtable 88 | 89 | } # End of PrivateData hashtable 90 | 91 | # HelpInfo URI of this module 92 | HelpInfoURI = 'https://aka.ms/ps-modules-help' 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/code/Microsoft.PowerShell.SecretManagement.Library.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.PowerShell.SecretManagement.Library 5 | $version$ 6 | Microsoft.PowerShell.SecretManagement.Library 7 | Microsoft 8 | Microsoft,PowerShell 9 | https://github.com/PowerShell/SecretManagement 10 | README.md 11 | images\PowerShell_64.png 12 | MIT 13 | false 14 | Public APIs needed to create SecretManagement extension vault modules. 15 | © Microsoft Corporation. All rights reserved. 16 | PowerShell, SecretManagement, reference 17 | en-US 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/code/Microsoft.PowerShell.SecretManagement.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | true 7 | ./Microsoft.PowerShell.SecretManagement.Library.nuspec 8 | id=$(AssemblyName);version=$(ModuleVersion);artifacts=$(ArtifactsPath) 9 | Library 10 | Microsoft.PowerShell.SecretManagement 11 | Microsoft.PowerShell.SecretManagement 12 | $(ModuleVersion).0 13 | $(ModuleVersion) 14 | $(ModuleVersion) 15 | net462 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/code/images/PowerShell_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/SecretManagement/72678c0a7bab2808b09b5fc0505e75d8924c0b0a/src/code/images/PowerShell_64.png -------------------------------------------------------------------------------- /test/Microsoft.PowerShell.SecretManagement.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | Describe "Test Microsoft.PowerShell.SecretManagement module" { 5 | BeforeDiscovery { 6 | $TestCases = 'ByteArray', 'String', 'SecureString', 'PSCredential', 'Hashtable' 7 | } 8 | 9 | BeforeAll { 10 | $ProjectRoot = Split-Path $PSScriptRoot 11 | $ModulePath = Join-Path $ProjectRoot "module" 12 | $ManifestPath = Join-Path $ModulePath "Microsoft.PowerShell.SecretManagement.psd1" 13 | 14 | $BasePath = Join-Path $TestDrive "SecretManagementStorePath" 15 | New-Item -ItemType Directory -Path $BasePath -Force 16 | 17 | $StorePath = Join-Path $BasePath "StorePath.xml" 18 | $MetaStorePath = Join-Path $BasePath "MetaStorePath.xml" 19 | 20 | # Script extension module 21 | $scriptModuleName = "TVaultScript" 22 | $implementingModuleName = "TVaultScript.Extension" 23 | $scriptModulePath = Join-Path $TestDrive $scriptModuleName 24 | New-Item -ItemType Directory $scriptModulePath -Force 25 | 26 | $scriptModuleFilePath = Join-Path $scriptModulePath "${scriptModuleName}.psd1" 27 | "@{{ 28 | ModuleVersion = '1.0' 29 | NestedModules = @('.\{0}') 30 | FunctionsToExport = @() 31 | }}" -f $implementingModuleName | Out-File -FilePath $scriptModuleFilePath 32 | 33 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-Clixml -Path $StorePath 34 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-Clixml -Path $MetaStorePath 35 | 36 | $scriptImplementationTemplate = @' 37 | $storePath = "{0}" 38 | function SetStore 39 | {{ 40 | param ( 41 | [string] $name, 42 | [object] $value 43 | ) 44 | 45 | $store = Import-CliXml -Path $script:storePath 46 | if ($store.ContainsKey($name)) 47 | {{ 48 | $null = $store.Remove($name) 49 | }} 50 | $null = $store.Add($name, $value) 51 | $store | Export-Clixml -Path $script:storePath 52 | }} 53 | function GetStore 54 | {{ 55 | param ( 56 | [string] $name 57 | ) 58 | 59 | $store = Import-CliXml -Path $script:storePath 60 | return $store[$name] 61 | }} 62 | function RemoveStore 63 | {{ 64 | param ( 65 | [string] $Name 66 | ) 67 | 68 | $store = Import-CliXml -Path $script:storePath 69 | $null = $store.Remove($Name) 70 | $store | Export-CliXml -Path $script:StorePath 71 | }} 72 | 73 | $metaStorePath = "{1}" 74 | function SetMetaStore 75 | {{ 76 | param ( 77 | [string] $name, 78 | [object] $value 79 | ) 80 | 81 | $store = Import-CliXml -Path $script:metaStorePath 82 | if ($store.ContainsKey($name)) 83 | {{ 84 | $null = $store.Remove($name) 85 | }} 86 | $null = $store.Add($name, $value) 87 | $store | Export-Clixml -Path $script:metaStorePath 88 | }} 89 | function GetMetaStore 90 | {{ 91 | param ( 92 | [string] $name 93 | ) 94 | 95 | $store = Import-CliXml -Path $script:metaStorePath 96 | return $store[$name] 97 | }} 98 | function RemoveMetaStore 99 | {{ 100 | param ( 101 | [string] $Name 102 | ) 103 | 104 | $store = Import-CliXml -Path $script:metaStorePath 105 | $null = $store.Remove($Name) 106 | $store | Export-CliXml -Path $script:metaStorePath 107 | }} 108 | 109 | function Get-Secret 110 | {{ 111 | param ( 112 | [string] $Name, 113 | [string] $VaultName, 114 | [hashtable] $AdditionalParameters 115 | ) 116 | 117 | $secret = GetStore $Name 118 | 119 | if ($secret -is [byte[]]) 120 | {{ 121 | return @(,$secret) 122 | }} 123 | 124 | return $secret 125 | }} 126 | 127 | # NOTE: Metadata is supported only through Set-SecretInfo (not Set-Secret) 128 | function Set-Secret 129 | {{ 130 | param ( 131 | [string] $Name, 132 | [object] $Secret, 133 | [string] $VaultName, 134 | [hashtable] $AdditionalParameters 135 | ) 136 | 137 | try {{ 138 | SetStore $Name $Secret 139 | return $true 140 | }} 141 | catch {{ }} 142 | 143 | return $false 144 | }} 145 | 146 | function Set-SecretInfo 147 | {{ 148 | param ( 149 | [string] $Name, 150 | [hashtable] $Metadata, 151 | [string] $VaultName, 152 | [hashtable] $AdditionalParameters 153 | ) 154 | 155 | if ($Metadata["Fail"] -eq $true) {{ 156 | throw [System.Management.Automation.CommandNotFoundException] 157 | }} 158 | 159 | SetMetaStore $Name $Metadata 160 | }} 161 | 162 | function Remove-Secret 163 | {{ 164 | param ( 165 | [string] $Name, 166 | [string] $VaultName, 167 | [hashtable] $AdditionalParameters 168 | ) 169 | 170 | 171 | RemoveStore $Name 172 | RemoveMetaStore $Name 173 | }} 174 | 175 | function Get-SecretInfo 176 | {{ 177 | param ( 178 | [string] $Filter, 179 | [string] $VaultName, 180 | [hashtable] $AdditionalParameters 181 | ) 182 | 183 | if ([string]::IsNullOrEmpty($Filter)) 184 | {{ 185 | $Filter = '*' 186 | }} 187 | $store = Import-CliXml -Path $script:storePath 188 | $metaStore = Import-CliXml -Path $script:metaStorePath 189 | $pattern = [WildcardPattern]::new($Filter) 190 | foreach ($key in $store.Keys) 191 | {{ 192 | if ($pattern.IsMatch($key)) 193 | {{ 194 | $secret = $store[$key] 195 | $type = if ($secret -is [byte[]]) {{ [Microsoft.PowerShell.SecretManagement.SecretType]::ByteArray }} 196 | elseif ($secret -is [string]) {{ [Microsoft.PowerShell.SecretManagement.SecretType]::String }} 197 | elseif ($secret -is [securestring]) {{ [Microsoft.PowerShell.SecretManagement.SecretType]::SecureString }} 198 | elseif ($secret -is [PSCredential]) {{ [Microsoft.PowerShell.SecretManagement.SecretType]::PSCredential }} 199 | elseif ($secret -is [hashtable]) {{ [Microsoft.PowerShell.SecretManagement.SecretType]::Hashtable }} 200 | else {{ [Microsoft.PowerShell.SecretManagement.SecretType]::Unknown }} 201 | 202 | $metadataDict = [System.Collections.Generic.Dictionary[[string],[object]]]::new() 203 | $metadataHashtable = if ($metaStore.ContainsKey($key)) {{ $metaStore[$key] }} else {{ $null }} 204 | if ($metadataHashtable -ne $null) {{ 205 | foreach ($key in $metadataHashtable.Keys) {{ 206 | if (! $metadataDict.ContainsKey($key)) 207 | {{ 208 | $metadataDict.Add($key, $metadataHashtable[$key]) 209 | }} 210 | }} 211 | }} 212 | 213 | Write-Output ([Microsoft.PowerShell.SecretManagement.SecretInformation]::new($key, $type, $VaultName, $metadataDict)) 214 | }} 215 | }} 216 | }} 217 | 218 | function Test-SecretVault 219 | {{ 220 | param ( 221 | [string] $VaultName, 222 | [hashtable] $AdditionalParameters 223 | ) 224 | 225 | $valid = $true 226 | if (! $AdditionalParameters.ContainsKey('AccessId')) 227 | {{ 228 | $valid = $false 229 | Write-Error 'Missing AccessId parameter' 230 | }} 231 | if (! $AdditionalParameters.ContainsKey('SubscriptionId')) 232 | {{ 233 | $valid = $false 234 | Write-Error 'Missing SubscriptionId parameter' 235 | }} 236 | 237 | # Used for data stream redirection test. 238 | Write-Warning 'Test-SecretVault: Bogus Warning' 239 | Write-Information 'Test-SecretVault: Bogus Information' 240 | 241 | return $valid 242 | }} 243 | 244 | function Unregister-SecretVault 245 | {{ 246 | param ( 247 | [string] $VaultName, 248 | [hashtable] $AdditionalParameters 249 | ) 250 | 251 | SetStore "UnRegisterSecretVaultCalled" $true 252 | }} 253 | 254 | function Unlock-SecretVault 255 | {{ 256 | param ( 257 | [string] $Name, 258 | [SecureString] $Password, 259 | [string] $VaultName, 260 | [hashtable] $AdditionalParameters 261 | ) 262 | 263 | try {{ 264 | SetStore 'UnlockState' '0x11580' 265 | }} 266 | catch 267 | {{ 268 | Write-Verbose -Verbose 'Unlock-SecretVault: SetStore failed.' 269 | }} 270 | }} 271 | '@ 272 | 273 | $scriptImplementation = $scriptImplementationTemplate -f $StorePath, $MetaStorePath 274 | 275 | $implementingModulePath = Join-Path $scriptModulePath $implementingModuleName 276 | New-Item -ItemType Directory $implementingModulePath -Force 277 | $implementingManifestFilePath = Join-Path $implementingModulePath "${implementingModuleName}.psd1" 278 | $manifestInfo = " 279 | @{{ 280 | ModuleVersion = '1.0' 281 | RootModule = '{0}' 282 | FunctionsToExport = @('Set-Secret','Set-SecretInfo','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault','Unregister-SecretVault','Unlock-SecretVault') 283 | }} 284 | " -f $implementingModuleName 285 | $manifestInfo | Out-File -FilePath $implementingManifestFilePath 286 | $implementingModuleFilePath = Join-Path $implementingModulePath "${implementingModuleName}.psm1" 287 | $scriptImplementation | Out-File -FilePath $implementingModuleFilePath 288 | 289 | Import-Module -Force -Name $ManifestPath 290 | 291 | $PreviousSecretVaults = Get-SecretVault 292 | $PreviousSecretVaults | Unregister-SecretVault 293 | 294 | $StoreTypes = @{ 295 | ByteArray = @{ 296 | Kind = 'ByteArray' 297 | Title = 'script' 298 | Vault = 'ScriptTestVault' 299 | Name = 'BinVaultBlob' 300 | Value = [System.Text.Encoding]::UTF8.GetBytes('BinVaultHelloStr') 301 | Stringifier = { 302 | param([byte[]] $Blob) 303 | 304 | $sb = [System.Text.StringBuilder]::new() 305 | $null = & { 306 | $sb = $sb 307 | foreach ($byte in $Blob) { 308 | $sb.AppendFormat('{0:X2}', $byte) 309 | } 310 | } 311 | 312 | return $sb.ToString() 313 | } 314 | } 315 | String = @{ 316 | Kind = 'String' 317 | Title = 'script' 318 | Vault = 'ScriptTestVault' 319 | Name = 'BinVaultStr' 320 | Value = 'HelloBinVault' 321 | Stringifier = { 322 | if ($args[0] -is [securestring]) { 323 | return & $StoreTypes['SecureString']['Stringifier'] $args[0] 324 | } 325 | 326 | return $args[0] 327 | } 328 | } 329 | SecureString = @{ 330 | Kind = 'SecureString' 331 | Title = 'script' 332 | Vault = 'ScriptTestVault' 333 | Name = 'BinVaultSecureStr' 334 | Value = ConvertTo-SecureString ([System.IO.Path]::GetRandomFileName()) -AsPlainText -Force 335 | Stringifier = { 336 | $ptr = [IntPtr]::Zero 337 | try { 338 | $ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($args[0]) 339 | $value = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr) 340 | return $value 341 | } finally { 342 | [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr) 343 | } 344 | } 345 | } 346 | PSCredential = @{ 347 | Kind = 'PSCredential' 348 | Title = 'script' 349 | Vault = 'ScriptTestVault' 350 | Name = 'BinVaultCred' 351 | Value = [pscredential]::new('UserName', (ConvertTo-SecureString ([System.IO.Path]::GetRandomFileName()) -AsPlainText -Force)) 352 | Stringifier = { 353 | $networkCred = ([pscredential]$args[0]).GetNetworkCredential() 354 | return "u:$($networkCred.UserName)p:$($networkCred.Password)" 355 | } 356 | } 357 | Hashtable = @{ 358 | Kind = 'Hashtable' 359 | Title = 'script' 360 | Vault = 'ScriptTestVault' 361 | Name = 'BinVaultStr' 362 | Value = @{ 363 | Blob = ([byte[]] @(1,2)) 364 | Str = "Hello" 365 | SecureString = (ConvertTo-SecureString ([System.IO.Path]::GetRandomFileName()) -AsPlainText -Force) 366 | Cred = ([pscredential]::New("UserA", (ConvertTo-SecureString ([System.IO.Path]::GetRandomFileName()) -AsPlainText -Force))) 367 | } 368 | Stringifier = { 369 | param([hashtable] $ht) 370 | end { 371 | $sb = [System.Text.StringBuilder]::new('{') 372 | $null = & { 373 | $sb = $sb 374 | $first = $true 375 | foreach ($entry in $ht.GetEnumerator() | Sort-Object Key) { 376 | if ($first) { 377 | $first = $false 378 | } else { 379 | $sb.Append('|') 380 | } 381 | $sb.Append($entry.Key).Append(':') 382 | if ($entry.Value -is [hashtable]) { 383 | $sb.Append((& $StoreTypes['Hashtable']['Stringifier'] $entry.Value)) 384 | continue 385 | } 386 | 387 | if ($entry.Value -is [securestring]) { 388 | $sb.Append((& $StoreTypes['SecureString']['Stringifier'] $entry.Value)) 389 | continue 390 | } 391 | 392 | if ($entry.Value -is [byte[]] -or $entry.Value -is [object[]]) { 393 | $sb.Append((& $StoreTypes['ByteArray']['Stringifier'] $entry.Value)) 394 | continue 395 | } 396 | 397 | if ($entry.Value -is [pscredential]) { 398 | $sb.Append((& $StoreTypes['PSCredential']['Stringifier'] $entry.Value)) 399 | continue 400 | } 401 | 402 | $sb.Append([string]$entry.Value) 403 | } 404 | 405 | $sb.Append('}') 406 | } 407 | 408 | return $sb.ToString() 409 | } 410 | } 411 | } 412 | } 413 | } 414 | 415 | AfterAll { 416 | Unregister-SecretVault -Name ScriptTestVault -ErrorAction Ignore 417 | foreach ($vault in $PreviousSecretVaults) { 418 | $params = @{ 419 | ModuleName = $vault.ModuleName 420 | DefaultVault = $vault.IsDefault 421 | } 422 | 423 | if ($vault.VaultParameters -and $vault.VaultParameters.Count -gt 0) { 424 | $params['VaultParameters'] = $vault.VaultParameters 425 | } 426 | 427 | Register-SecretVault @params 428 | } 429 | 430 | Remove-Module -Name TVaultScript -Force -ErrorAction Ignore 431 | Remove-Module -Name Microsoft.PowerShell.SecretManagement -Force -ErrorAction Ignore 432 | } 433 | 434 | Context "Script extension vault <_> type tests" -ForEach $TestCases { 435 | BeforeAll { 436 | $SecretTestInfo = $StoreTypes[$_] 437 | 438 | Register-SecretVault -Name ScriptTestVault -ModuleName $scriptModuleFilePath 439 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-Clixml -Path $StorePath 440 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-CliXml -Path $MetaStorePath 441 | } 442 | 443 | AfterAll { 444 | Get-SecretVault ScriptTestVault | Unregister-SecretVault 445 | } 446 | 447 | It "Verifies writing <_> type to script vault" { 448 | Set-Secret -Name $SecretTestInfo['Name'] -Secret $SecretTestInfo['Value'] -Vault $SecretTestInfo['Vault'] -ErrorAction Stop 449 | } 450 | 451 | It "Verifies reading <_> type from script vault" { 452 | $result = Get-Secret -Name $SecretTestInfo['Name'] -Vault $SecretTestInfo['Vault'] -ErrorAction Stop 453 | $secretString = & $SecretTestInfo['Stringifier'] $SecretTestInfo['Value'] 454 | $resultString = & $SecretTestInfo['Stringifier'] $result 455 | $resultString | Should -BeExactly $secretString 456 | } 457 | 458 | It "Verifies enumerating <_> type from script vault" { 459 | $blobInfo = Get-SecretInfo -Name $SecretTestInfo['Name'] -Vault $SecretTestInfo['Vault'] -ErrorAction Stop 460 | $blobInfo.Name | Should -BeExactly $SecretTestInfo['Name'] 461 | $blobInfo.Type | Should -BeExactly $SecretTestInfo['Kind'] 462 | $blobInfo.VaultName | Should -BeExactly $SecretTestInfo['Vault'] 463 | } 464 | 465 | It "Verifies removing <_> type from script vault" { 466 | Remove-Secret -Name $SecretTestInfo['Name'] -Vault $SecretTestInfo['Vault'] -ErrorAction Stop 467 | { Get-Secret -Name $SecretTestInfo['Name'] -Vault $SecretTestInfo['Vault'] -ErrorAction Stop } | 468 | Should -Throw -ErrorId 'GetSecretNotFound,Microsoft.PowerShell.SecretManagement.GetSecretCommand' 469 | } 470 | } 471 | 472 | Context "API Tests" { 473 | It "Verifies the SecretInformation constructor" { 474 | $metadata = @{ Name='Name1'; Target='Target1' } 475 | $secretInfo = [Microsoft.PowerShell.SecretManagement.SecretInformation]::new( 476 | 'MyName', 477 | [Microsoft.PowerShell.SecretManagement.SecretType]::String, 478 | 'MyVault', 479 | $metadata) 480 | 481 | $secretInfo.Name | Should -BeExactly 'MyName' 482 | $secretInfo.Type | Should -BeExactly 'String' 483 | $secretInfo.VaultName | Should -BeExactly 'MyVault' 484 | $secretInfo.Metadata['Name'] | Should -BeExactly 'Name1' 485 | $secretInfo.Metadata['Target'] | Should -BeExactly 'Target1' 486 | } 487 | } 488 | 489 | Context "Script extension (non-default) vault tests" { 490 | 491 | BeforeAll { 492 | $randomSecretD = [System.IO.Path]::GetRandomFileName() 493 | } 494 | 495 | It "Verifies reserved 'Verbose' keyword in VaultParameters throws expected error" { 496 | { Register-SecretVault -Name ScriptTestVault -ModuleName $scriptModuleFilePath -VaultParameters @{ Verbose = $true } -ErrorAction Stop } | 497 | Should -Throw -ErrorId 'RegisterSecretVaultCommandCannotUseReservedName,Microsoft.PowerShell.SecretManagement.RegisterSecretVaultCommand' 498 | } 499 | 500 | It "Should register the script vault extension successfully but with invalid parameters" { 501 | $additionalParameters = @{ Hello = "There" } 502 | Register-SecretVault -Name ScriptTestVault -ModuleName $scriptModuleFilePath -VaultParameters $additionalParameters -ErrorAction Stop 503 | } 504 | 505 | It "Verifies Test-SecretVault fails with errors" { 506 | { Test-SecretVault -Name ScriptTestVault -ErrorAction Stop } | 507 | Should -Throw -ErrorId 'Microsoft.PowerShell.Commands.WriteErrorException,Microsoft.PowerShell.SecretManagement.TestSecretVaultCommand' 508 | } 509 | 510 | It "Verifies the only script vault extension is designated as the default vault" { 511 | $vaultInfo = Get-SecretVault -Name ScriptTestVault 512 | $vaultInfo.IsDefault | Should -BeTrue 513 | } 514 | 515 | It "Verifies that a secret item added with default vault designated results in no error" { 516 | Set-Secret -Name TestDefaultItem -Secret $randomSecretD -ErrorAction Stop 517 | } 518 | 519 | It "Should successfully unregister script vault extension" { 520 | Unregister-SecretVault -Name ScriptTestVault -ErrorAction Stop 521 | } 522 | 523 | It "Should register the script vault extension successfully" { 524 | $additionalParameters = @{ AccessId = "AccessAT"; SubscriptionId = "1234567890" } 525 | Register-SecretVault -Name ScriptTestVault -ModuleName $scriptModuleFilePath -VaultParameters $additionalParameters ` 526 | -Description 'ScriptTestVaultDescription' -ErrorAction Stop 527 | } 528 | 529 | It "Verifies description field for registered test vault" { 530 | (Get-SecretVault -Name ScriptTestVault).Description | Should -BeExactly 'ScriptTestVaultDescription' 531 | } 532 | 533 | It "Should throw error when registering existing registered vault extension" { 534 | $additionalParameters = @{ AccessId = "AccessAT"; SubscriptionId = "1234567890" } 535 | { Register-SecretVault -Name ScriptTestVault -ModuleName $scriptModuleFilePath -VaultParameters $additionalParameters } | 536 | Should -Throw -ErrorId 'RegisterSecretVaultInvalidVaultName,Microsoft.PowerShell.SecretManagement.RegisterSecretVaultCommand' 537 | } 538 | 539 | It "Verifies Test-SecretVault succeeds" { 540 | Test-SecretVault -Name ScriptTestVault | Should -BeTrue 541 | } 542 | 543 | It "Verifes Test-SecretVault extension vault data streams can be redirected" { 544 | $results = Test-SecretVault -Name ScriptTestVault 3>&1 6>&1 545 | $results[0] | Should -BeExactly 'Test-SecretVault: Bogus Warning' 546 | $results[1] | Should -BeExactly 'Test-SecretVault: Bogus Information' 547 | $results[2] | Should -BeTrue 548 | } 549 | 550 | # Metadata is set through extension vault 'Set-SecretInfo' command, and not via a Metadata 551 | # parameter on 'Set-Secret' command. 552 | It "Verifies Set-Secret with metadata succeeds" { 553 | Set-Secret -Name TestDefaultMeta -Secret $randomSecretD -Metadata @{ Fail = $false } -ErrorAction Stop 554 | $info = Get-SecretInfo -Name TestDefaultMeta 555 | $info.Metadata | Should -Not -BeNullOrEmpty 556 | $info.Metadata["Fail"] | Should -BeFalse 557 | } 558 | 559 | It "Verifes Set-SecretInfo function" { 560 | Set-SecretInfo -Name TestDefaultMeta -Metadata @{ Fail = $false; Data = "MyData" } -ErrorAction Stop 561 | $info = Get-SecretInfo -Name TestDefaultMeta 562 | $info.Metadata | Should -Not -BeNullOrEmpty 563 | $info.Metadata["Data"] | Should -BeExactly "MyData" 564 | } 565 | 566 | It "Verifies unsupported Set-SecretInfo fails with error" { 567 | { Set-SecretInfo -Name TestDefaultMeta -Metadata @{ Fail = $true } -ErrorAction Stop } | 568 | Should -Throw -ErrorId 'SetSecretMetadataInvalidOperation,Microsoft.PowerShell.SecretManagement.SetSecretInfoCommand' 569 | } 570 | 571 | It "Verifies Unlock-SecretVault command" { 572 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-Clixml -Path $StorePath 573 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-CliXml -Path $MetaStorePath 574 | 575 | Unlock-SecretVault -Name ScriptTestVault -Password (ConvertTo-SecureString -String $randomSecretD -AsPlainText -Force) -ErrorAction Stop 576 | 577 | # Verify vault 'Unlock-SecretVault' function was called. 578 | $dict = Import-Clixml -Path $StorePath 579 | $dict['UnlockState'] | Should -BeExactly '0x11580' 580 | } 581 | } 582 | 583 | Context "Set-SecretVaultDefault cmdlet tests" { 584 | 585 | BeforeAll { 586 | $randomSecretE = [System.IO.Path]::GetRandomFileName() 587 | } 588 | 589 | It "Should throw error when setting non existent vault as default" { 590 | { Set-SecretVaultDefault -Name NoSuchVault } | 591 | Should -Throw -ErrorId 'VaultNotFound,Microsoft.PowerShell.SecretManagement.SetSecretVaultDefaultCommand' 592 | } 593 | 594 | It "Verifies cmdlet successfully sets default vault" { 595 | Set-SecretVaultDefault -Name ScriptTestVault 596 | (Get-SecretVault -Name ScriptTestVault).IsDefault | Should -BeTrue 597 | } 598 | 599 | It "Verifies cmdlet successfully clears default vault" { 600 | Set-SecretVaultDefault -ClearDefault 601 | (Get-SecretVault -Name ScriptTestVault).IsDefault | Should -BeFalse 602 | } 603 | 604 | It "Verifies setting default vault works as default" { 605 | Set-SecretVaultDefault -Name ScriptTestVault 606 | (Get-SecretVault -Name ScriptTestVault).IsDefault | Should -BeTrue 607 | Set-Secret -Name GoesToDefaultVault -Secret $randomSecretE 608 | Get-Secret -Name GoesToDefaultVault -Vault ScriptTestVault -AsPlainText | Should -BeExactly $randomSecretE 609 | } 610 | } 611 | 612 | Context "Unregister-SecretVault cmdlet tests" { 613 | 614 | It "Verifies unregister operation calls the extension 'Unregister-SecretVault' function before unregistering" { 615 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-Clixml -Path $StorePath 616 | [System.Collections.Generic.Dictionary[[string],[object]]]::new() | Export-Clixml -Path $MetaStorePath 617 | 618 | Unregister-SecretVault -Name ScriptTestVault -ErrorAction Stop 619 | 620 | $store = Import-Clixml -Path $StorePath 621 | $store['UnRegisterSecretVaultCalled'] | Should -BeTrue 622 | 623 | <# 624 | # Restore the extension module registration. 625 | $additionalParameters = @{ AccessId = "AccessAT"; SubscriptionId = "1234567890" } 626 | { Register-SecretVault -Name ScriptTestVault -ModuleName $script:scriptModuleFilePath -VaultParameters $additionalParameters -ErrorVariable err } | Should -Not -Throw 627 | $err.Count | Should -Be 0 628 | #> 629 | } 630 | } 631 | } 632 | -------------------------------------------------------------------------------- /tools/installPSResources.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | param( 4 | [ValidateSet("PSGallery", "CFS")] 5 | [string]$PSRepository = "PSGallery" 6 | ) 7 | 8 | if ($PSRepository -eq "CFS" -and -not (Get-PSResourceRepository -Name CFS -ErrorAction SilentlyContinue)) { 9 | Register-PSResourceRepository -Name CFS -Uri "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/PowerShellGalleryMirror/nuget/v3/index.json" 10 | } 11 | 12 | # NOTE: Due to a bug in Install-PSResource with upstream feeds, we have to 13 | # request an exact version. Otherwise, if a newer version is available in the 14 | # upstream feed, it will fail to install any version at all. 15 | Install-PSResource -Verbose -TrustRepository -RequiredResource @{ 16 | InvokeBuild = @{ 17 | version = "5.12.1" 18 | repository = $PSRepository 19 | } 20 | platyPS = @{ 21 | version = "0.14.2" 22 | repository = $PSRepository 23 | } 24 | Pester = @{ 25 | version = "5.7.1" 26 | repository = $PSRepository 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tools/updateVersion.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | param( 5 | [Parameter(Mandatory)] 6 | [semver]$Version, 7 | 8 | [Parameter(Mandatory)] 9 | [string]$Changes 10 | ) 11 | 12 | git diff --staged --quiet --exit-code 13 | if ($LASTEXITCODE -ne 0) { 14 | throw "There are staged changes in the repository. Please commit or reset them before running this script." 15 | } 16 | 17 | $Path = "Directory.Build.props" 18 | $f = Get-Content -Path $Path 19 | $f = $f -replace '^(?\s+)(.+)(?)$', "`${prefix}${Version}`${suffix}" 20 | $f | Set-Content -Path $Path 21 | git add $Path 22 | 23 | git commit --edit --message "v${Version}: $Changes" 24 | --------------------------------------------------------------------------------