This repository has been archived by the owner on Dec 8, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add SecretsManagement vault extension samples (#21)
- Loading branch information
Showing
9 changed files
with
448 additions
and
0 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
Samples/VaultExtension/Modules/CodeLocalVault/CodeLocalVault.psd1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@{ | ||
ModuleVersion = '1.0' | ||
RequiredAssemblies = @('CodeLocalVault') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# CodeLocalVault | ||
|
||
This is a simple binary module vault extension example. | ||
It contains a PowerShell module manifest (CodeLocalVault.psd1) and a C# source file and dotNet csproj file needed to build the binary assembly. | ||
This module uses Export-CliXml/Import-CliXml cmdlets to store and retrieve secret objects to a file. | ||
|
||
This is **not** a secure storage, and is only intended as a vault extension example. | ||
|
||
You can experiment with this vault extension example by first building a CodeLocalVault.dll binary assembly, and then copying this module to your local machine under a file path that PowerShell uses to discover modules (e.g., within a $env:PSModulePath path). | ||
|
||
You can then register this as a Secrets Management local vault extension with the following command: | ||
|
||
```powershell | ||
Register-SecretsVault -Name CodeLocalVault -ModuleName CodeLocalVault | ||
Get-SecretsVault CodeLocalVault | ||
Name ModuleName ImplementingType | ||
---- ---------- ---------------- | ||
CodeLocalVault CodeLocalVault CodeLocalVault.ImplementingClass | ||
``` |
270 changes: 270 additions & 0 deletions
270
Samples/VaultExtension/Modules/CodeLocalVault/build/CodeLocalVault.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using Microsoft.PowerShell.SecretsManagement; | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.Management.Automation; | ||
using System.Management.Automation.Runspaces; | ||
using System.Security; | ||
|
||
namespace CodeLocalVault | ||
{ | ||
#region ImplementingClass | ||
|
||
public class ImplementingClass : SecretsManagementExtension | ||
{ | ||
#region Constructors | ||
|
||
private ImplementingClass() : base(string.Empty) | ||
{ } | ||
|
||
public ImplementingClass(string vaultName) : base(vaultName) | ||
{ } | ||
|
||
#endregion | ||
|
||
#region Abstract implementations | ||
|
||
public override bool SetSecret( | ||
string name, | ||
object secret, | ||
IReadOnlyDictionary<string, object> parameters, | ||
out Exception error) | ||
{ | ||
var results = PowerShellInvoker.InvokeCommand( | ||
command: "Set-Secret", | ||
args: new object[] { name, secret }, | ||
dataStreams: out PSDataStreams dataStreams); | ||
|
||
if (dataStreams.Error.Count > 0) | ||
{ | ||
error = dataStreams.Error[0].Exception; | ||
return false; | ||
} | ||
|
||
error = null; | ||
return true; | ||
} | ||
|
||
public override object GetSecret( | ||
string name, | ||
IReadOnlyDictionary<string, object> parameters, | ||
out Exception error) | ||
{ | ||
var result = PowerShellInvoker.InvokeCommand( | ||
command: "Get-Secret", | ||
args: new object[] { name }, | ||
dataStreams: out PSDataStreams dataStreams); | ||
|
||
error = dataStreams.Error.Count > 0 ? dataStreams.Error[0].Exception : null; | ||
|
||
return result.Count > 0 ? result[0] : null; | ||
} | ||
|
||
public override bool RemoveSecret( | ||
string name, | ||
IReadOnlyDictionary<string, object> parameters, | ||
out Exception error) | ||
{ | ||
PowerShellInvoker.InvokeCommand( | ||
command: "Remove-Secret", | ||
args: new object[] { name }, | ||
dataStreams: out PSDataStreams dataStreams); | ||
|
||
if (dataStreams.Error.Count > 0) | ||
{ | ||
error = dataStreams.Error[0].Exception; | ||
return false; | ||
} | ||
|
||
error = null; | ||
return true; | ||
} | ||
|
||
public override KeyValuePair<string, string>[] GetSecretInfo( | ||
string filter, | ||
IReadOnlyDictionary<string, object> parameters, | ||
out Exception error) | ||
{ | ||
var results = PowerShellInvoker.InvokeCommand( | ||
command: "Enumerate-Secrets", | ||
args: new object[] { filter }, | ||
dataStreams: out PSDataStreams dataStreams); | ||
|
||
error = dataStreams.Error.Count > 0 ? dataStreams.Error[0].Exception : null; | ||
|
||
var list = new List<KeyValuePair<string, string>>(results.Count); | ||
foreach (dynamic result in results) | ||
{ | ||
string typeName; | ||
var resultValue = (result.Value is PSObject) ? ((PSObject)result.Value).BaseObject : result.Value; | ||
switch (resultValue) | ||
{ | ||
case byte[] blob: | ||
typeName = nameof(SupportedTypes.ByteArray); | ||
break; | ||
|
||
case string str: | ||
typeName = nameof(SupportedTypes.String); | ||
break; | ||
|
||
case SecureString sstr: | ||
typeName = nameof(SupportedTypes.SecureString); | ||
break; | ||
|
||
case PSCredential cred: | ||
typeName = nameof(SupportedTypes.PSCredential); | ||
break; | ||
|
||
case Hashtable ht: | ||
typeName = nameof(SupportedTypes.Hashtable); | ||
break; | ||
|
||
default: | ||
typeName = nameof(SupportedTypes.Unknown); | ||
break; | ||
} | ||
|
||
list.Add( | ||
new KeyValuePair<string, string>( | ||
key: result.Name, | ||
value: typeName)); | ||
} | ||
|
||
return list.ToArray(); | ||
} | ||
|
||
#endregion | ||
} | ||
|
||
#endregion | ||
|
||
#region PowerShellInvoker | ||
|
||
internal static class PowerShellInvoker | ||
{ | ||
#region Members | ||
|
||
private const string FunctionsDefScript = @" | ||
function Get-Path | ||
{ | ||
$path = Join-Path $env:TEMP 'TVECode' | ||
if (! (Test-Path -Path $path)) | ||
{ | ||
[System.IO.Directory]::CreateDirectory($path) | ||
} | ||
return $path | ||
} | ||
function Get-Secret | ||
{ | ||
param( | ||
[Parameter(Mandatory=$true)] | ||
[ValidateNotNullOrEmpty()] | ||
[string] $Name | ||
) | ||
if ([WildcardPattern]::ContainsWildcardCharacters($Name)) | ||
{ | ||
throw ""The Name parameter cannot contain any wild card characters."" | ||
} | ||
$filePath = Join-Path -Path (Get-Path) -ChildPath ""${Name}.xml"" | ||
if (! (Test-Path -Path $filePath)) | ||
{ | ||
return | ||
} | ||
Import-CliXml -Path $filePath | ||
} | ||
function Enumerate-Secrets | ||
{ | ||
param( | ||
[string] $Name | ||
) | ||
if ([string]::IsNullOrEmpty($Name)) { $Name = '*' } | ||
$files = dir(Join-Path -Path (Get-Path) -ChildPath ""${Name}.xml"") 2>$null | ||
foreach ($file in $files) | ||
{ | ||
$secretName = [System.IO.Path]::GetFileNameWithoutExtension((Split-Path $file -Leaf)) | ||
$secret = Import-Clixml -Path $file.FullName | ||
Write-Output([pscustomobject] @{ | ||
Name = $secretName | ||
Value = $secret | ||
}) | ||
} | ||
} | ||
function Set-Secret | ||
{ | ||
param( | ||
[Parameter(Mandatory=$true)] | ||
[ValidateNotNullOrEmpty()] | ||
[string] $Name, | ||
[Parameter(Mandatory=$true)] | ||
[ValidateNotNull()] | ||
[object] $Secret | ||
) | ||
$filePath = Join-Path -Path (Get-Path) ""${Name}.xml"" | ||
if (Test-Path -Path $filePath) | ||
{ | ||
Write-Error ""Secret name, $Name, is already used in this vault."" | ||
return | ||
} | ||
$Secret | Export-Clixml -Path $filePath | ||
} | ||
function Remove-Secret | ||
{ | ||
param( | ||
[string] $Name | ||
) | ||
$filePath = Join-Path -Path (Get-Path) ""${Name}.xml"" | ||
if (! (Test-Path -Path $filePath)) | ||
{ | ||
Write-Error ""The secret $Name does not exist"" | ||
return | ||
} | ||
Remove-Item -Path $filePath | ||
} | ||
"; | ||
|
||
#endregion | ||
|
||
#region Methods | ||
|
||
public static Collection<PSObject> InvokeCommand( | ||
string command, | ||
object[] args, | ||
out PSDataStreams dataStreams) | ||
{ | ||
using (var powerShell = System.Management.Automation.PowerShell.Create()) | ||
{ | ||
powerShell.AddScript(FunctionsDefScript).Invoke(); | ||
powerShell.Commands.Clear(); | ||
|
||
var results = powerShell.AddCommand(command).AddParameters(args).Invoke(); | ||
dataStreams = powerShell.Streams; | ||
return results; | ||
} | ||
} | ||
} | ||
|
||
#endregion | ||
|
||
#endregion | ||
} |
16 changes: 16 additions & 0 deletions
16
Samples/VaultExtension/Modules/CodeLocalVault/build/CodeLocalVault.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.1</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Reference Include="Microsoft.PowerShell.SecretsManagement"> | ||
<HintPath>.\Modules\Microsoft.PowerShell.SecretsManagement\Microsoft.PowerShell.SecretsManagement.dll</HintPath> | ||
</Reference> | ||
<Reference Include="System.Management.Automation"> | ||
<HintPath>C:\Program Files\PowerShell\6\System.Management.Automation.dll</HintPath> | ||
</Reference> | ||
</ItemGroup> | ||
|
||
</Project> |
5 changes: 5 additions & 0 deletions
5
Samples/VaultExtension/Modules/ScriptLocalVault/ImplementingModule/ImplementingModule.psd1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@{ | ||
ModuleVersion = '1.0' | ||
RootModule = '.\ImplementingModule.psm1' | ||
FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo') | ||
} |
Oops, something went wrong.