Enhance configuration data validation and add new utility functions

- Added validation for empty values in Assert-ConfigurationDataParameter.
- Introduced Assert-ConfigurationDataSecretReference for secret reference validation.
- Implemented Test-ConfigurationDataExpressionParentheses to validate expression syntax.
- Added Test-ConfigurationDataMissingReferenceError to check for missing parameter or variable definitions.
- Updated Invoke-ConfigurationDataExpression to handle new validation logic.
- Revised Readme.md to reflect changes in secret reference handling.
This commit is contained in:
Torsten Brendgen
2026-07-01 20:34:59 +02:00
parent 88b842c8e9
commit 1cd225c7a0
7 changed files with 145 additions and 2 deletions

View File

@@ -42,6 +42,10 @@ function Assert-ConfigurationDataParameter {
return
}
if((Test-ConfigurationDataValueIsEmpty -Value $Value) -and (-not $Required)){
return
}
Assert-ConfigurationDataParameterType -Name $Name -Definition $Definition -Value $Value
Assert-ConfigurationDataParameterAllowedValue -Name $Name -Definition $Definition -Value $Value

View File

@@ -48,11 +48,15 @@ function Assert-ConfigurationDataParameterType {
if(($Value -isnot [string]) -and ($Value -isnot [System.Security.SecureString]) -and (-not (Test-ConfigurationDataMap -Value $Value))){
throw "Parameter [$Name] expects type [secureString], but received [$($Value.GetType().Name)]."
}
Assert-ConfigurationDataSecretReference -Name $Name -TypeName "securestring" -Value $Value
}
"credential" {
if(($Value -isnot [System.Management.Automation.PSCredential]) -and (-not (Test-ConfigurationDataMap -Value $Value))){
throw "Parameter [$Name] expects type [credential], but received [$($Value.GetType().Name)]."
}
Assert-ConfigurationDataSecretReference -Name $Name -TypeName "credential" -Value $Value
}
default {
throw "Parameter [$Name] uses unsupported type [$TypeName]."

View File

@@ -0,0 +1,45 @@
function Assert-ConfigurationDataSecretReference {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]
$Name,
[Parameter(Mandatory=$true)]
[ValidateSet("credential", "securestring")]
[string]
$TypeName,
[Parameter(Mandatory=$true)]
$Value
)
if(-not (Test-ConfigurationDataMap -Value $Value)){
return
}
if(-not (Test-ConfigurationDataMapContainsKey -Map $Value -Key "Provider")){
throw "Parameter [$Name] uses type [$TypeName] and a secret reference, but [Provider] is not defined."
}
if(-not (Test-ConfigurationDataMapContainsKey -Map $Value -Key "Name")){
throw "Parameter [$Name] uses type [$TypeName] and a secret reference, but [Name] is not defined."
}
$Provider = [string](Get-ConfigurationDataMapValue -Map $Value -Key "Provider")
if([string]::IsNullOrWhiteSpace($Provider)){
throw "Parameter [$Name] secret reference [Provider] must not be empty."
}
$SecretName = [string](Get-ConfigurationDataMapValue -Map $Value -Key "Name")
if([string]::IsNullOrWhiteSpace($SecretName)){
throw "Parameter [$Name] secret reference [Name] must not be empty."
}
switch($Provider.ToLowerInvariant()){
"keepass" { return }
default {
throw "Parameter [$Name] uses unsupported secret provider [$Provider]."
}
}
}

View File

@@ -23,12 +23,40 @@ function Invoke-ConfigurationDataExpression {
return [bool]::Parse($Expression)
}
if(-not (Test-ConfigurationDataExpressionParentheses -Expression $Expression)){
throw "Invalid configuration data expression [$Expression]."
}
if($Expression -notmatch "^([A-Za-z][A-Za-z0-9]*)\((.*)\)$"){
throw "Invalid configuration data expression [$Expression]."
}
$FunctionName = $Matches[1]
$ArgumentText = $Matches[2]
if($FunctionName -ieq "coalesce"){
$RawArguments = @(Split-ConfigurationDataExpressionArguments -ArgumentText $ArgumentText)
Assert-ConfigurationDataExpressionMinimumArgumentCount -Name $FunctionName -Arguments $RawArguments -Count 1
foreach($Argument in $RawArguments){
try {
$Value = Invoke-ConfigurationDataExpressionArgument -Argument $Argument -Context $Context
if(-not (Test-ConfigurationDataValueIsEmpty -Value $Value)){
return $Value
}
}
catch {
if(Test-ConfigurationDataMissingReferenceError -ErrorRecord $_){
continue
}
throw
}
}
return $null
}
$Arguments = @()
foreach($Argument in @(Split-ConfigurationDataExpressionArguments -ArgumentText $ArgumentText)){
$Arguments += ,(Invoke-ConfigurationDataExpressionArgument -Argument $Argument -Context $Context)

View File

@@ -0,0 +1,39 @@
function Test-ConfigurationDataExpressionParentheses {
[CmdletBinding()]
Param(
[AllowNull()]
[string]
$Expression
)
if([string]::IsNullOrWhiteSpace($Expression)){
return $true
}
$Depth = 0
$InString = $false
for($Index = 0; $Index -lt $Expression.Length; $Index++){
$Character = $Expression[$Index]
if($Character -eq "'"){
$InString = -not $InString
continue
}
if($InString){
continue
}
if($Character -eq "("){
$Depth++
}elseif($Character -eq ")"){
$Depth--
if($Depth -lt 0){
return $false
}
}
}
return $Depth -eq 0 -and -not $InString
}

View File

@@ -0,0 +1,11 @@
function Test-ConfigurationDataMissingReferenceError {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[System.Management.Automation.ErrorRecord]
$ErrorRecord
)
$Message = $ErrorRecord.Exception.Message
return $Message -match "^(Parameter|Variable) \[.+\] is not defined\.$"
}

View File

@@ -103,7 +103,9 @@ FarmPassphrase = @{
Required = $true
Sensitive = $true
Value = @{
SecretName = 'SharePointFarmPassphrase'
Provider = 'KeePass'
Vault = 'BGW'
Name = 'SharePoint/FarmPassphrase'
}
}
@@ -112,11 +114,21 @@ SetupCredential = @{
Required = $true
Sensitive = $true
Value = @{
CredentialName = 'SharePointSetup'
Provider = 'KeePass'
Vault = 'BGW'
Name = 'SharePoint/SetupAccount'
UserName = 'BGW\SVC_SHP_SETUP'
}
}
```
Secret references are validated by `Resolve-DSCConfigurationData`, but they are resolved by `Resolve-DSCConfigurationSecrets` before the normal data resolve step:
```powershell
$withSecrets = Resolve-DSCConfigurationSecrets -ConfigurationData $merged
$resolved = Resolve-DSCConfigurationData -ConfigurationData $withSecrets
```
Array values can be restricted item by item:
```powershell