683 lines
17 KiB
Markdown
683 lines
17 KiB
Markdown
# Resolve-DSCConfigurationData
|
|
|
|
`Resolve-DSCConfigurationData` resolves parameter, variable, and expression references in DSC configuration data.
|
|
|
|
Typical flow:
|
|
|
|
```powershell
|
|
$merged = Merge-DSCConfigurationData -Template $service -Deployment $environment
|
|
$resolved = Resolve-DSCConfigurationData -ConfigurationData $merged
|
|
```
|
|
|
|
Pipeline flow:
|
|
|
|
```powershell
|
|
$resolved = Merge-DSCConfigurationData -Template $service -Deployment $environment |
|
|
Resolve-DSCConfigurationData
|
|
```
|
|
|
|
Expressions use an ARM-like syntax:
|
|
|
|
```powershell
|
|
"[parameters('DatabasePrefix')]"
|
|
"[variables('ServiceDbPrefix')]"
|
|
"[concat(parameters('DatabasePrefix'), '_', parameters('ServiceDatabaseSegment'))]"
|
|
```
|
|
|
|
## Parameter Example
|
|
|
|
This block shows all currently supported parameter properties.
|
|
|
|
```powershell
|
|
@{
|
|
Parameters = @{
|
|
DatabasePrefix = @{
|
|
Type = 'string'
|
|
|
|
# Optional explicit value. If Value is present, it wins over DefaultValue.
|
|
Value = 'SharePoint'
|
|
|
|
# Used when Value is not present.
|
|
DefaultValue = 'SharePoint'
|
|
|
|
# If true, Value or DefaultValue must be present and not empty.
|
|
Required = $true
|
|
|
|
# Optional fixed set of valid values.
|
|
AllowedValues = @(
|
|
'SharePoint',
|
|
'ProjectServer',
|
|
'Search'
|
|
)
|
|
|
|
# Optional string length validation.
|
|
MinLength = 2
|
|
MaxLength = 32
|
|
|
|
# Optional numeric range validation. Applies to numeric values such as Type = 'int'.
|
|
MinValue = 1
|
|
MaxValue = 65535
|
|
|
|
# Optional regex validation.
|
|
Pattern = '^[A-Za-z][A-Za-z0-9_-]*$'
|
|
|
|
# Metadata for later reporting/output tooling. The resolver does not mask values yet.
|
|
Sensitive = $false
|
|
|
|
# Prevents child templates from changing this parameter definition during merge.
|
|
Sealed = $false
|
|
|
|
# Emits a warning when the parameter is present.
|
|
Deprecated = @{
|
|
Message = @{
|
|
'de-DE' = 'Der Parameter [DatabasePrefix] ist veraltet. Verwende [SharePointDatabasePrefix].'
|
|
'en-US' = 'Parameter [DatabasePrefix] is deprecated. Use [SharePointDatabasePrefix].'
|
|
}
|
|
}
|
|
|
|
Metadata = @{
|
|
Description = @{
|
|
'de-DE' = 'Praefix fuer alle von der SharePoint-Farm angelegten Datenbanken.'
|
|
'en-US' = 'Prefix for all databases created by the SharePoint farm.'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Notes:
|
|
|
|
- `Value` wins over `DefaultValue`.
|
|
- `Type`, `Required`, `AllowedValues`, `MinLength`, `MaxLength`, `MinValue`, `MaxValue`, and `Pattern` are validated by the resolver.
|
|
- `AllowedValues` validates scalar values directly and array values item by item.
|
|
- `Sensitive` is currently metadata only.
|
|
- `Sealed = $true` on a parameter seals the whole parameter definition during merge. Child templates cannot change any property of that parameter.
|
|
- `Deprecated.Message` can be a string or a localized hashtable.
|
|
- Optional string parameters can allow empty values with a pattern like `'^$|^[A-Za-z][A-Za-z0-9_-]*$'`.
|
|
|
|
Supported parameter types:
|
|
|
|
- `string`
|
|
- `int` / `integer`
|
|
- `bool` / `boolean`
|
|
- `array`
|
|
- `hashtable` / `object`
|
|
- `secureString`
|
|
- `credential`
|
|
|
|
`secureString` and `credential` are intended for secret references, not raw secrets in PSD1 files:
|
|
|
|
```powershell
|
|
FarmPassphrase = @{
|
|
Type = 'secureString'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretManagement'
|
|
Vault = 'Contoso'
|
|
Name = 'SharePoint/FarmPassphrase'
|
|
}
|
|
}
|
|
|
|
SetupCredential = @{
|
|
Type = 'credential'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretManagement'
|
|
Vault = 'Contoso'
|
|
Name = 'Application/SetupAccount'
|
|
UserName = 'CONTOSO\svc-app-setup'
|
|
}
|
|
}
|
|
```
|
|
|
|
Secret references are validated and resolved by `Resolve-DSCConfigurationData` before the normal parameter and variable resolve step:
|
|
|
|
```powershell
|
|
$resolved = Resolve-DSCConfigurationData -ConfigurationData $merged -ProviderSettings @{
|
|
SecretManagement = @{
|
|
DefaultVault = 'Contoso'
|
|
}
|
|
}
|
|
```
|
|
|
|
Use `-SkipSecrets` when you only want structural validation/resolution without loading provider secrets. Secret references are replaced with typed dummy values, so references such as `reference(..., 'UserName')` keep working in tests and previews.
|
|
|
|
```powershell
|
|
$preview = Resolve-DSCConfigurationData -ConfigurationData $merged -SkipSecrets
|
|
```
|
|
|
|
SecretManagement is the only built-in secret resolver provider. KeePass, SecretStore, Azure Key Vault, and other backends should be registered as SecretManagement vaults.
|
|
|
|
KeePass can be registered through the `SecretManagement.KeePass` vault extension:
|
|
|
|
```powershell
|
|
Register-DSCConfigurationDataCredentialProvider `
|
|
-Provider SecretManagement `
|
|
-Vault Contoso `
|
|
-VaultType KeePass `
|
|
-DatabasePath 'C:\DSC\Contoso\Secrets.kdbx' `
|
|
-KeyPath 'C:\DSC\Contoso\Secrets.key' `
|
|
-UseMasterPassword `
|
|
-RegisterVault `
|
|
-DefaultVault `
|
|
-SettingsPath 'C:\DSC\Contoso'
|
|
```
|
|
|
|
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
|
|
Register-DSCConfigurationDataCredentialProvider `
|
|
-Provider SecretManagement `
|
|
-Vault LocalStore `
|
|
-ModuleName Microsoft.PowerShell.SecretStore `
|
|
-RegisterVault `
|
|
-DefaultVault `
|
|
-SettingsPath 'C:\DSC\Contoso'
|
|
```
|
|
|
|
Provider setup can be removed again:
|
|
|
|
```powershell
|
|
Unregister-DSCConfigurationDataCredentialProvider `
|
|
-Provider SecretManagement `
|
|
-Vault Contoso `
|
|
-SettingsPath 'C:\DSC\Contoso' `
|
|
-UnregisterVault
|
|
```
|
|
|
|
Then use it directly:
|
|
|
|
```powershell
|
|
$resolved = Resolve-DSCConfigurationData `
|
|
-ConfigurationData $merged `
|
|
-ProviderSettingsPath 'C:\DSC\Contoso\ProviderSettings.SecretManagement.psd1'
|
|
```
|
|
|
|
## Sealed Template Blocks
|
|
|
|
`Sealed = $true` can also be placed on any hashtable block in the configuration data. During merge, child templates cannot add or overwrite anything at that node or below it.
|
|
|
|
```powershell
|
|
Resources = @{
|
|
NonNodeData = @{
|
|
Services = @{
|
|
SharePoint = @{
|
|
Farm = @{
|
|
ManagedAccounts = @{
|
|
Sealed = $true
|
|
|
|
FarmAccount = "[parameters('FarmCredential')]"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
The `Sealed` marker is kept during merge so later merge steps can enforce it. `Resolve-DSCConfigurationData` removes the marker from the final resolved data so DSC resource loops do not see it as a normal configuration item.
|
|
|
|
Inspect the configured provider and registered vault:
|
|
|
|
```powershell
|
|
Get-DSCConfigurationDataCredentialProvider `
|
|
-Provider SecretManagement `
|
|
-SettingsPath 'C:\DSC\Contoso' `
|
|
-TestVault
|
|
```
|
|
|
|
Update an existing KeePass-backed SecretManagement provider registration, for example to enable master-password based unlocks:
|
|
|
|
```powershell
|
|
$masterPassword = Read-Host -Prompt 'KeePass Master Password' -AsSecureString
|
|
|
|
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
|
|
|
|
Only one built-in provider is registered:
|
|
|
|
- `SecretManagement`: uses `Microsoft.PowerShell.SecretManagement` / `Get-Secret`
|
|
|
|
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:
|
|
|
|
```powershell
|
|
SetupCredential = @{
|
|
Type = 'credential'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretManagement'
|
|
Vault = 'LocalStore'
|
|
Name = 'SharePointSetupCredential'
|
|
}
|
|
}
|
|
```
|
|
|
|
SecretStore example through SecretManagement:
|
|
|
|
```powershell
|
|
FarmPassphrase = @{
|
|
Type = 'secureString'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretManagement'
|
|
Vault = 'LocalStore'
|
|
Name = 'SharePointFarmPassphrase'
|
|
}
|
|
}
|
|
```
|
|
|
|
Azure Key Vault example through SecretManagement:
|
|
|
|
```powershell
|
|
SetupCredential = @{
|
|
Type = 'credential'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretManagement'
|
|
Vault = 'contoso-kv'
|
|
Name = 'app-setup-password'
|
|
UserName = 'CONTOSO\svc-app-setup'
|
|
}
|
|
}
|
|
|
|
FarmPassphrase = @{
|
|
Type = 'secureString'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretManagement'
|
|
Vault = 'contoso-kv'
|
|
Name = 'farm-passphrase'
|
|
}
|
|
}
|
|
```
|
|
|
|
Array values can be restricted item by item:
|
|
|
|
```powershell
|
|
ServerRoles = @{
|
|
Type = 'array'
|
|
Value = @(
|
|
'WebFrontEnd',
|
|
'Application'
|
|
)
|
|
AllowedValues = @(
|
|
'WebFrontEnd',
|
|
'Application',
|
|
'Search'
|
|
)
|
|
}
|
|
```
|
|
|
|
Numeric values can be restricted with `MinValue` and `MaxValue`:
|
|
|
|
```powershell
|
|
SqlPort = @{
|
|
Type = 'int'
|
|
DefaultValue = 1433
|
|
MinValue = 1
|
|
MaxValue = 65535
|
|
}
|
|
```
|
|
|
|
## Variable Example
|
|
|
|
Variables may reference parameters and other variables. Nested variable references are supported. Circular references are rejected.
|
|
|
|
```powershell
|
|
@{
|
|
Parameters = @{
|
|
DatabasePrefix = @{
|
|
Type = 'string'
|
|
DefaultValue = 'SharePoint'
|
|
}
|
|
DomainLabel = @{
|
|
Type = 'string'
|
|
Value = 'corp'
|
|
}
|
|
Landscape = @{
|
|
Type = 'string'
|
|
Value = 'Test'
|
|
AllowedValues = @(
|
|
'Prod',
|
|
'Test'
|
|
)
|
|
}
|
|
ServiceDatabaseSegment = @{
|
|
Type = 'string'
|
|
DefaultValue = 'Services'
|
|
}
|
|
}
|
|
|
|
Variables = @{
|
|
StageCode = "[if(equals(parameters('Landscape'), 'Test'), 'TST', 'PRD')]"
|
|
DatabasePrefix = "[joinNotEmpty('_', parameters('DatabasePrefix'), parameters('DomainLabel'), variables('StageCode'))]"
|
|
ServiceDbPrefix = "[joinNotEmpty('_', variables('DatabasePrefix'), parameters('ServiceDatabaseSegment'))]"
|
|
ConfigDbName = "[joinNotEmpty('_', variables('DatabasePrefix'), 'Farm_Config')]"
|
|
}
|
|
}
|
|
```
|
|
|
|
Example output:
|
|
|
|
```text
|
|
StageCode : TST
|
|
DatabasePrefix : SharePoint_corp_TST
|
|
ServiceDbPrefix : SharePoint_corp_TST_Services
|
|
ConfigDbName : SharePoint_corp_TST_Farm_Config
|
|
```
|
|
|
|
## Functions
|
|
|
|
### References
|
|
|
|
```powershell
|
|
"[parameters('DatabasePrefix')]"
|
|
"[variables('ServiceDbPrefix')]"
|
|
"[reference('Resources.NonNodeData.Services.SharePoint.Farm.ManagedAccounts.FarmAccount')]"
|
|
"[reference('Resources.NonNodeData.Services.SharePoint.Farm.ManagedAccounts.FarmAccount', 'UserName')]"
|
|
```
|
|
|
|
`parameters(name)` returns the effective parameter value. `Value` is used before `DefaultValue`.
|
|
|
|
`variables(name)` resolves another variable. Variables may reference other variables.
|
|
|
|
`reference(path)` resolves another value from the configuration data by path. `reference(path, property)` resolves the value and then returns a property from it, such as `UserName` from a `PSCredential`.
|
|
|
|
### String Composition
|
|
|
|
```powershell
|
|
"[concat('SharePoint', '_', 'Services')]"
|
|
# SharePoint_Services
|
|
```
|
|
|
|
```powershell
|
|
"[format('{0}_{1}_{2}', parameters('DatabasePrefix'), parameters('DomainLabel'), parameters('Landscape'))]"
|
|
# SharePoint_corp_Test
|
|
```
|
|
|
|
```powershell
|
|
"[joinNotEmpty('_', parameters('DatabasePrefix'), parameters('DomainLabel'), '', 'Services')]"
|
|
# SharePoint_corp_Services
|
|
```
|
|
|
|
```powershell
|
|
"[defaultIfEmpty(parameters('DatabasePrefix'), 'SharePoint')]"
|
|
# SharePoint, when DatabasePrefix is empty
|
|
```
|
|
|
|
```powershell
|
|
"[coalesce(parameters('CustomPrefix'), parameters('DatabasePrefix'), 'SharePoint')]"
|
|
# First non-empty value
|
|
```
|
|
|
|
### Conditions And Boolean Logic
|
|
|
|
```powershell
|
|
"[if(equals(parameters('Landscape'), 'Test'), 'TST', 'PRD')]"
|
|
# TST
|
|
```
|
|
|
|
```powershell
|
|
"[equals(parameters('Landscape'), 'Test')]"
|
|
"[notEquals(parameters('Landscape'), 'Prod')]"
|
|
"[and(equals(parameters('Landscape'), 'Test'), not(empty(parameters('DatabasePrefix'))))]"
|
|
"[or(equals(parameters('Landscape'), 'Prod'), equals(parameters('Landscape'), 'Test'))]"
|
|
"[not(empty(parameters('DatabasePrefix')))]"
|
|
```
|
|
|
|
### Case And Text
|
|
|
|
```powershell
|
|
"[toLower('Contoso-CORP')]"
|
|
# contoso-corp
|
|
```
|
|
|
|
```powershell
|
|
"[toUpper('contoso-corp')]"
|
|
# CONTOSO-CORP
|
|
```
|
|
|
|
```powershell
|
|
"[trim(' SharePoint ')]"
|
|
"[trimStart(' SharePoint')]"
|
|
"[trimEnd('SharePoint ')]"
|
|
```
|
|
|
|
```powershell
|
|
"[replace('Contoso-CORP', '-', '_')]"
|
|
# Contoso_CORP
|
|
```
|
|
|
|
```powershell
|
|
"[substring('SharePoint', 5, 5)]"
|
|
# Point
|
|
```
|
|
|
|
```powershell
|
|
"[indexOf('CON', 1)]"
|
|
# G
|
|
```
|
|
|
|
`firstIndexOf` and `lastIndexOf` return the first or last function argument:
|
|
|
|
```powershell
|
|
"[firstIndexOf('a', 'b', 'c')]"
|
|
# a
|
|
|
|
"[lastIndexOf('a', 'b', 'c')]"
|
|
# c
|
|
```
|
|
|
|
### Name Cleanup
|
|
|
|
```powershell
|
|
"[sanitizeName(' SharePoint corp/Test DB ')]"
|
|
# SharePoint_corp_Test_DB
|
|
```
|
|
|
|
```powershell
|
|
"[sanitizeName(' SharePoint corp/Test DB ', '-')]"
|
|
# SharePoint-corp-Test-DB
|
|
```
|
|
|
|
```powershell
|
|
"[normalizeSeparator('__SharePoint___corp_Test__', '_')]"
|
|
# SharePoint_corp_Test
|
|
```
|
|
|
|
```powershell
|
|
"[prefixIfNotEmpty('corp', 'Contoso-')]"
|
|
# Contoso-corp
|
|
```
|
|
|
|
```powershell
|
|
"[suffixIfNotEmpty('SharePoint', '_DB')]"
|
|
# SharePoint_DB
|
|
```
|
|
|
|
### Collections
|
|
|
|
```powershell
|
|
"[split(parameters('DomainFQDN'), '.')]"
|
|
# @('contoso', 'com')
|
|
```
|
|
|
|
```powershell
|
|
"[join(split(parameters('DomainFQDN'), '.'), '_')]"
|
|
# contoso_com
|
|
```
|
|
|
|
```powershell
|
|
"[first(split(parameters('DomainFQDN'), '.'))]"
|
|
# contoso
|
|
```
|
|
|
|
```powershell
|
|
"[last(split(parameters('DomainFQDN'), '.'))]"
|
|
# de
|
|
```
|
|
|
|
```powershell
|
|
"[take(split(parameters('DomainFQDN'), '.'), 1)]"
|
|
# @('contoso')
|
|
```
|
|
|
|
```powershell
|
|
"[skip(split(parameters('DomainFQDN'), '.'), 1)]"
|
|
# @('de')
|
|
```
|
|
|
|
```powershell
|
|
"[unique(split('SP.SP.SQL', '.'))]"
|
|
# @('SP', 'SQL')
|
|
```
|
|
|
|
```powershell
|
|
"[sort(split('SQL.SP.APP', '.'))]"
|
|
# @('APP', 'SP', 'SQL')
|
|
```
|
|
|
|
### Inspection
|
|
|
|
```powershell
|
|
"[contains(parameters('DomainFQDN'), 'online')]"
|
|
# True
|
|
```
|
|
|
|
```powershell
|
|
"[contains(split(parameters('DomainFQDN'), '.'), 'de')]"
|
|
# True
|
|
```
|
|
|
|
```powershell
|
|
"[startsWith(parameters('DomainFQDN'), 'contoso')]"
|
|
# True
|
|
```
|
|
|
|
```powershell
|
|
"[endsWith(parameters('DomainFQDN'), 'de')]"
|
|
# True
|
|
```
|
|
|
|
```powershell
|
|
"[length(split(parameters('DomainFQDN'), '.'))]"
|
|
# 2
|
|
```
|
|
|
|
```powershell
|
|
"[empty(parameters('OptionalValue'))]"
|
|
# True, when OptionalValue is empty
|
|
```
|
|
|
|
### Padding
|
|
|
|
```powershell
|
|
"[padLeft('1', 2, '0')]"
|
|
# 01
|
|
```
|
|
|
|
```powershell
|
|
"[padRight('SP', 4, '0')]"
|
|
# SP00
|
|
```
|
|
|
|
## Full Function List
|
|
|
|
- `parameters(name)`
|
|
- `variables(name)`
|
|
- `reference(path)`
|
|
- `reference(path, property)`
|
|
- `concat(value1, value2, ...)`
|
|
- `format(formatString, value1, value2, ...)`
|
|
- `coalesce(value1, value2, ...)`
|
|
- `defaultIfEmpty(value, defaultValue)`
|
|
- `if(condition, trueValue, falseValue)`
|
|
- `equals(left, right)`
|
|
- `notEquals(left, right)`
|
|
- `and(value1, value2, ...)`
|
|
- `or(value1, value2, ...)`
|
|
- `not(value)`
|
|
- `toLower(value)`
|
|
- `toUpper(value)`
|
|
- `firstIndexOf(value1, value2, ...)`
|
|
- `lastIndexOf(value1, value2, ...)`
|
|
- `indexOf(value, index)`
|
|
- `substring(value, startIndex)`
|
|
- `substring(value, startIndex, length)`
|
|
- `replace(value, oldValue, newValue)`
|
|
- `sanitizeName(value)`
|
|
- `sanitizeName(value, separator)`
|
|
- `normalizeSeparator(value, separator)`
|
|
- `prefixIfNotEmpty(value, prefix)`
|
|
- `suffixIfNotEmpty(value, suffix)`
|
|
- `contains(value, search)`
|
|
- `startsWith(value, search)`
|
|
- `endsWith(value, search)`
|
|
- `split(value, separator)`
|
|
- `join(array, separator)`
|
|
- `joinNotEmpty(separator, value1, value2, ...)`
|
|
- `take(array, count)`
|
|
- `skip(array, count)`
|
|
- `first(array)`
|
|
- `last(array)`
|
|
- `unique(array)`
|
|
- `sort(array)`
|
|
- `trim(value)`
|
|
- `trimStart(value)`
|
|
- `trimEnd(value)`
|
|
- `padLeft(value, totalWidth)`
|
|
- `padLeft(value, totalWidth, paddingCharacter)`
|
|
- `padRight(value, totalWidth)`
|
|
- `padRight(value, totalWidth, paddingCharacter)`
|
|
- `length(value)`
|
|
- `empty(value)`
|
|
|
|
## Validation
|
|
|
|
Run the tests:
|
|
|
|
```powershell
|
|
Invoke-Pester -Script '.\PowerShell\Resolve-DSCConfigurationData\.tests\Resolve-DSCConfigurationData.Tests.ps1'
|
|
```
|