663 lines
15 KiB
Markdown
663 lines
15 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
|
|
|
|
# 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.
|
|
- `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 = 'KeePass'
|
|
Vault = 'Contoso'
|
|
Name = 'SharePoint/FarmPassphrase'
|
|
}
|
|
}
|
|
|
|
SetupCredential = @{
|
|
Type = 'credential'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'KeePass'
|
|
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 @{
|
|
KeePass = @{
|
|
DefaultVault = 'Contoso'
|
|
}
|
|
}
|
|
```
|
|
|
|
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:
|
|
|
|
```powershell
|
|
$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
|
|
Register-DSCConfigurationDataCredentialProvider `
|
|
-Provider KeePass `
|
|
-Vault Contoso `
|
|
-KeyPath 'C:\DSC\Contoso\KeePass-Contoso.key' `
|
|
-SettingsPath 'C:\DSC\Contoso'
|
|
```
|
|
|
|
SecretManagement can be initialized through the same entry point:
|
|
|
|
```powershell
|
|
Register-DSCConfigurationDataCredentialProvider `
|
|
-Provider SecretManagement `
|
|
-Vault LocalStore `
|
|
-ModuleName Microsoft.PowerShell.SecretStore `
|
|
-RegisterVault `
|
|
-DefaultVault `
|
|
-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'
|
|
```
|
|
|
|
Provider setup can be removed again:
|
|
|
|
```powershell
|
|
Unregister-DSCConfigurationDataCredentialProvider `
|
|
-Provider KeePass `
|
|
-Vault 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
|
|
```
|
|
|
|
Use `-ResetSecretStore` only when you intentionally want to delete all secrets from the local SecretStore.
|
|
|
|
Then use it directly:
|
|
|
|
```powershell
|
|
$resolved = Resolve-DSCConfigurationData `
|
|
-ConfigurationData $merged `
|
|
-ProviderSettingsPath 'C:\DSC\Contoso\ProviderSettings.KeePass.psd1'
|
|
```
|
|
|
|
For portable unattended tests, use an AES key file with `ConvertFrom-SecureString -Key`:
|
|
|
|
```powershell
|
|
$resolved = Resolve-DSCConfigurationData -ConfigurationData $merged -ProviderSettings @{
|
|
KeePass = @{
|
|
DefaultVault = 'Test'
|
|
MasterKey = @{
|
|
ProtectedValue = '76492d1116743f0423413b16050a5345...'
|
|
KeyPath = 'F:\Secrets\KeePass-Test.key'
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`MasterKey` also supports:
|
|
|
|
```powershell
|
|
MasterKey = @{
|
|
EnvironmentVariable = 'KEEPASS_TEST_MASTERKEY'
|
|
}
|
|
```
|
|
|
|
### Secret Provider Files
|
|
|
|
Secret providers are loaded automatically from the module folder `Providers`.
|
|
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`
|
|
- `SecretStore`: convenience provider for local SecretStore vaults through `Get-Secret`
|
|
|
|
SecretManagement example:
|
|
|
|
```powershell
|
|
SetupCredential = @{
|
|
Type = 'credential'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretManagement'
|
|
Vault = 'LocalStore'
|
|
Name = 'SharePointSetupCredential'
|
|
}
|
|
}
|
|
```
|
|
|
|
SecretStore example:
|
|
|
|
```powershell
|
|
FarmPassphrase = @{
|
|
Type = 'secureString'
|
|
Required = $true
|
|
Sensitive = $true
|
|
Value = @{
|
|
Provider = 'SecretStore'
|
|
Vault = 'LocalStore'
|
|
Name = 'SharePointFarmPassphrase'
|
|
}
|
|
}
|
|
```
|
|
|
|
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')]"
|
|
```
|
|
|
|
`parameters(name)` returns the effective parameter value. `Value` is used before `DefaultValue`.
|
|
|
|
`variables(name)` resolves another variable. Variables may reference other variables.
|
|
|
|
### 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)`
|
|
- `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'
|
|
```
|