Refactor secret management providers and introduce unified handling

- Removed KeePass and SecretStore provider implementations.
- Integrated KeePass and SecretStore as vaults under the SecretManagement provider.
- Added new functions: Get-DSCConfigurationDataCredentialProvider and Set-DSCConfigurationDataCredentialProvider for managing credential providers.
- Implemented Unlock-ConfigurationDataSecretManagementVault to handle vault unlocking with master passwords.
- Updated README to reflect changes in provider usage and examples.
- Enhanced error handling and validation for vault registration and settings.
This commit is contained in:
Torsten Brendgen
2026-07-06 22:50:39 +02:00
parent 3fa52d7ada
commit 45e71c093c
12 changed files with 608 additions and 550 deletions

View File

@@ -0,0 +1,49 @@
function Unlock-ConfigurationDataSecretManagementVault {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[hashtable]
$ProviderSettings = @{}
)
if(-not $ProviderSettings.ContainsKey("SecretManagement")){
return
}
$SecretManagementSettings = $ProviderSettings.SecretManagement
if(-not ($SecretManagementSettings -is [System.Collections.IDictionary])){
return
}
if(-not (Test-ConfigurationDataMapContainsKey -Map $SecretManagementSettings -Key "MasterPassword")){
return
}
$Vault = [string](Get-ConfigurationDataMapValue -Map $SecretManagementSettings -Key "DefaultVault" -DefaultValue "")
if([string]::IsNullOrWhiteSpace($Vault)){
throw "SecretManagement provider settings define [MasterPassword], but [DefaultVault] is not defined."
}
$UnlockSecretVaultCommand = Get-Command -Name Unlock-SecretVault -ErrorAction SilentlyContinue
if($null -eq $UnlockSecretVaultCommand){
throw "Command [Unlock-SecretVault] was not found. Install module [Microsoft.PowerShell.SecretManagement]."
}
$MasterPassword = Resolve-ConfigurationDataProviderSecureString -Value $SecretManagementSettings.MasterPassword
& $UnlockSecretVaultCommand -Name $Vault -Password $MasterPassword
$TestSecretVaultCommand = Get-Command -Name Test-SecretVault -ErrorAction SilentlyContinue
if($null -ne $TestSecretVaultCommand){
$IsUnlocked = $false
try {
$IsUnlocked = [bool](& $TestSecretVaultCommand -Name $Vault -ErrorAction Stop)
}
catch {
throw "SecretManagement vault [$Vault] could not be unlocked. $($_.Exception.Message)"
}
if(-not $IsUnlocked){
throw "SecretManagement vault [$Vault] could not be unlocked. Verify the KeePass database path, KeePass key file, and protected master password in the provider settings."
}
}
}

View File

@@ -1,94 +0,0 @@
$AzureKeyVaultProvider = @{
Name = "AzureKeyVault"
SupportedTypes = @(
"credential",
"securestring",
"string"
)
Resolver = {
Param(
[Parameter(Mandatory=$true)]
[System.Collections.IDictionary]
$Reference,
[Parameter(Mandatory=$true)]
[ValidateSet("credential", "securestring", "string")]
[string]
$ExpectedType,
[Parameter(Mandatory=$false)]
[hashtable]
$ProviderSettings = @{}
)
$GetSecretCommand = Get-Command -Name Get-AzKeyVaultSecret -ErrorAction SilentlyContinue
if($null -eq $GetSecretCommand){
throw "AzureKeyVault provider requires the module [Az.KeyVault] and command [Get-AzKeyVaultSecret]."
}
$Vault = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "Vault" -DefaultValue "")
$Name = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "Name")
$UserName = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "UserName" -DefaultValue "")
$Options = Get-ConfigurationDataMapValue -Map $Reference -Key "Options" -DefaultValue @{}
if([string]::IsNullOrWhiteSpace($Vault) -and $ProviderSettings.ContainsKey("DefaultVault")){
$Vault = [string]$ProviderSettings.DefaultVault
}
if([string]::IsNullOrWhiteSpace($Vault)){
throw "AzureKeyVault secret [$Name] requires [Vault] or provider setting [DefaultVault]."
}
if($ProviderSettings.ContainsKey("SubscriptionId") -or $ProviderSettings.ContainsKey("TenantId")){
$SetContextCommand = Get-Command -Name Set-AzContext -ErrorAction SilentlyContinue
if($null -eq $SetContextCommand){
throw "AzureKeyVault provider settings define Azure context values, but command [Set-AzContext] was not found. Install [Az.Accounts] or remove SubscriptionId/TenantId from provider settings."
}
$ContextParameters = @{}
if($ProviderSettings.ContainsKey("SubscriptionId") -and -not [string]::IsNullOrWhiteSpace([string]$ProviderSettings.SubscriptionId)){
$ContextParameters["SubscriptionId"] = [string]$ProviderSettings.SubscriptionId
}
if($ProviderSettings.ContainsKey("TenantId") -and -not [string]::IsNullOrWhiteSpace([string]$ProviderSettings.TenantId)){
$ContextParameters["Tenant"] = [string]$ProviderSettings.TenantId
}
if($ContextParameters.Count -gt 0){
& $SetContextCommand @ContextParameters | Out-Null
}
}
$CommandParameters = @{
VaultName = $Vault
Name = $Name
}
if($Options -is [System.Collections.IDictionary]){
foreach($Key in $Options.Keys){
$CommandParameters[$Key] = $Options[$Key]
}
}
if($ExpectedType -eq "string" -and $GetSecretCommand.Parameters.ContainsKey("AsPlainText") -and -not $CommandParameters.ContainsKey("AsPlainText")){
$CommandParameters["AsPlainText"] = $true
}
$Secret = & $GetSecretCommand @CommandParameters
if($null -eq $Secret){
throw "Azure Key Vault secret [$Name] was not found in vault [$Vault]."
}
if($ExpectedType -eq "string" -and $Secret -is [string]){
return $Secret
}
if($Secret.PSObject.Properties["SecretValue"]){
return ConvertFrom-ConfigurationDataSecretValue -Secret $Secret.SecretValue -ExpectedType $ExpectedType -Name $Name -UserName $UserName
}
return ConvertFrom-ConfigurationDataSecretValue -Secret $Secret -ExpectedType $ExpectedType -Name $Name -UserName $UserName
}
}
Register-ConfigurationDataSecretProvider @AzureKeyVaultProvider

View File

@@ -1,88 +0,0 @@
$KeePassProvider = @{
Name = "KeePass"
SupportedTypes = @(
"credential",
"securestring",
"string"
)
Resolver = {
Param(
[Parameter(Mandatory=$true)]
[System.Collections.IDictionary]
$Reference,
[Parameter(Mandatory=$true)]
[ValidateSet("credential", "securestring", "string")]
[string]
$ExpectedType,
[Parameter(Mandatory=$false)]
[hashtable]
$ProviderSettings = @{}
)
$KeePassCommand = Get-Command -Name Get-KeePassEntry -ErrorAction SilentlyContinue
if($null -eq $KeePassCommand){
throw "KeePass provider requires the [PoShKeePass] module command [Get-KeePassEntry]."
}
$Vault = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "Vault" -DefaultValue "")
$Name = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "Name")
$Options = Get-ConfigurationDataMapValue -Map $Reference -Key "Options" -DefaultValue @{}
if([string]::IsNullOrWhiteSpace($Vault) -and $ProviderSettings.ContainsKey("DefaultVault")){
$Vault = [string]$ProviderSettings.DefaultVault
}
$CommandParameters = @{}
if(-not [string]::IsNullOrWhiteSpace($Vault)){
$CommandParameters["DatabaseProfileName"] = $Vault
}
if($Options -is [System.Collections.IDictionary]){
foreach($Key in $Options.Keys){
$CommandParameters[$Key] = $Options[$Key]
}
}
if($ProviderSettings.ContainsKey("MasterKey")){
$CommandParameters["MasterKey"] = Resolve-ConfigurationDataProviderSecureString -Value $ProviderSettings.MasterKey
}
if(-not $CommandParameters.ContainsKey("Title") -and -not $CommandParameters.ContainsKey("Path")){
$CommandParameters["Title"] = $Name
}
$Entry = & $KeePassCommand @CommandParameters
if($null -eq $Entry){
throw "KeePass entry [$Name] was not found."
}
$Entry = @($Entry)[0]
$UserName = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "UserName" -DefaultValue "")
if([string]::IsNullOrWhiteSpace($UserName)){
$UserName = [string]$Entry.UserName
}
$Password = $Entry.Password
if($ExpectedType -eq "credential"){
if([string]::IsNullOrWhiteSpace($UserName)){
throw "KeePass entry [$Name] does not provide a username and no [UserName] override was defined."
}
return ConvertTo-ConfigurationDataCredential -UserName $UserName -Password $Password
}
if($Password -is [System.Security.SecureString]){
return $Password
}
if($ExpectedType -eq "securestring"){
return ConvertTo-SecureString -String ([string]$Password) -AsPlainText -Force
}
return [string]$Password
}
}
Register-ConfigurationDataSecretProvider @KeePassProvider

View File

@@ -1,57 +0,0 @@
$SecretStoreProvider = @{
Name = "SecretStore"
SupportedTypes = @(
"credential",
"securestring",
"string"
)
Resolver = {
Param(
[Parameter(Mandatory=$true)]
[System.Collections.IDictionary]
$Reference,
[Parameter(Mandatory=$true)]
[ValidateSet("credential", "securestring", "string")]
[string]
$ExpectedType,
[Parameter(Mandatory=$false)]
[hashtable]
$ProviderSettings = @{}
)
$GetSecretCommand = Get-Command -Name Get-Secret -ErrorAction SilentlyContinue
if($null -eq $GetSecretCommand){
throw "SecretStore provider requires the module [Microsoft.PowerShell.SecretManagement] and command [Get-Secret]."
}
$Vault = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "Vault" -DefaultValue "")
$Name = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "Name")
$UserName = [string](Get-ConfigurationDataMapValue -Map $Reference -Key "UserName" -DefaultValue "")
$Options = Get-ConfigurationDataMapValue -Map $Reference -Key "Options" -DefaultValue @{}
if([string]::IsNullOrWhiteSpace($Vault) -and $ProviderSettings.ContainsKey("DefaultVault")){
$Vault = [string]$ProviderSettings.DefaultVault
}
$CommandParameters = @{
Name = $Name
}
if(-not [string]::IsNullOrWhiteSpace($Vault)){
$CommandParameters["Vault"] = $Vault
}
if($Options -is [System.Collections.IDictionary]){
foreach($Key in $Options.Keys){
$CommandParameters[$Key] = $Options[$Key]
}
}
$Secret = & $GetSecretCommand @CommandParameters
return ConvertFrom-ConfigurationDataSecretValue -Secret $Secret -ExpectedType $ExpectedType -Name $Name -UserName $UserName
}
}
Register-ConfigurationDataSecretProvider @SecretStoreProvider

View File

@@ -0,0 +1,119 @@
function Get-DSCConfigurationDataCredentialProvider {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[string]
$Vault,
[Parameter(Mandatory=$false)]
[string]
$SettingsPath,
[Parameter(Mandatory=$false)]
[switch]
$TestVault
)
DynamicParam {
$ProviderNames = @(Get-ConfigurationDataSecretProviderName)
if($ProviderNames.Count -eq 0){
$ProviderNames = @("__NoProvidersRegistered__")
}
$ParameterAttribute = [System.Management.Automation.ParameterAttribute]::new()
$ParameterAttribute.Mandatory = $false
$ParameterAttribute.Position = 0
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList (,[string[]]$ProviderNames)
$Attributes = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$Attributes.Add($ParameterAttribute)
$Attributes.Add($ValidateSetAttribute)
$RuntimeParameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
"Provider",
[string],
$Attributes
)
$Dictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
$Dictionary.Add("Provider", $RuntimeParameter)
return $Dictionary
}
begin {
$Provider = [string]$PSBoundParameters["Provider"]
if([string]::IsNullOrWhiteSpace($Provider)){
$ProviderNames = @(Get-ConfigurationDataSecretProviderName)
if($ProviderNames.Count -eq 1){
$Provider = $ProviderNames[0]
}elseif($ProviderNames.Count -eq 0){
throw "No configuration data secret providers are registered."
}else{
throw "Provider is required. Registered providers: $($ProviderNames -join ', ')."
}
}
$ResolvedSettingsPath = Resolve-ConfigurationDataProviderSettingsPath -Provider $Provider -SettingsPath $SettingsPath
}
process {
$Settings = @{}
$SettingsExists = Test-Path -LiteralPath $ResolvedSettingsPath -PathType Leaf
if($SettingsExists){
$Settings = Import-PowerShellDataFile -LiteralPath $ResolvedSettingsPath
}
$ProviderSettings = @{}
if($Settings -is [System.Collections.IDictionary] -and $Settings.ContainsKey($Provider)){
$ProviderSettings = $Settings[$Provider]
}
$DefaultVault = ""
if($ProviderSettings -is [System.Collections.IDictionary] -and $ProviderSettings.ContainsKey("DefaultVault")){
$DefaultVault = [string]$ProviderSettings.DefaultVault
}
if([string]::IsNullOrWhiteSpace($Vault)){
$Vault = $DefaultVault
}
$Vaults = @()
$GetSecretVaultCommand = Get-Command -Name Get-SecretVault -ErrorAction SilentlyContinue
if($null -ne $GetSecretVaultCommand){
if([string]::IsNullOrWhiteSpace($Vault)){
$Vaults = @(& $GetSecretVaultCommand)
}else{
$Vaults = @(& $GetSecretVaultCommand -Name $Vault -ErrorAction SilentlyContinue)
}
}
$TestResult = $null
$TestError = $null
if($TestVault -and -not [string]::IsNullOrWhiteSpace($Vault)){
$TestSecretVaultCommand = Get-Command -Name Test-SecretVault -ErrorAction SilentlyContinue
if($null -eq $TestSecretVaultCommand){
$TestError = "Command [Test-SecretVault] was not found."
}else{
try {
$TestResult = [bool](& $TestSecretVaultCommand -Name $Vault -ErrorAction Stop)
}
catch {
$TestResult = $false
$TestError = $_.Exception.Message
}
}
}
[PSCustomObject]@{
Provider = $Provider
SettingsPath = $ResolvedSettingsPath
SettingsExists = $SettingsExists
DefaultVault = $DefaultVault
RequestedVault = $Vault
ProviderSettings = $ProviderSettings
RegisteredVaults = $Vaults
TestVault = $TestResult
TestError = $TestError
}
}
}

View File

@@ -1,10 +1,19 @@
function Register-DSCConfigurationDataCredentialProvider { function Register-DSCConfigurationDataCredentialProvider {
[CmdletBinding(SupportsShouldProcess=$true)] [CmdletBinding(SupportsShouldProcess=$true)]
Param( Param(
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true, Position=1)]
[string] [string]
$Vault, $Vault,
[Parameter(Mandatory=$false)]
[ValidateSet("Generic", "KeePass")]
[string]
$VaultType = "Generic",
[Parameter(Mandatory=$false)]
[string]
$DatabasePath,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[string] [string]
$KeyPath, $KeyPath,
@@ -13,10 +22,6 @@ function Register-DSCConfigurationDataCredentialProvider {
[string] [string]
$SettingsPath, $SettingsPath,
[Parameter(Mandatory=$false)]
[System.Security.SecureString]
$MasterKey,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[string] [string]
$ModuleName, $ModuleName,
@@ -25,14 +30,6 @@ function Register-DSCConfigurationDataCredentialProvider {
[hashtable] [hashtable]
$VaultParameters = @{}, $VaultParameters = @{},
[Parameter(Mandatory=$false)]
[string]
$SubscriptionId,
[Parameter(Mandatory=$false)]
[string]
$TenantId,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[switch] [switch]
$RegisterVault, $RegisterVault,
@@ -47,25 +44,17 @@ function Register-DSCConfigurationDataCredentialProvider {
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[switch] [switch]
$ConfigureSecretStore, $UseMasterPassword,
[Parameter(Mandatory=$false)]
[ValidateSet("None", "Password")]
[string]
$Authentication = "None",
[Parameter(Mandatory=$false)]
[ValidateSet("None", "Prompt")]
[string]
$Interaction = "None",
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[Alias("MasterKey")]
[System.Security.SecureString] [System.Security.SecureString]
$Password, $MasterPassword,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[int] [Alias("MasterKeyPath")]
$PasswordTimeout, [string]
$MasterPasswordKeyPath,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[switch] [switch]
@@ -120,44 +109,78 @@ function Register-DSCConfigurationDataCredentialProvider {
process { process {
switch($Provider){ switch($Provider){
"KeePass" {
if([string]::IsNullOrWhiteSpace($KeyPath)){
throw "Provider [KeePass] requires [KeyPath]."
}
if($null -eq $MasterKey){
$MasterKey = Read-Host -Prompt "KeePass MasterKey for vault [$Vault]" -AsSecureString
}
if($PSCmdlet.ShouldProcess($SettingsPath, "Create provider settings for [$Provider]")){
$Key = New-ConfigurationDataAesKeyFile -Path $KeyPath -Force:$Force
$ProtectedValue = $MasterKey | ConvertFrom-SecureString -Key $Key
$ProviderSettings = [ordered]@{
KeePass = [ordered]@{
DefaultVault = $Vault
MasterKey = [ordered]@{
ProtectedValue = $ProtectedValue
KeyPath = $KeyPath
}
}
}
Export-PowerShellDataFile -InputObject $ProviderSettings -Path $SettingsPath -Force:$Force
if($PassThru){
return $ProviderSettings
}
Write-Verbose "Provider settings written to [$SettingsPath]."
}
}
"SecretManagement" { "SecretManagement" {
if([string]::IsNullOrWhiteSpace($ModuleName)){ if([string]::IsNullOrWhiteSpace($ModuleName)){
if($VaultType -eq "KeePass"){
$ModuleName = "SecretManagement.KeePass"
}else{
$ModuleName = "Microsoft.PowerShell.SecretStore" $ModuleName = "Microsoft.PowerShell.SecretStore"
} }
}
if($RegisterVault -or $PSBoundParameters.ContainsKey("ModuleName")){ $ShouldRegisterVault = $RegisterVault -or $PSBoundParameters.ContainsKey("ModuleName")
if($VaultType -eq "KeePass" -and $ShouldRegisterVault){
if([string]::IsNullOrWhiteSpace($DatabasePath) -and -not $VaultParameters.ContainsKey("Path")){
throw "SecretManagement KeePass vault registration requires [DatabasePath] or VaultParameters['Path']."
}
if(-not [string]::IsNullOrWhiteSpace($DatabasePath)){
$VaultParameters["Path"] = $DatabasePath
}
if(-not [string]::IsNullOrWhiteSpace($KeyPath)){
$VaultParameters["KeyPath"] = $KeyPath
}
if($UseMasterPassword){
$VaultParameters["UseMasterPassword"] = $true
}
}
if($ShouldRegisterVault){
if($VaultType -eq "KeePass"){
$KeePassModule = Get-Module -Name SecretManagement.KeePass -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
if($null -eq $KeePassModule){
throw "SecretManagement KeePass vault registration requires module [SecretManagement.KeePass]."
}
$ModuleReference = $KeePassModule.Name
if(-not [string]::IsNullOrWhiteSpace($KeePassModule.Path)){
$ModuleReference = $KeePassModule.Path
}
$CommandParameters = @{
Name = $Vault
ModuleName = $ModuleReference
VaultParameters = @{
Path = $VaultParameters["Path"]
UseMasterPassword = $false
UseWindowsAccount = $false
KeyPath = $null
ShowFullTitle = $false
ShowRecycleBin = $false
}
}
foreach($Key in @("KeyPath", "UseMasterPassword", "UseWindowsAccount", "ShowFullTitle", "ShowRecycleBin", "SkipValidate")){
if($VaultParameters.ContainsKey($Key)){
$CommandParameters.VaultParameters[$Key] = $VaultParameters[$Key]
}
}
if($DefaultVault){
$CommandParameters["DefaultVault"] = $true
}
if($AllowClobber){
$CommandParameters["AllowClobber"] = $true
}
if($PSCmdlet.ShouldProcess($Vault, "Register KeePass SecretManagement vault [$ModuleReference]")){
Register-SecretVault @CommandParameters
}
}else{
$CommandParameters = @{ $CommandParameters = @{
Name = $Vault Name = $Vault
ModuleName = $ModuleName ModuleName = $ModuleName
@@ -179,6 +202,7 @@ function Register-DSCConfigurationDataCredentialProvider {
Register-SecretVault @CommandParameters Register-SecretVault @CommandParameters
} }
} }
}
$ProviderSettings = [ordered]@{ $ProviderSettings = [ordered]@{
SecretManagement = [ordered]@{ SecretManagement = [ordered]@{
@@ -186,80 +210,21 @@ function Register-DSCConfigurationDataCredentialProvider {
} }
} }
if($PSCmdlet.ShouldProcess($SettingsPath, "Create provider settings for [$Provider]")){ if($VaultType -ne "Generic"){
Export-PowerShellDataFile -InputObject $ProviderSettings -Path $SettingsPath -Force:$Force $ProviderSettings.SecretManagement["VaultType"] = $VaultType
} }
if($PassThru){ if($null -ne $MasterPassword){
return $ProviderSettings if([string]::IsNullOrWhiteSpace($MasterPasswordKeyPath)){
} throw "MasterPassword requires [MasterPasswordKeyPath]."
}
"SecretStore" {
if($ConfigureSecretStore){
$CommandParameters = @{
Authentication = $Authentication
Interaction = $Interaction
} }
if($null -ne $Password){ $Key = New-ConfigurationDataAesKeyFile -Path $MasterPasswordKeyPath -Force:$Force
$CommandParameters["Password"] = $Password $ProtectedValue = $MasterPassword | ConvertFrom-SecureString -Key $Key
$ProviderSettings.SecretManagement["MasterPassword"] = [ordered]@{
ProtectedValue = $ProtectedValue
KeyPath = $MasterPasswordKeyPath
} }
if($PasswordTimeout -gt 0){
$CommandParameters["PasswordTimeout"] = $PasswordTimeout
}
if($PSCmdlet.ShouldProcess("SecretStore", "Configure local SecretStore")){
Set-SecretStoreConfiguration @CommandParameters -Confirm:$false
}
}
if($RegisterVault){
$CommandParameters = @{
Name = $Vault
ModuleName = "Microsoft.PowerShell.SecretStore"
}
if($DefaultVault){
$CommandParameters["DefaultVault"] = $true
}
if($AllowClobber){
$CommandParameters["AllowClobber"] = $true
}
if($PSCmdlet.ShouldProcess($Vault, "Register SecretStore vault")){
Register-SecretVault @CommandParameters
}
}
$ProviderSettings = [ordered]@{
SecretStore = [ordered]@{
DefaultVault = $Vault
}
}
if($PSCmdlet.ShouldProcess($SettingsPath, "Create provider settings for [$Provider]")){
Export-PowerShellDataFile -InputObject $ProviderSettings -Path $SettingsPath -Force:$Force
}
if($PassThru){
return $ProviderSettings
}
}
"AzureKeyVault" {
$ProviderSettings = [ordered]@{
AzureKeyVault = [ordered]@{
DefaultVault = $Vault
}
}
if(-not [string]::IsNullOrWhiteSpace($SubscriptionId)){
$ProviderSettings["AzureKeyVault"]["SubscriptionId"] = $SubscriptionId
}
if(-not [string]::IsNullOrWhiteSpace($TenantId)){
$ProviderSettings["AzureKeyVault"]["TenantId"] = $TenantId
} }
if($PSCmdlet.ShouldProcess($SettingsPath, "Create provider settings for [$Provider]")){ if($PSCmdlet.ShouldProcess($SettingsPath, "Create provider settings for [$Provider]")){

View File

@@ -28,6 +28,7 @@ function Resolve-DSCConfigurationData {
} }
if(-not $SkipSecrets){ if(-not $SkipSecrets){
Unlock-ConfigurationDataSecretManagementVault -ProviderSettings $ProviderSettings
$ConfigurationData = Resolve-ConfigurationDataParameterSecrets -ConfigurationData $ConfigurationData -ProviderSettings $ProviderSettings $ConfigurationData = Resolve-ConfigurationDataParameterSecrets -ConfigurationData $ConfigurationData -ProviderSettings $ProviderSettings
} }

View File

@@ -0,0 +1,258 @@
function Set-DSCConfigurationDataCredentialProvider {
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
[Parameter(Mandatory=$false)]
[string]
$Vault,
[Parameter(Mandatory=$false)]
[AllowEmptyString()]
[ValidateSet("", "Generic", "KeePass")]
[string]
$VaultType = "",
[Parameter(Mandatory=$false)]
[string]
$DatabasePath,
[Parameter(Mandatory=$false)]
[string]
$KeyPath,
[Parameter(Mandatory=$false)]
[switch]
$DisableKeePassKeyFile,
[Parameter(Mandatory=$false)]
[string]
$ModuleName,
[Parameter(Mandatory=$false)]
[hashtable]
$VaultParameters = @{},
[Parameter(Mandatory=$false)]
[switch]
$RegisterVault,
[Parameter(Mandatory=$false)]
[switch]
$DefaultVault,
[Parameter(Mandatory=$false)]
[switch]
$AllowClobber,
[Parameter(Mandatory=$false)]
[Alias("UseMasterKey")]
[switch]
$UseMasterPassword,
[Parameter(Mandatory=$false)]
[Alias("NoMasterKey", "NoMasterPassword")]
[switch]
$DisableMasterPassword,
[Parameter(Mandatory=$false)]
[Alias("MasterKey")]
[System.Security.SecureString]
$MasterPassword,
[Parameter(Mandatory=$false)]
[Alias("MasterKeyPath")]
[string]
$MasterPasswordKeyPath,
[Parameter(Mandatory=$false)]
[switch]
$UseWindowsAccount,
[Parameter(Mandatory=$false)]
[switch]
$ShowFullTitle,
[Parameter(Mandatory=$false)]
[switch]
$ShowRecycleBin,
[Parameter(Mandatory=$false)]
[switch]
$SkipValidate,
[Parameter(Mandatory=$false)]
[string]
$SettingsPath,
[Parameter(Mandatory=$false)]
[switch]
$Force,
[Parameter(Mandatory=$false)]
[switch]
$PassThru
)
DynamicParam {
$ProviderNames = @(Get-ConfigurationDataSecretProviderName)
if($ProviderNames.Count -eq 0){
$ProviderNames = @("__NoProvidersRegistered__")
}
$ParameterAttribute = [System.Management.Automation.ParameterAttribute]::new()
$ParameterAttribute.Mandatory = $false
$ParameterAttribute.Position = 0
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList (,[string[]]$ProviderNames)
$Attributes = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
$Attributes.Add($ParameterAttribute)
$Attributes.Add($ValidateSetAttribute)
$RuntimeParameter = [System.Management.Automation.RuntimeDefinedParameter]::new(
"Provider",
[string],
$Attributes
)
$Dictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
$Dictionary.Add("Provider", $RuntimeParameter)
return $Dictionary
}
begin {
$Provider = [string]$PSBoundParameters["Provider"]
if([string]::IsNullOrWhiteSpace($Provider)){
$ProviderNames = @(Get-ConfigurationDataSecretProviderName)
if($ProviderNames.Count -eq 1){
$Provider = $ProviderNames[0]
}elseif($ProviderNames.Count -eq 0){
throw "No configuration data secret providers are registered."
}else{
throw "Provider is required. Registered providers: $($ProviderNames -join ', ')."
}
}
$ProviderInfo = Get-DSCConfigurationDataCredentialProvider -Provider $Provider -Vault $Vault -SettingsPath $SettingsPath
if([string]::IsNullOrWhiteSpace($Vault)){
$Vault = [string]$ProviderInfo.DefaultVault
}
if([string]::IsNullOrWhiteSpace($Vault)){
throw "Vault is required. Pass [Vault] or define [DefaultVault] in the provider settings file."
}
}
process {
$ExistingVault = @($ProviderInfo.RegisteredVaults | Select-Object -First 1)
$ExistingVaultParameters = @{}
if($ExistingVault.Count -gt 0 -and $ExistingVault[0].PSObject.Properties["VaultParameters"] -and $ExistingVault[0].VaultParameters -is [System.Collections.IDictionary]){
foreach($Key in $ExistingVault[0].VaultParameters.Keys){
$ExistingVaultParameters[$Key] = $ExistingVault[0].VaultParameters[$Key]
}
}
$EffectiveVaultType = $VaultType
if([string]::IsNullOrWhiteSpace($EffectiveVaultType) -and $ProviderInfo.ProviderSettings -is [System.Collections.IDictionary] -and $ProviderInfo.ProviderSettings.ContainsKey("VaultType")){
$EffectiveVaultType = [string]$ProviderInfo.ProviderSettings.VaultType
}
if([string]::IsNullOrWhiteSpace($EffectiveVaultType) -and $ExistingVault.Count -gt 0 -and $ExistingVault[0].PSObject.Properties["ModuleName"]){
if([string]$ExistingVault[0].ModuleName -match "SecretManagement\.KeePass"){
$EffectiveVaultType = "KeePass"
}
}
if([string]::IsNullOrWhiteSpace($EffectiveVaultType)){
$EffectiveVaultType = "Generic"
}
$EffectiveVaultParameters = @{}
foreach($Key in $ExistingVaultParameters.Keys){
$EffectiveVaultParameters[$Key] = $ExistingVaultParameters[$Key]
}
foreach($Key in $VaultParameters.Keys){
$EffectiveVaultParameters[$Key] = $VaultParameters[$Key]
}
if(-not [string]::IsNullOrWhiteSpace($DatabasePath)){
$EffectiveVaultParameters["Path"] = $DatabasePath
}
if(-not [string]::IsNullOrWhiteSpace($KeyPath)){
$EffectiveVaultParameters["KeyPath"] = $KeyPath
}
if($DisableKeePassKeyFile){
$EffectiveVaultParameters["KeyPath"] = $null
}
if($UseMasterPassword -and $DisableMasterPassword){
throw "Use either [UseMasterPassword] or [DisableMasterPassword], not both."
}
foreach($SwitchName in @("UseMasterPassword", "UseWindowsAccount", "ShowFullTitle", "ShowRecycleBin", "SkipValidate")){
if($PSBoundParameters.ContainsKey($SwitchName)){
$EffectiveVaultParameters[$SwitchName] = [bool]$PSBoundParameters[$SwitchName]
}
}
if($DisableMasterPassword){
$EffectiveVaultParameters["UseMasterPassword"] = $false
}
if($PSCmdlet.ShouldProcess($Vault, "Update DSC configuration data credential provider settings")){
$EffectiveMasterPasswordKeyPath = $MasterPasswordKeyPath
if([string]::IsNullOrWhiteSpace($EffectiveMasterPasswordKeyPath) -and
$ProviderInfo.ProviderSettings -is [System.Collections.IDictionary] -and
$ProviderInfo.ProviderSettings.ContainsKey("MasterPassword") -and
$ProviderInfo.ProviderSettings.MasterPassword -is [System.Collections.IDictionary] -and
$ProviderInfo.ProviderSettings.MasterPassword.ContainsKey("KeyPath")){
$EffectiveMasterPasswordKeyPath = [string]$ProviderInfo.ProviderSettings.MasterPassword.KeyPath
}
$CommandParameters = @{
Provider = $Provider
Vault = $Vault
VaultType = $EffectiveVaultType
SettingsPath = $SettingsPath
VaultParameters = $EffectiveVaultParameters
Force = $Force
PassThru = $true
}
if(-not [string]::IsNullOrWhiteSpace($ModuleName)){
$CommandParameters["ModuleName"] = $ModuleName
}
if($RegisterVault){
$CommandParameters["RegisterVault"] = $true
}
if($DefaultVault){
$CommandParameters["DefaultVault"] = $true
}
if($AllowClobber){
$CommandParameters["AllowClobber"] = $true
}
if($null -ne $MasterPassword){
$CommandParameters["MasterPassword"] = $MasterPassword
}
if(-not [string]::IsNullOrWhiteSpace($EffectiveMasterPasswordKeyPath)){
$CommandParameters["MasterPasswordKeyPath"] = $EffectiveMasterPasswordKeyPath
}
$Result = Register-DSCConfigurationDataCredentialProvider @CommandParameters
}
if($PassThru){
if($null -ne $Result){
return $Result
}
return (Get-DSCConfigurationDataCredentialProvider -Provider $Provider -Vault $Vault -SettingsPath $SettingsPath)
}
}
}

View File

@@ -5,10 +5,6 @@ function Unregister-DSCConfigurationDataCredentialProvider {
[string] [string]
$Vault, $Vault,
[Parameter(Mandatory=$false)]
[string]
$KeyPath,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[string] [string]
$SettingsPath, $SettingsPath,
@@ -17,18 +13,10 @@ function Unregister-DSCConfigurationDataCredentialProvider {
[switch] [switch]
$RemoveSettings, $RemoveSettings,
[Parameter(Mandatory=$false)]
[switch]
$RemoveKeyFile,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[switch] [switch]
$UnregisterVault, $UnregisterVault,
[Parameter(Mandatory=$false)]
[switch]
$ResetSecretStore,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[switch] [switch]
$Force, $Force,
@@ -86,9 +74,7 @@ function Unregister-DSCConfigurationDataCredentialProvider {
Vault = $Vault Vault = $Vault
SettingsPath = $SettingsPath SettingsPath = $SettingsPath
RemovedSettings = $false RemovedSettings = $false
RemovedKeyFile = $false
UnregisteredVault = $false UnregisteredVault = $false
ResetSecretStore = $false
} }
if($RemoveSettings -or (-not $PSBoundParameters.ContainsKey("RemoveSettings"))){ if($RemoveSettings -or (-not $PSBoundParameters.ContainsKey("RemoveSettings"))){
@@ -100,40 +86,12 @@ function Unregister-DSCConfigurationDataCredentialProvider {
} }
} }
if($RemoveKeyFile){
if([string]::IsNullOrWhiteSpace($KeyPath)){
throw "RemoveKeyFile requires [KeyPath]."
}
if(Test-Path -Path $KeyPath -PathType Leaf){
if($PSCmdlet.ShouldProcess($KeyPath, "Remove provider key file")){
Remove-Item -Path $KeyPath -Force:$Force
$Result.RemovedKeyFile = $true
}
}
}
if($UnregisterVault){ if($UnregisterVault){
if($Provider -notin @("SecretManagement", "SecretStore")){
Write-Verbose "Provider [$Provider] does not register a SecretManagement vault."
}else{
if($PSCmdlet.ShouldProcess($Vault, "Unregister SecretManagement vault")){ if($PSCmdlet.ShouldProcess($Vault, "Unregister SecretManagement vault")){
Unregister-SecretVault -Name $Vault Unregister-SecretVault -Name $Vault
$Result.UnregisteredVault = $true $Result.UnregisteredVault = $true
} }
} }
}
if($ResetSecretStore){
if($Provider -ne "SecretStore"){
Write-Verbose "ResetSecretStore is only applicable to provider [SecretStore]."
}else{
if($PSCmdlet.ShouldProcess("SecretStore", "Reset local SecretStore and delete all contained secrets")){
Reset-SecretStore -Force -Confirm:$false
$Result.ResetSecretStore = $true
}
}
}
if($PassThru){ if($PassThru){
return [PSCustomObject]$Result return [PSCustomObject]$Result

183
Readme.md
View File

@@ -110,7 +110,7 @@ FarmPassphrase = @{
Required = $true Required = $true
Sensitive = $true Sensitive = $true
Value = @{ Value = @{
Provider = 'KeePass' Provider = 'SecretManagement'
Vault = 'Contoso' Vault = 'Contoso'
Name = 'SharePoint/FarmPassphrase' Name = 'SharePoint/FarmPassphrase'
} }
@@ -121,7 +121,7 @@ SetupCredential = @{
Required = $true Required = $true
Sensitive = $true Sensitive = $true
Value = @{ Value = @{
Provider = 'KeePass' Provider = 'SecretManagement'
Vault = 'Contoso' Vault = 'Contoso'
Name = 'Application/SetupAccount' Name = 'Application/SetupAccount'
UserName = 'CONTOSO\svc-app-setup' UserName = 'CONTOSO\svc-app-setup'
@@ -133,7 +133,7 @@ Secret references are validated and resolved by `Resolve-DSCConfigurationData` b
```powershell ```powershell
$resolved = Resolve-DSCConfigurationData -ConfigurationData $merged -ProviderSettings @{ $resolved = Resolve-DSCConfigurationData -ConfigurationData $merged -ProviderSettings @{
KeePass = @{ SecretManagement = @{
DefaultVault = 'Contoso' DefaultVault = 'Contoso'
} }
} }
@@ -141,28 +141,43 @@ $resolved = Resolve-DSCConfigurationData -ConfigurationData $merged -ProviderSet
Use `-SkipSecrets` when you only want structural validation/resolution without loading provider secrets. Use `-SkipSecrets` when you only want structural validation/resolution without loading provider secrets.
For unattended KeePass access, pass the KeePass master key through `ProviderSettings`, not through the PSD1 parameter definition: SecretManagement is the only built-in secret resolver provider. KeePass, SecretStore, Azure Key Vault, and other backends should be registered as SecretManagement vaults.
```powershell KeePass can be registered through the `SecretManagement.KeePass` vault extension:
$resolved = Resolve-DSCConfigurationData -ConfigurationData $merged -ProviderSettings @{
KeePass = @{
DefaultVault = 'Contoso'
MasterKey = (Get-Secret -Name 'KeePass-Contoso-MasterKey')
}
}
```
You can generate the provider settings file with:
```powershell ```powershell
Register-DSCConfigurationDataCredentialProvider ` Register-DSCConfigurationDataCredentialProvider `
-Provider KeePass ` -Provider SecretManagement `
-Vault Contoso ` -Vault Contoso `
-KeyPath 'C:\DSC\Contoso\KeePass-Contoso.key' ` -VaultType KeePass `
-DatabasePath 'C:\DSC\Contoso\Secrets.kdbx' `
-KeyPath 'C:\DSC\Contoso\Secrets.key' `
-UseMasterPassword `
-RegisterVault `
-DefaultVault `
-SettingsPath 'C:\DSC\Contoso' -SettingsPath 'C:\DSC\Contoso'
``` ```
SecretManagement can be initialized through the same entry point: For unattended KeePass vaults that still require a master password, store the master password as a protected SecureString in the provider settings. The password is encrypted with an AES key file and the vault is unlocked automatically during `Resolve-DSCConfigurationData`:
```powershell
$masterPassword = Read-Host -Prompt 'KeePass Master Password' -AsSecureString
Register-DSCConfigurationDataCredentialProvider `
-Provider SecretManagement `
-Vault Contoso `
-VaultType KeePass `
-DatabasePath 'C:\DSC\Contoso\Secrets.kdbx' `
-KeyPath 'C:\DSC\Contoso\Secrets.key' `
-UseMasterPassword `
-MasterPassword $masterPassword `
-MasterPasswordKeyPath 'C:\DSC\Contoso\KeePass-MasterPassword.key' `
-RegisterVault `
-DefaultVault `
-SettingsPath 'C:\DSC\Contoso'
```
Generic SecretManagement vaults can be registered through the same entry point:
```powershell ```powershell
Register-DSCConfigurationDataCredentialProvider ` Register-DSCConfigurationDataCredentialProvider `
@@ -174,131 +189,59 @@ Register-DSCConfigurationDataCredentialProvider `
-SettingsPath 'C:\DSC\Contoso' -SettingsPath 'C:\DSC\Contoso'
``` ```
SecretStore can also be configured for unattended local usage:
```powershell
Register-DSCConfigurationDataCredentialProvider `
-Provider SecretStore `
-Vault LocalStore `
-ConfigureSecretStore `
-Authentication None `
-Interaction None `
-RegisterVault `
-DefaultVault `
-SettingsPath 'C:\DSC\Contoso'
```
Azure Key Vault can be registered as a settings file without storing Azure credentials. Authentication is expected to come from the active Az context, managed identity, service principal login, or another external Azure authentication flow:
```powershell
Register-DSCConfigurationDataCredentialProvider `
-Provider AzureKeyVault `
-Vault contoso-kv `
-SubscriptionId '00000000-0000-0000-0000-000000000000' `
-TenantId '11111111-1111-1111-1111-111111111111' `
-SettingsPath 'C:\DSC\Contoso'
```
Provider setup can be removed again: Provider setup can be removed again:
```powershell ```powershell
Unregister-DSCConfigurationDataCredentialProvider ` Unregister-DSCConfigurationDataCredentialProvider `
-Provider KeePass ` -Provider SecretManagement `
-Vault Contoso ` -Vault Contoso `
-SettingsPath 'C:\DSC\Contoso' ` -SettingsPath 'C:\DSC\Contoso' `
-KeyPath 'C:\DSC\Contoso\KeePass-Contoso.key' `
-RemoveKeyFile
```
For SecretManagement or SecretStore vault registration:
```powershell
Unregister-DSCConfigurationDataCredentialProvider `
-Provider SecretStore `
-Vault LocalStore `
-SettingsPath 'C:\DSC\Contoso' `
-UnregisterVault -UnregisterVault
``` ```
Use `-ResetSecretStore` only when you intentionally want to delete all secrets from the local SecretStore.
Then use it directly: Then use it directly:
```powershell ```powershell
$resolved = Resolve-DSCConfigurationData ` $resolved = Resolve-DSCConfigurationData `
-ConfigurationData $merged ` -ConfigurationData $merged `
-ProviderSettingsPath 'C:\DSC\Contoso\ProviderSettings.KeePass.psd1' -ProviderSettingsPath 'C:\DSC\Contoso\ProviderSettings.SecretManagement.psd1'
``` ```
For portable unattended tests, use an AES key file with `ConvertFrom-SecureString -Key`: Inspect the configured provider and registered vault:
```powershell ```powershell
$resolved = Resolve-DSCConfigurationData -ConfigurationData $merged -ProviderSettings @{ Get-DSCConfigurationDataCredentialProvider `
KeePass = @{ -Provider SecretManagement `
DefaultVault = 'Test' -SettingsPath 'C:\DSC\Contoso' `
MasterKey = @{ -TestVault
ProtectedValue = '76492d1116743f0423413b16050a5345...'
KeyPath = 'F:\Secrets\KeePass-Test.key'
}
}
}
``` ```
`MasterKey` also supports: Update an existing KeePass-backed SecretManagement provider registration, for example to enable master-password based unlocks:
```powershell ```powershell
MasterKey = @{ $masterPassword = Read-Host -Prompt 'KeePass Master Password' -AsSecureString
EnvironmentVariable = 'KEEPASS_TEST_MASTERKEY'
} Set-DSCConfigurationDataCredentialProvider `
-Provider SecretManagement `
-SettingsPath 'C:\DSC\Contoso' `
-UseMasterPassword `
-MasterPassword $masterPassword `
-MasterPasswordKeyPath 'C:\DSC\Contoso\KeePass-MasterPassword.key' `
-VaultParameters @{
ShowFullTitle = $true
} `
-RegisterVault `
-AllowClobber `
-Force
``` ```
### Secret Provider Files ### Secret Provider
Secret providers are loaded automatically from the module folder `Providers`. Only one built-in provider is registered:
The naming convention is:
```text
Providers\Provider.<Name>.ps1
```
Each provider registers itself with the same schema:
```powershell
$MyProvider = @{
Name = 'MyProvider'
SupportedTypes = @(
'credential',
'securestring',
'string'
)
Resolver = {
param(
[System.Collections.IDictionary] $Reference,
[string] $ExpectedType,
[hashtable] $ProviderSettings
)
# Return a PSCredential for ExpectedType = credential,
# a SecureString for ExpectedType = securestring,
# or a string for ExpectedType = string.
}
}
Register-ConfigurationDataSecretProvider @MyProvider
```
The KeePass provider is implemented in:
```text
Providers\Provider.KeePass.ps1
```
Built-in providers:
- `KeePass`: uses `PoShKeePass` / `Get-KeePassEntry`
- `SecretManagement`: uses `Microsoft.PowerShell.SecretManagement` / `Get-Secret` - `SecretManagement`: uses `Microsoft.PowerShell.SecretManagement` / `Get-Secret`
- `SecretStore`: convenience provider for local SecretStore vaults through `Get-Secret`
- `AzureKeyVault`: uses `Az.KeyVault` / `Get-AzKeyVaultSecret` Backend-specific behavior belongs to the registered SecretManagement vault extension. For example, KeePass is handled by `SecretManagement.KeePass`, SecretStore by `Microsoft.PowerShell.SecretStore`, and Azure Key Vault by the matching SecretManagement vault extension.
SecretManagement example: SecretManagement example:
@@ -315,7 +258,7 @@ SetupCredential = @{
} }
``` ```
SecretStore example: SecretStore example through SecretManagement:
```powershell ```powershell
FarmPassphrase = @{ FarmPassphrase = @{
@@ -323,14 +266,14 @@ FarmPassphrase = @{
Required = $true Required = $true
Sensitive = $true Sensitive = $true
Value = @{ Value = @{
Provider = 'SecretStore' Provider = 'SecretManagement'
Vault = 'LocalStore' Vault = 'LocalStore'
Name = 'SharePointFarmPassphrase' Name = 'SharePointFarmPassphrase'
} }
} }
``` ```
Azure Key Vault example: Azure Key Vault example through SecretManagement:
```powershell ```powershell
SetupCredential = @{ SetupCredential = @{
@@ -338,7 +281,7 @@ SetupCredential = @{
Required = $true Required = $true
Sensitive = $true Sensitive = $true
Value = @{ Value = @{
Provider = 'AzureKeyVault' Provider = 'SecretManagement'
Vault = 'contoso-kv' Vault = 'contoso-kv'
Name = 'app-setup-password' Name = 'app-setup-password'
UserName = 'CONTOSO\svc-app-setup' UserName = 'CONTOSO\svc-app-setup'
@@ -350,7 +293,7 @@ FarmPassphrase = @{
Required = $true Required = $true
Sensitive = $true Sensitive = $true
Value = @{ Value = @{
Provider = 'AzureKeyVault' Provider = 'SecretManagement'
Vault = 'contoso-kv' Vault = 'contoso-kv'
Name = 'farm-passphrase' Name = 'farm-passphrase'
} }

View File

@@ -1,14 +1,16 @@
@{ @{
RootModule = "Resolve-DSCConfigurationData.psm1" RootModule = "Resolve-DSCConfigurationData.psm1"
ModuleVersion = "1.0.1" ModuleVersion = "1.0.2"
GUID = "1d6ba0d4-93d5-4b0f-94c7-bf10a30fe0e8" GUID = "1d6ba0d4-93d5-4b0f-94c7-bf10a30fe0e8"
Author = "Torsten Brendgen" Author = "Torsten Brendgen"
Copyright = "(c) Torsten Brendgen. All rights reserved." Copyright = "(c) Torsten Brendgen. All rights reserved."
Description = "Resolves parameters, variables, and expressions in DSC configuration data." Description = "Resolves parameters, variables, and expressions in DSC configuration data."
PowerShellVersion = "5.1" PowerShellVersion = "5.1"
FunctionsToExport = @( FunctionsToExport = @(
"Get-DSCConfigurationDataCredentialProvider",
"Resolve-DSCConfigurationData", "Resolve-DSCConfigurationData",
"Register-DSCConfigurationDataCredentialProvider", "Register-DSCConfigurationDataCredentialProvider",
"Set-DSCConfigurationDataCredentialProvider",
"Unregister-DSCConfigurationDataCredentialProvider" "Unregister-DSCConfigurationDataCredentialProvider"
) )
CmdletsToExport = @() CmdletsToExport = @()

View File

@@ -14,7 +14,9 @@ foreach($File in @($Private + $Providers + $Public)){
} }
Export-ModuleMember -Function @( Export-ModuleMember -Function @(
"Get-DSCConfigurationDataCredentialProvider",
"Resolve-DSCConfigurationData", "Resolve-DSCConfigurationData",
"Register-DSCConfigurationDataCredentialProvider", "Register-DSCConfigurationDataCredentialProvider",
"Set-DSCConfigurationDataCredentialProvider",
"Unregister-DSCConfigurationDataCredentialProvider" "Unregister-DSCConfigurationDataCredentialProvider"
) )