Compare commits

..

5 Commits

Author SHA1 Message Date
Torsten Brendgen
6986570510 Merge branch 'main' of https://git.local.unique-studios.de/PowerShell-Projekte/DSC-Configuration-Compiler 2026-06-27 23:19:38 +02:00
Torsten Brendgen
846dccf468 clean dev commit 2026-06-27 23:18:34 +02:00
7dd71be14c Merge pull request 'dev' (#1) from dev into main
Reviewed-on: #1
2026-06-27 21:18:01 +00:00
f5dcc022cf adding readme.md 2026-04-14 15:04:28 +02:00
5a57021e6a initial dev commit 2026-04-14 15:01:50 +02:00
10 changed files with 4738 additions and 531 deletions

3813
Compiler.Core.ps1 Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,7 @@
<<<<<<< HEAD
$formsType = 'System.Windows.Forms.Form' -as [type]
if ($null -eq $formsType) {
=======
 using namespace System.Windows.Forms  using namespace System.Windows.Forms
using namespace System.Drawing using namespace System.Drawing
@@ -2923,55 +2927,75 @@
} }
# Concat mit einer dynamischen Anzahl von Argumenten # Concat mit einer dynamischen Anzahl von Argumenten
static [string] Concat([string[]]$input) { static [string] Concat([object[]]$input) {
return $input -join '' return (($input | ForEach-Object { [string]$_ }) -join '')
} }
# ToLower-Methode # ToLower-Methode
static [string] ToLower([string[]]$input) { static [string] ToLower([object[]]$input) {
return $input[0].ToLower() if ($input.Count -lt 1) {
return ""
}
return ([string]$input[0]).ToLowerInvariant()
} }
# ToUpper-Methode # ToUpper-Methode
static [string] ToUpper([string[]]$input) { static [string] ToUpper([object[]]$input) {
return $input[0].ToUpper() if ($input.Count -lt 1) {
return ""
}
return ([string]$input[0]).ToUpperInvariant()
} }
# FirstIndexOf-Methode # FirstIndexOf-Methode
static [string] FirstIndexOf([string[]] $input){ static [string] FirstIndexOf([object[]] $input){
return $input[0] if ($input.Count -lt 1) {
return ""
}
return [string]$input[0]
} }
# LastIndexOf-Methode # LastIndexOf-Methode
static [string] LastIndexOf([string[]] $input){ static [string] LastIndexOf([object[]] $input){
return $input[-1] if ($input.Count -lt 1) {
return ""
}
return [string]$input[-1]
} }
# IndexOf-Methode # IndexOf-Methode
static [string] IndexOf([string[]] $input){ static [string] IndexOf([object[]] $input){
return $input[$input[-1]] if ($input.Count -lt 2) {
throw "IndexOf benötigt mindestens zwei Argumente."
}
return [string]$input[[int]$input[-1]]
} }
# Substring-Methode # Substring-Methode
static [String] Substring([string[]] $input){ static [String] Substring([object[]] $input){
if($input.Count -eq 2){ if($input.Count -eq 2){
$array = $input.Split(',') return ([string]$input[0]).Substring([int]$input[1])
return $array[0].Substring($array[1])
}elseif($input.Count -eq 3){ }elseif($input.Count -eq 3){
$array = $input.Split(',') return ([string]$input[0]).Substring([int]$input[1],[int]$input[2])
return $array[0].Substring($array[1],$array[2])
}else{ }else{
Write-Warning "Zuviele Parameter" throw "Substring erwartet zwei oder drei Argumente."
return $false
} }
} }
# Replace-Methode # Replace-Methode
static [String] Replace([string[]] $input){ static [String] Replace([object[]] $input){
$array = $input.Split(',') if ($input.Count -lt 3) {
return $array[0].Replace($array[1],$array[2]) throw "Replace erwartet drei Argumente."
}
return ([string]$input[0]).Replace([string]$input[1],[string]$input[2])
} }
hidden [System.Collections.Specialized.OrderedDictionary] ResolveDeploymentParameterDefaultValues(){ hidden [System.Collections.Specialized.OrderedDictionary] ResolveDeploymentParameterDefaultValues(){
@@ -3025,6 +3049,10 @@
[ref]$errors [ref]$errors
) )
if ($errors) {
throw "Fehler beim Parsen der Datei: $($errors[0].Message)"
}
$hashtableAst = $ast.Find({ $hashtableAst = $ast.Find({
$args[0] -is [System.Management.Automation.Language.HashtableAst] $args[0] -is [System.Management.Automation.Language.HashtableAst]
}, $true) }, $true)
@@ -3038,176 +3066,108 @@
return $this.Template return $this.Template
} }
hidden [System.Collections.Specialized.OrderedDictionary] ConvertToOrderedHashtable($HashtableAst){ hidden [System.Collections.Specialized.OrderedDictionary] ConvertToOrderedHashtable([System.Management.Automation.Language.HashtableAst]$HashtableAst){
$ordered = New-Object System.Collections.Specialized.OrderedDictionary
# Verwende OrderedDictionary statt [ordered]@{}
$ordered = New-Object System.Collections.Specialized.OrderedDictionary $sortedKvps = $HashtableAst.KeyValuePairs | Sort-Object { $_.Item1.Extent.StartOffset }
# Sortiere KeyValuePairs nach ihrer Position im Quelltext foreach ($kvp in $sortedKvps) {
$sortedKvps = $HashtableAst.KeyValuePairs | Sort-Object { $_.Item1.Extent.StartOffset } $key = [string]$this.ConvertAstValue($kvp.Item1)
$value = $this.ConvertAstValue($kvp.Item2)
foreach ($kvp in $sortedKvps) { $ordered.Add($key, $value)
# Schlüssel extrahieren }
$key = $kvp.Item1.Extent.Text -replace '[''"]', ''
return $ordered
# Wert verarbeiten }
$valueAst = $kvp.Item2
hidden [System.Object] ConvertAstValue([System.Management.Automation.Language.Ast]$Ast) {
# Suche nach HashtableAst in PipelineAst if ($null -eq $Ast) {
$hashtable = $null return $null
if ($valueAst -is [System.Management.Automation.Language.HashtableAst]) { }
$hashtable = $valueAst
} if ($Ast -is [System.Management.Automation.Language.PipelineAst]) {
elseif ($valueAst -is [System.Management.Automation.Language.PipelineAst]) { if ($Ast.PipelineElements.Count -eq 1 -and $Ast.PipelineElements[0] -is [System.Management.Automation.Language.CommandExpressionAst]) {
# Suche HashtableAst innerhalb der Pipeline return $this.ConvertAstValue($Ast.PipelineElements[0].Expression)
$hashtable = $valueAst.Find({
$args[0] -is [System.Management.Automation.Language.HashtableAst]
}, $false)
}
if ($hashtable) {
$value = $this.ConvertToOrderedHashtable($hashtable)
}
elseif ($valueAst -is [System.Management.Automation.Language.ArrayLiteralAst]) {
$value = @()
foreach ($element in $valueAst.Elements) {
if ($element -is [System.Management.Automation.Language.HashtableAst]) {
$value += $this.ConvertToOrderedHashtable($element)
}
else {
$value += Invoke-Expression $element.Extent.Text
}
}
}
else {
try {
$value = Invoke-Expression $valueAst.Extent.Text
}
catch {
$value = $valueAst.Extent.Text
}
}
$ordered.Add($key, $value)
} }
return $ordered return $Ast.SafeGetValue()
}
if ($Ast -is [System.Management.Automation.Language.CommandExpressionAst]) {
return $this.ConvertAstValue($Ast.Expression)
}
if ($Ast -is [System.Management.Automation.Language.ParenExpressionAst]) {
return $this.ConvertAstValue($Ast.Pipeline)
}
if ($Ast -is [System.Management.Automation.Language.ArrayExpressionAst]) {
return $this.ConvertAstValue($Ast.SubExpression)
}
if ($Ast -is [System.Management.Automation.Language.StatementBlockAst]) {
if ($Ast.Statements.Count -eq 1 -and $Ast.Statements[0] -is [System.Management.Automation.Language.PipelineAst]) {
return $this.ConvertAstValue($Ast.Statements[0])
}
throw "Nicht unterstützter Ausdruck in PSD1-Datei: $($Ast.Extent.Text)"
}
if ($Ast -is [System.Management.Automation.Language.ArrayLiteralAst]) {
$result = @()
foreach ($element in $Ast.Elements) {
$result += $this.ConvertAstValue($element)
}
return $result
}
if ($Ast -is [System.Management.Automation.Language.HashtableAst]) {
return $this.ConvertToOrderedHashtable($Ast)
}
if ($Ast -is [System.Management.Automation.Language.VariableExpressionAst]) {
switch ($Ast.VariablePath.UserPath) {
'true' { return $true }
'false' { return $false }
'null' { return $null }
default { return $Ast.Extent.Text }
}
}
try {
return $Ast.SafeGetValue()
}
catch {
return $Ast.Extent.Text
}
} }
hidden [System.Object] ResolveValue($Value, $Parameters, $Variables){ hidden [System.Object] ResolveValue($Value, $Parameters, $Variables){
$resolutionStack = [System.Collections.Generic.List[string]]::new()
return $this.ResolveValueInternal($Value, $Parameters, $Variables, $resolutionStack)
}
hidden [System.Object] ResolveValueInternal($Value, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack){
if ($Value -is [string]) { if ($Value -is [string]) {
# Resolve Parameter-Referenzen: [Parameter('name')] $trimmedValue = $Value.Trim()
while ($Value -match "\[Parameter\('([^']*)'\)\]") { if ($this.IsTemplateExpression($trimmedValue)) {
$paramName = $matches[1] return $this.EvaluateTemplateExpression($trimmedValue.Substring(1, $trimmedValue.Length - 2), $Parameters, $Variables, $ResolutionStack)
if ($Parameters.Contains($paramName)) {
# Prüfe erst auf Value, dann auf DefaultValue
$paramValue = $Parameters[$paramName].Value
if ($null -ne $paramValue) {
$Value = $Value -replace "\[Parameter\('$([regex]::Escape($paramName))'\)\]", $paramValue
}
else {
break
}
}
else {
Write-Warning "Parameter '$paramName' nicht gefunden"
break
}
}
# Resolve Variable-Referenzen: [Variable('name')]
while ($Value -match "\[Variable\('([^']*)'\)\]") {
$varName = $matches[1]
if ($Variables.Contains($varName)) {
$varValue = $this.ResolveValue($Variables[$varName],$Parameters,$Variables)
$Value = $Value -replace "\[Variable\('$([regex]::Escape($varName))'\)\]", $varValue
}
else {
Write-Warning "Variable '$varName' nicht gefunden"
break
}
}
while ($Value -match "Parameter\('([^']*)'\)") {
$paramName = $matches[1]
if ($Parameters.Contains($paramName)) {
# Prüfe erst auf Value, dann auf DefaultValue
$paramValue = if ($Parameters[$paramName].Value) {
$Parameters[$paramName].Value
}
elseif ($Parameters[$paramName].DefaultValue) {
$Parameters[$paramName].DefaultValue
}
else {
Write-Warning "Parameter '$paramName' hat weder Value noch DefaultValue"
$null
}
if ($null -ne $paramValue) {
$Value = $Value -replace "Parameter\('$([regex]::Escape($paramName))'\)", $("'"+$paramValue+"'")
}
else {
break
}
}
else {
Write-Warning "Parameter '$paramName' nicht gefunden"
break
}
} }
#Resolve Variable-Referenzen: Variable('name') return $this.ResolveInlineTemplateExpressions($Value, $Parameters, $Variables, $ResolutionStack)
while ($Value -match "Variable\('([^']*)'\)"){
$varName = $matches[1]
if ($Variables.Contains($varName)) {
$varValue = $this.ResolveValue($Variables[$varName],$Parameters,$Variables)
$Value = $Value -replace "Variable\('$([regex]::Escape($varName))'\)", $("'"+$varValue+"'")
}
else {
Write-Warning "Variable '$varName' nicht gefunden"
break
}
}
# Resolve SSPUtility-Funktionen
foreach($Function in $([TemplateBuilder].GetMethods('Static, Public')).Name | Get-Unique) {
$pattern = "\["+$Function+"\((.*?)\)\]"
while ($Value -match $pattern) {
try {
$fullMatch = $matches[0]
$argsString = $matches[1]
# Parse die Argumente korrekt - sie sind bereits als PowerShell-Array formatiert
# Wir müssen sie nur evaluieren
$scriptBlock = [scriptblock]::Create("@($argsString)")
$argsArray = & $scriptBlock
# Rufe die Methode direkt auf statt Invoke-Expression
$result = [TemplateBuilder]::$Function($argsArray)
$Value = $Value.Replace($fullMatch, $result)
}
catch {
Write-Warning "Fehler beim Ausführen von $matches[0] : $_"
break
}
}
}
return $Value
} }
elseif ($Value -is [System.Collections.IDictionary]) { elseif ($Value -is [System.Collections.IDictionary]) {
$resolved = New-Object System.Collections.Specialized.OrderedDictionary $resolved = New-Object System.Collections.Specialized.OrderedDictionary
foreach ($key in $Value.Keys) { foreach ($key in $Value.Keys) {
$resolved[$key] = $this.ResolveValue($Value[$key],$Parameters,$Variables) $resolved[$key] = $this.ResolveValueInternal($Value[$key], $Parameters, $Variables, $ResolutionStack)
} }
return $resolved return $resolved
} }
elseif ($Value -is [array]) { elseif ($Value -is [array]) {
$resolved = @() $resolved = @()
foreach ($item in $Value) { foreach ($item in $Value) {
$resolved += $this.ResolveValue($item,$Parameters,$Variables) $resolved += $this.ResolveValueInternal($item, $Parameters, $Variables, $ResolutionStack)
} }
return $resolved return $resolved
} }
@@ -3215,6 +3175,229 @@
return $Value return $Value
} }
} }
hidden [bool] IsTemplateExpression([string]$Value) {
if ([string]::IsNullOrWhiteSpace($Value)) {
return $false
}
if (-not ($Value.StartsWith('[') -and $Value.EndsWith(']'))) {
return $false
}
return $this.IsFunctionInvocation($Value.Substring(1, $Value.Length - 2))
}
hidden [bool] IsFunctionInvocation([string]$Value) {
return $Value.Trim() -match '^[A-Za-z_][A-Za-z0-9_]*\s*\(.*\)$'
}
hidden [string] ResolveInlineTemplateExpressions([string]$Value, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) {
$resolved = $Value
$matches = [regex]::Matches($Value, '\[[^\[\]]+\]')
for ($i = $matches.Count - 1; $i -ge 0; $i--) {
$match = $matches[$i]
$expressionText = $match.Value.Substring(1, $match.Value.Length - 2)
if (-not $this.IsFunctionInvocation($expressionText)) {
continue
}
$replacement = $this.EvaluateTemplateExpression($expressionText, $Parameters, $Variables, $ResolutionStack)
$resolved = $resolved.Remove($match.Index, $match.Length).Insert($match.Index, [string]$replacement)
}
return $resolved
}
hidden [System.Object] EvaluateTemplateExpression([string]$ExpressionText, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) {
$trimmedExpression = $ExpressionText.Trim()
if ($trimmedExpression -notmatch '^([A-Za-z_][A-Za-z0-9_]*)\s*\((.*)\)$') {
return $ExpressionText
}
$functionName = $matches[1]
$argumentsText = $matches[2]
$resolvedArguments = @()
$result = $null
foreach ($argument in $this.SplitExpressionArguments($argumentsText)) {
$resolvedArguments += ,($this.ResolveExpressionToken($argument, $Parameters, $Variables, $ResolutionStack))
}
switch ($functionName.ToLowerInvariant()) {
'parameter' { $result = $this.GetParameterValue([string]$resolvedArguments[0], $Parameters); break }
'parameters' { $result = $this.GetParameterValue([string]$resolvedArguments[0], $Parameters); break }
'variable' { $result = $this.GetVariableValue([string]$resolvedArguments[0], $Parameters, $Variables, $ResolutionStack); break }
'variables' { $result = $this.GetVariableValue([string]$resolvedArguments[0], $Parameters, $Variables, $ResolutionStack); break }
'concat' { $result = [TemplateBuilder]::Concat($resolvedArguments); break }
'tolower' { $result = [TemplateBuilder]::ToLower($resolvedArguments); break }
'toupper' { $result = [TemplateBuilder]::ToUpper($resolvedArguments); break }
'firstindexof' { $result = [TemplateBuilder]::FirstIndexOf($resolvedArguments); break }
'lastindexof' { $result = [TemplateBuilder]::LastIndexOf($resolvedArguments); break }
'indexof' { $result = [TemplateBuilder]::IndexOf($resolvedArguments); break }
'substring' { $result = [TemplateBuilder]::Substring($resolvedArguments); break }
'replace' { $result = [TemplateBuilder]::Replace($resolvedArguments); break }
default {
throw "Nicht unterstützte Template-Funktion '$functionName'"
return $null
}
}
return $result
}
hidden [string[]] SplitExpressionArguments([string]$ArgumentsText) {
$arguments = New-Object 'System.Collections.Generic.List[string]'
if ([string]::IsNullOrWhiteSpace($ArgumentsText)) {
return $arguments.ToArray()
}
$builder = New-Object System.Text.StringBuilder
$depth = 0
$inSingleQuote = $false
$inDoubleQuote = $false
for ($i = 0; $i -lt $ArgumentsText.Length; $i++) {
$char = $ArgumentsText[$i]
if ($char -eq "'" -and -not $inDoubleQuote) {
[void]$builder.Append($char)
if ($inSingleQuote -and $i + 1 -lt $ArgumentsText.Length -and $ArgumentsText[$i + 1] -eq "'") {
$i++
[void]$builder.Append($ArgumentsText[$i])
continue
}
$inSingleQuote = -not $inSingleQuote
continue
}
if ($char -eq '"' -and -not $inSingleQuote) {
$inDoubleQuote = -not $inDoubleQuote
[void]$builder.Append($char)
continue
}
if (-not $inSingleQuote -and -not $inDoubleQuote) {
if ($char -eq '(') {
$depth++
}
elseif ($char -eq ')') {
$depth--
}
elseif ($char -eq ',' -and $depth -eq 0) {
$arguments.Add($builder.ToString().Trim())
[void]$builder.Clear()
continue
}
}
[void]$builder.Append($char)
}
if ($builder.Length -gt 0 -or $ArgumentsText.Trim().Length -gt 0) {
$arguments.Add($builder.ToString().Trim())
}
return $arguments.ToArray()
}
hidden [System.Object] ResolveExpressionToken([string]$Token, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) {
$trimmedToken = $Token.Trim()
if ([string]::IsNullOrWhiteSpace($trimmedToken)) {
return ''
}
if ($this.IsTemplateExpression($trimmedToken)) {
return $this.EvaluateTemplateExpression($trimmedToken.Substring(1, $trimmedToken.Length - 2), $Parameters, $Variables, $ResolutionStack)
}
if ($this.IsFunctionInvocation($trimmedToken)) {
return $this.EvaluateTemplateExpression($trimmedToken, $Parameters, $Variables, $ResolutionStack)
}
if (($trimmedToken.StartsWith("'") -and $trimmedToken.EndsWith("'")) -or ($trimmedToken.StartsWith('"') -and $trimmedToken.EndsWith('"'))) {
return $this.UnquoteString($trimmedToken)
}
if ($trimmedToken -match '^\$?true$') {
return $true
}
if ($trimmedToken -match '^\$?false$') {
return $false
}
if ($trimmedToken -match '^\$?null$') {
return $null
}
if ($trimmedToken -match '^-?\d+$') {
return [int]$trimmedToken
}
if ($trimmedToken -match '^-?\d+\.\d+$') {
return [double]$trimmedToken
}
return $trimmedToken
}
hidden [string] UnquoteString([string]$Value) {
if ($Value.StartsWith("'") -and $Value.EndsWith("'")) {
return ($Value.Substring(1, $Value.Length - 2) -replace "''", "'")
}
if ($Value.StartsWith('"') -and $Value.EndsWith('"')) {
return $Value.Substring(1, $Value.Length - 2)
}
return $Value
}
hidden [System.Object] GetParameterValue([string]$ParameterName, $Parameters) {
if ($null -eq $Parameters -or -not $Parameters.Contains($ParameterName)) {
throw "Parameter '$ParameterName' nicht gefunden"
}
$parameter = $Parameters[$ParameterName]
if ($parameter -is [System.Collections.IDictionary]) {
if ($parameter.Contains('Value') -and $null -ne $parameter.Value) {
return $parameter.Value
}
if ($parameter.Contains('DefaultValue')) {
return $parameter.DefaultValue
}
}
return $parameter
}
hidden [System.Object] GetVariableValue([string]$VariableName, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) {
if ($null -eq $Variables -or -not $Variables.Contains($VariableName)) {
throw "Variable '$VariableName' nicht gefunden"
return $null
}
if ($ResolutionStack.Contains($VariableName)) {
$cycle = @($ResolutionStack.ToArray()) + $VariableName
throw "Zirkuläre Variablenreferenz erkannt: $($cycle -join ' -> ')"
return $null
}
$ResolutionStack.Add($VariableName)
$resolvedValue = $null
try {
$resolvedValue = $this.ResolveValueInternal($Variables[$VariableName], $Parameters, $Variables, $ResolutionStack)
}
finally {
[void]$ResolutionStack.Remove($VariableName)
}
return $resolvedValue
}
} }
class TabControlBuilder : BaseComponent{ class TabControlBuilder : BaseComponent{
@@ -3325,26 +3508,27 @@
} }
#endregion #endregion
>>>>>>> 7dd71be14c5216e6f7f99d006643ed1139a69376
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Windows.Forms
}
$drawingType = 'System.Drawing.Color' -as [type]
if ($null -eq $drawingType) {
Add-Type -AssemblyName System.Drawing Add-Type -AssemblyName System.Drawing
}
$settingsManager = [SettingsManager]::new("DSC Tool") if ([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne [System.Threading.ApartmentState]::STA) {
$powershell = (Get-Command powershell.exe -ErrorAction SilentlyContinue).Source
# Verwendung if ($powershell) {
$logManager = [LogManager]::new("DSC Tool") & $powershell -NoProfile -ExecutionPolicy Bypass -STA -File $PSCommandPath @args
$logManager.Info("Anwendung gestartet") exit $LASTEXITCODE
[BaseComponent]::InitializeShared($settingsManager, $logManager)
foreach($function in $(Get-ChildItem -Path $settingsManager.Get("FunctionsPath") -Recurse -Filter "*.ps1" -File)){
$logManager.Info("Lade Funktion: $($function.FullName)")
try{
. $function.FullName
}catch{
$logManager.Error("Fehler beim Laden: $_")
}
} }
}
<<<<<<< HEAD
$coreScript = Join-Path -Path $PSScriptRoot -ChildPath 'Compiler.Core.ps1'
& $coreScript @args
=======
$stateManager = [StateManager]::new() $stateManager = [StateManager]::new()
$stateManager.SetStates(@{ $stateManager.SetStates(@{
hasSelection = $false hasSelection = $false
@@ -3811,4 +3995,5 @@
# Initial alle Button-States aktualisieren # Initial alle Button-States aktualisieren
$stateManager.UpdateAllButtons() $stateManager.UpdateAllButtons()
$form.Show() $form.Show()
>>>>>>> 7dd71be14c5216e6f7f99d006643ed1139a69376

View File

@@ -1,93 +0,0 @@
@{
Resources = @{
NonNodeData = @{
Services = @{
SharePoint = @{
Farm = @{
Passphrase = 'Use-SecureString-Or-KeyVault'
ConfigDatabaseName = 'SharePoint_Farm_Config'
ServiceApplications = @{
AppManagementService = @{
DatabaseName = 'SharePoint_Services_AppManagement'
Provision = 'True'
}
StateService = @{
DatabaseName = 'SharePoint_Services_StateService'
Provision = 'True'
}
SubscriptionSettingsService = @{
DatabaseName = 'SharePoint_Services_SubscriptionSettings'
Provision = 'True'
}
ManagedMetadataService = @{
DatabaseName = 'SharePoint_Services_ManagedMetadata'
Name = 'Managed Metadata Service'
ApplicationPool = 'SharePoint Service Applications'
Provision = 'True'
}
SearchService = @{
DatabaseName = 'SharePoint_Services_Search'
Name = 'Search Service Application'
ApplicationPool = 'SharePoint Service Applications'
Provision = 'True'
}
UsageAndHealthService = @{
DatabaseName = 'SharePoint_Services_UsageAndHealth'
Provision = 'True'
}
SecureStoreService = @{
DatabaseName = 'SharePoint_Services_SecureStore'
Name = 'Secure Store Service'
ApplicationPool = 'SharePoint Service Applications'
Provision = 'True'
}
UserProfileService = @{
SyncDBName = 'SharePoint_Services_UserProfile_SyncDB'
ApplicationPool = 'SharePoint User Profile Services'
Provision = 'True'
Name = 'User Profile Service'
SocialDBName = 'SharePoint_Services_UserProfile_SocialDB'
ProfileDBName = 'SharePoint_Services_UserProfile_ProfileDB'
}
}
CentralAdminAuth = 'NTLM'
CentralAdminPort = '2016'
AdminContentDatabase = 'SharePoint_Farm_AdminContent'
ServiceApplicationPools = @{
Default = @{
Account = 'CONTOSO\sp_services'
Name = 'SharePoint Service Applications'
}
UserProfile = @{
Account = 'CONTOSO\sp_ups'
Name = 'SharePoint User Profile Services'
}
}
}
Database = @{
SQLAlias = @{
SQLServer = @{
InstanceName = ''
ServerName = ''
TcpPort = '0'
}
}
}
General = @{
ProductKey = '0000-0000-0000-0000-0000'
}
Windows = @{
Registry = @{
DisableLoopbackCheck = @{
Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa'
Name = 'DisableLoopbackCheck'
Value = '1'
Type = 'DWord'
}
}
}
}
}
}
}
}

View File

@@ -1,89 +0,0 @@
function Import-OrderedPowerShellDataFile {
param(
[Parameter(Mandatory)]
[string]$Path
)
function ConvertTo-OrderedHashtable {
param(
$HashtableAst
)
# Verwende OrderedDictionary statt [ordered]@{}
$ordered = New-Object System.Collections.Specialized.OrderedDictionary
# Sortiere KeyValuePairs nach ihrer Position im Quelltext
$sortedKvps = $HashtableAst.KeyValuePairs | Sort-Object { $_.Item1.Extent.StartOffset }
foreach ($kvp in $sortedKvps) {
# Schlüssel extrahieren
$key = $kvp.Item1.Extent.Text -replace '[''"]', ''
# Wert verarbeiten
$valueAst = $kvp.Item2
# Suche nach HashtableAst in PipelineAst
$hashtable = $null
if ($valueAst -is [System.Management.Automation.Language.HashtableAst]) {
$hashtable = $valueAst
}
elseif ($valueAst -is [System.Management.Automation.Language.PipelineAst]) {
# Suche HashtableAst innerhalb der Pipeline
$hashtable = $valueAst.Find({
$args[0] -is [System.Management.Automation.Language.HashtableAst]
}, $false)
}
if ($hashtable) {
$value = ConvertTo-OrderedHashtable -HashtableAst $hashtable
}
elseif ($valueAst -is [System.Management.Automation.Language.ArrayLiteralAst]) {
$value = @()
foreach ($element in $valueAst.Elements) {
if ($element -is [System.Management.Automation.Language.HashtableAst]) {
$value += ConvertTo-OrderedHashtable -HashtableAst $element
}
else {
$value += Invoke-Expression $element.Extent.Text
}
}
}
else {
try {
$value = Invoke-Expression $valueAst.Extent.Text
}
catch {
$value = $valueAst.Extent.Text
}
}
$ordered.Add($key, $value)
}
return $ordered
}
# Datei parsen
$errors = $null
$tokens = $null
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
$Path,
[ref]$tokens,
[ref]$errors
)
if ($errors) {
throw "Fehler beim Parsen der Datei: $($errors[0].Message)"
}
# Finde die oberste Hashtable
$hashtableAst = $ast.Find({
$args[0] -is [System.Management.Automation.Language.HashtableAst]
}, $true)
if (-not $hashtableAst) {
throw "Keine Hashtable in der Datei gefunden"
}
return ConvertTo-OrderedHashtable -HashtableAst $hashtableAst
}

View File

@@ -10,10 +10,11 @@ function Merge-Templates {
try { try {
try { try {
$TemplateData = Import-OrderedPowerShellDataFile -Path $file -ErrorAction Stop $TemplateData = [TemplateBuilder]::new($file).Template
} }
catch { catch {
$Errors += "Datei konnte nicht importiert werden" $Errors += "Datei konnte nicht importiert werden"
continue
} }
$ConfigurationData = Merge-ConfigurationData -Template $ConfigurationData -Deployment $TemplateData $ConfigurationData = Merge-ConfigurationData -Template $ConfigurationData -Deployment $TemplateData
@@ -40,4 +41,4 @@ function Merge-Templates {
else { else {
return $ConfigurationData return $ConfigurationData
} }
} }

185
README.md Normal file
View File

@@ -0,0 +1,185 @@
# DSC Configuration Compiler
Ein WinForms-basiertes PowerShell-Tool zum Laden, Kombinieren, Anzeigen und Exportieren von DSC-nahen Konfigurationen auf Basis von `psd1`-Dateien.
Der Fokus des Projekts liegt auf einem einfachen Template-Workflow:
- Templates aus Ordnern laden
- mehrere Templates zu einer Zielkonfiguration zusammenfuehren
- Parameter und Variablen aufloesen
- die resultierende Struktur im TreeView pruefen
- die zusammengefuehrte Konfiguration wieder als `psd1` exportieren
## Voraussetzungen
- Windows
- PowerShell mit WinForms-Unterstuetzung
- Schreibzugriff auf `%APPDATA%`
Das Tool speichert Einstellungen und Logs unterhalb von `%APPDATA%\DSC Tool`.
## Start
Das Projekt kann direkt ueber `Compiler.ps1` gestartet werden:
```powershell
powershell -ExecutionPolicy Bypass -File .\Compiler.ps1
```
Alternativ:
```powershell
pwsh -File .\Compiler.ps1
```
## Projektstruktur
```text
.
|-- Compiler.ps1
|-- Functions/
|-- Templates/
`-- Deployments/
```
- `Compiler.ps1`: Hauptanwendung, UI, Parser- und Resolver-Logik
- `Functions/`: zusaetzliche Funktionen fuer Import, Export, Merge und UI-Aktualisierung
- `Templates/`: wiederverwendbare Konfigurationsbausteine
- `Deployments/`: exportierte oder vorbereitete Zielkonfigurationen
## Bedienung
1. Anwendung starten.
2. Falls noetig ueber `Einstellungen -> Ordner waehlen...` den Root-Ordner setzen.
3. Templates aus den Kategorien laden.
4. Gewuenschte Templates per Doppelklick zur Auswahl hinzufuegen.
5. Die Auswahl zusammenfuehren.
6. Das Ergebnis im TreeView pruefen.
7. Die zusammengefuehrte Konfiguration als `psd1` exportieren.
Import und Export werden ueber den Deployment-Ordner abgewickelt.
## Template-Modell
Templates bestehen aus drei optionalen Hauptbereichen:
- `Parameters`
- `Variables`
- `Resources`
Ein vereinfachtes Beispiel:
```powershell
@{
Parameters = @{
DatabasePrefix = @{
Type = 'string'
DefaultValue = 'SP'
}
}
Variables = @{
ConfigDbName = "[concat(parameters('DatabasePrefix'),'_','Farm_Config')]"
}
Resources = @{
NonNodeData = @{
Services = @{
Example = @{
DatabaseName = "[variables('ConfigDbName')]"
}
}
}
}
}
```
## Ausdruckssyntax
Die Auswertung ist ARM-inspiriert. Template-Ausdruecke stehen in eckigen Klammern:
```powershell
"[concat(parameters('Prefix'),'_','Database')]"
```
Unterstuetzte Referenzen:
- `parameters('Name')`
- `variables('Name')`
Zusaetzlich werden aus Rueckwaertskompatibilitaet auch diese Varianten verstanden:
- `Parameter('Name')`
- `Variable('Name')`
Unterstuetzte Funktionen:
- `concat(...)`
- `toLower(...)`
- `toUpper(...)`
- `substring(...)`
- `replace(...)`
- `indexOf(...)`
- `firstIndexOf(...)`
- `lastIndexOf(...)`
Funktionsnamen werden ohne freie PowerShell-Ausfuehrung ausgewertet. Die `psd1`-Dateien werden als Daten geparst, nicht als beliebiger Script-Code interpretiert.
## Aufloesungsreihenfolge
Die Aufloesung erfolgt in dieser Reihenfolge:
1. Parameterwerte und Defaultwerte
2. Variablen
3. Ressourcenwerte
Zirkulaere Variablenreferenzen werden erkannt und als Fehler behandelt.
## Merge-Verhalten
Beim Zusammenfuehren mehrerer Templates werden vorhandene Eintraege rekursiv kombiniert:
- Hashtables werden rekursiv gemerged
- Arrays werden anhand passender Name-Felder zusammengefuehrt
- fehlende Werte aus Templates werden in das Ergebnis uebernommen
Die finale Anzeige im TreeView basiert auf dem zusammengefuehrten und bereits aufgeloesten Modell.
## Export
Der Export schreibt die aktuelle TreeView-Struktur wieder in eine `psd1`-Datei.
Standardziel beim Speichern ist:
```text
Deployments\merged_config.psd1
```
## Hinweise
- Einstellungen werden benutzerbezogen gespeichert.
- Logs werden pro Tag unter `%APPDATA%\DSC Tool\Logs` geschrieben.
- Templates sollten deklarativ bleiben und nur Daten plus Template-Ausdruecke enthalten.
- Fuer neue Templates wird die ARM-aehnliche Syntax mit `parameters()` und `variables()` empfohlen.
## Beispielinhalt
Im Repository ist bereits ein groesseres Beispiel enthalten:
- `Templates\Service\SharePointServer.psd1`
Dieses Template zeigt die Verwendung von:
- Parametern
- Variablen
- verschachtelten Ressourcen
- String-Funktionen wie `concat()` und `substring()`
## Weiterentwicklung
Sinnvolle naechste Schritte fuer das Projekt sind:
- Tests fuer Parser, Resolver und Merge-Verhalten
- typsicherer Export-Roundtrip
- robustere Initialisierung der Pfade und Settings
- weitere Template-Funktionen nach Bedarf

View File

@@ -1,14 +0,0 @@
@{
Resources = @{
NonNodeData = @{
Services = @{
ActiveDirectory = @{
Domain = @{
FQDN = "contoso.local"
NetBIOS = "Contoso"
}
}
}
}
}
}

View File

@@ -0,0 +1,161 @@
@{
Parameters = @{
DomainLabel = @{
Type = 'string'
DefaultValue = ''
Required = $false
MinLength = 2
MaxLength = 32
Pattern = '^[A-Za-z][A-Za-z0-9_-]*$'
Metadata = @{
Description = @{
'de-DE' = 'Kurzer logischer Bezeichner der Umgebung oder Domaene.'
'en-US' = 'Short logical identifier for the environment or domain.'
}
}
}
DomainFQDN = @{
Type = 'string'
DefaultValue = ''
Required = $true
Pattern = '^[A-Za-z0-9.-]+$'
Metadata = @{
Description = @{
'de-DE' = 'Vollqualifizierter DNS-Name der Active-Directory-Domaene.'
'en-US' = 'Fully qualified DNS name of the Active Directory domain.'
}
}
}
DomainNetBIOS = @{
Type = 'string'
DefaultValue = ''
Required = $true
MinLength = 1
MaxLength = 15
Pattern = '^[A-Za-z0-9_-]+$'
Metadata = @{
Description = @{
'de-DE' = 'NetBIOS-Name der Active-Directory-Domaene.'
'en-US' = 'NetBIOS name of the Active Directory domain.'
}
}
}
}
Resources = @{
NonNodeData = @{
RequiredModules = @(
@{
Name = 'ActiveDirectoryDsc'
Version = '6.7.1'
}
)
Services = @{
ActiveDirectory = @{
Domain = @{
Ensure = 'Present'
FQDN = "[parameters('DomainFQDN')]"
NetBIOS = "[parameters('DomainNetBIOS')]"
ForestMode = "[parameters('ForestMode')]"
DomainMode = "[parameters('DomainMode')]"
DnsDelegation = $false
Credentials = @{
DomainAdministrator = @{
CredentialName = "[parameters('DomainAdministratorCredentialName')]"
}
SafeModeAdministrator = @{
SecretName = "[parameters('SafeModeAdministratorPasswordSecretName')]"
}
}
}
DomainControllers = @(
@{
NodeName = '*'
Ensure = 'Present'
InstallDns = $true
IsGlobalCatalog = $true
SiteName = "[parameters('DefaultSiteName')]"
}
)
Sites = @(
@{
Name = "[parameters('DefaultSiteName')]"
Ensure = 'Present'
}
)
Subnets = @(
@{
Name = "[parameters('DefaultSubnet')]"
SiteName = "[parameters('DefaultSiteName')]"
Ensure = 'Present'
}
)
OrganizationalUnits = @(
@{
Name = 'Servers'
Path = "[concat('DC=', replace(parameters('DomainFQDN'), '.', ',DC='))]"
Ensure = 'Present'
ProtectedFromAccidentalDeletion = $true
}
@{
Name = 'Service Accounts'
Path = "[concat('DC=', replace(parameters('DomainFQDN'), '.', ',DC='))]"
Ensure = 'Present'
ProtectedFromAccidentalDeletion = $true
}
@{
Name = 'Groups'
Path = "[concat('DC=', replace(parameters('DomainFQDN'), '.', ',DC='))]"
Ensure = 'Present'
ProtectedFromAccidentalDeletion = $true
}
)
Groups = @(
@{
GroupName = 'GG-SharePoint-Admins'
Path = "[concat('OU=Groups,DC=', replace(parameters('DomainFQDN'), '.', ',DC='))]"
Scope = 'Global'
Category = 'Security'
Ensure = 'Present'
Members = @()
}
)
Users = @(
@{
UserName = 'SVC_SHP_Setup'
Path = "[concat('OU=Service Accounts,DC=', replace(parameters('DomainFQDN'), '.', ',DC='))]"
Ensure = 'Present'
Enabled = $true
Password = @{
SecretName = 'SVC_SHP_Setup'
}
}
)
Trusts = @()
Dns = @{
Forwarders = @()
ReverseLookupZones = @()
}
}
}
}
}
}

View File

@@ -0,0 +1,200 @@
@{
Parameters = @{
DatabasePrefix = @{
Type = 'string'
Value = 'SharePoint'
DefaultValue = 'SharePoint'
Metadata = @{
Description = @{
'de-DE' = 'Praefix fuer alle von der SharePoint-Farm angelegten Datenbanken.'
'en-US' = 'Prefix for all databases created by the SharePoint farm.'
}
}
}
ServiceDatabaseSegment = @{
Type = 'string'
DefaultValue = 'Services'
Metadata = @{
Description = @{
'de-DE' = 'Namenssegment fuer SharePoint-Service-Datenbanken innerhalb des Datenbanknamens.'
'en-US' = 'Name segment used for SharePoint service databases within the database name.'
}
}
}
ContentDatabaseSegment = @{
Type = 'string'
DefaultValue = 'Content'
Metadata = @{
Description = @{
'de-DE' = 'Namenssegment fuer SharePoint-Content-Datenbanken innerhalb des Datenbanknamens.'
'en-US' = 'Name segment used for SharePoint content databases within the database name.'
}
}
}
ServiceApplicationPoolDefault = @{
Type = 'string'
DefaultValue = 'SharePoint Service Applications'
Metadata = @{
Description = @{
'de-DE' = 'Anzeigename des Standard-Application-Pools fuer SharePoint-Serviceanwendungen.'
'en-US' = 'Display name of the default application pool for SharePoint service applications.'
}
}
}
ServiceApplicationPoolUserProfileService = @{
Type = 'string'
DefaultValue = 'SharePoint User Profile Services'
Metadata = @{
Description = @{
'de-DE' = 'Anzeigename des Application-Pools fuer den SharePoint User Profile Service.'
'en-US' = 'Display name of the application pool for the SharePoint User Profile Service.'
}
}
}
ServiceApplicationPoolDefaultAccount = @{
Type = 'string'
DefaultValue = 'SVC_SHP_SAP'
Metadata = @{
Description = @{
'de-DE' = 'Kontoname fuer den Standard-Application-Pool der SharePoint-Serviceanwendungen.'
'en-US' = 'Account name used by the default application pool for SharePoint service applications.'
}
}
}
WebApplicationPoolDefault = @{
Type = 'string'
DefaultValue = 'SharePoint Web Applications'
Metadata = @{
Description = @{
'de-DE' = 'Anzeigename des Standard-Application-Pools fuer SharePoint-Webanwendungen.'
'en-US' = 'Display name of the default application pool for SharePoint web applications.'
}
}
}
WebApplicationPoolDefaultAccount = @{
Type = 'string'
DefaultValue = 'SVC_SHP_WAP'
Metadata = @{
Description = @{
'de-DE' = 'Kontoname fuer den Standard-Application-Pool der SharePoint-Webanwendungen.'
'en-US' = 'Account name used by the default application pool for SharePoint web applications.'
}
}
}
DatabaseInstanceName = @{
Type = 'string'
DefaultValue = 'SQL_Server'
Metadata = @{
Description = @{
'de-DE' = 'Standard-SQL-Instanzname fuer SharePoint-Datenbankverbindungen und Aliase.'
'en-US' = 'Default SQL instance name used for SharePoint database connections and aliases.'
}
}
}
}
Variables = @{
ServiceDbPrefix = "[concat(parameters('DatabasePrefix'),'_',parameters('ServiceDatabaseSegment'),'_')]"
ContentDbPrefix = "[concat(parameters('DatabasePrefix'),'_',parameters('ContentDatabaseSegment'),'_')]"
ConfigDbName = "[concat(parameters('DatabasePrefix'),'_','Farm_Config')]"
AdminDbName = "[concat(parameters('DatabasePrefix'),'_','Farm_AdminContent')]"
}
Resources = @{
NonNodeData = @{
Services = @{
SharePoint = @{
General = @{
ProductKey = '0000-0000-0000-0000-0000'
}
Windows = @{
Registry = @{
DisableLoopbackCheck = @{
Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa'
Name = 'DisableLoopbackCheck'
Type = 'DWord'
Value = 1
}
}
}
Database = @{
SQLAlias = @{
SQLServer = @{
ServerName = ''
InstanceName = "[parameters('DatabaseInstanceName')]"
TcpPort = 0
}
}
}
Farm = @{
Passphrase = 'Use-SecureString-Or-KeyVault'
ConfigDatabaseName = "[variables('ConfigDbName')]"
AdminContentDatabase = "[variables('AdminDbName')]"
CentralAdminPort = 2016
CentralAdminAuth = 'NTLM' # oder Kerberos
ServiceApplicationPools = @{
Default = @{
Name = "[parameters('ServiceApplicationPoolDefault')]"
Account = 'CONTOSO\sp_services'
}
UserProfile = @{
Name = "[parameters('ServiceApplicationPoolUserProfileService')]"
Account = 'CONTOSO\sp_ups'
}
}
ServiceApplications = @{
ManagedMetadataService = @{
Provision = $true
Name = 'Managed Metadata Service'
ApplicationPool = 'SharePoint Service Applications'
DatabaseName = "[concat(variables('ServiceDbPrefix'), 'ManagedMetadata')]"
}
UserProfileService = @{
Provision = $true
Name = 'User Profile Service'
ApplicationPool = 'SharePoint User Profile Services'
ProfileDBName = "[concat(variables('ServiceDbPrefix'), 'UserProfile_ProfileDB')]"
SocialDBName = "[concat(variables('ServiceDbPrefix'), 'UserProfile_SocialDB')]"
SyncDBName = "[concat(variables('ServiceDbPrefix'), 'UserProfile_SyncDB')]"
}
SearchService = @{
Provision = $true
Name = 'Search Service Application'
ApplicationPool = 'SharePoint Service Applications'
DatabaseName = "[concat(variables('ServiceDbPrefix'),'Search')]"
}
StateService = @{
Provision = $true
DatabaseName = "[concat(variables('ServiceDbPrefix'),'StateService')]"
}
UsageAndHealthService = @{
Provision = $true
DatabaseName = "[concat(variables('ServiceDbPrefix'),'UsageAndHealth')]"
}
AppManagementService = @{
Provision = $true
DatabaseName = "[concat(variables('ServiceDbPrefix'),'AppManagement')]"
}
SubscriptionSettingsService = @{
Provision = $true
DatabaseName = "[concat(variables('ServiceDbPrefix'),'SubscriptionSettings')]"
}
SecureStoreService = @{
Provision = $true
Name = 'Secure Store Service'
ApplicationPool = 'SharePoint Service Applications'
DatabaseName = "[concat(variables('ServiceDbPrefix'),'SecureStore')]"
}
}
}
}
}
}
}
}

View File

@@ -1,142 +0,0 @@
@{
Parameters = @{
DatabasePrefix = @{
Type = 'string'
Value = 'SharePoint'
DefaultValue = 'SP'
Metadata = @{
Description = 'Prefix für alle Datenbanknamen'
}
}
ServiceDatabasePrefix = @{
Type = 'string'
DefaultValue = 'Services'
}
ContentDatabasePrefix = @{
Type = 'string'
DefaultValue = 'Content'
}
ServiceApplicationPoolDefault = @{
Type = 'string'
DefaultValue = 'SharePoint Service Applications'
}
ServiceApplicationPoolUserProfileService = @{
Type = 'string'
DefaultValue = 'SharePoint User Profile Services'
}
DatbaseInstanceName = @{
Type = 'string'
DefaultValue = 'mssqlserver'
Metadata = @{
Description = 'Instanz Name für die Standart Instanz'
}
}
}
Variables = @{
ServiceDbPrefix = "[Concat(Parameter('DatabasePrefix'),'_',Parameter('ServiceDatabasePrefix'),'_')]"
ContentDbPrefix = "[Concat(Parameter('DatabasePrefix'),'_',Parameter('ServiceDatabasePrefix'),'_')]"
ConfigDbName = "[Concat(Parameter('DatabasePrefix'),'_','Farm_Config')]"
AdminDbName = "[Concat(Parameter('DatabasePrefix'),'_','Farm_AdminContent')]"
HZDTest = "[Substring(Parameter('DatbaseInstanceName'),0,5)]"
Test = "[ToUpper(Parameter('DatbaseInstanceName'),0,5)]"
}
Resources = @{
NonNodeData = @{
Services = @{
SharePoint = @{
General = @{
ProductKey = '0000-0000-0000-0000-0000'
}
Windows = @{
Registry = @{
DisableLoopbackCheck = @{
Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa'
Name = 'DisableLoopbackCheck'
Type = 'DWord'
Value = 1
}
}
}
Database = @{
SQLAlias = @{
SQLServer = @{
ServerName = ''
InstanceName = ''
TcpPort = 0
}
}
}
Farm = @{
Passphrase = 'Use-SecureString-Or-KeyVault'
ConfigDatabaseName = "[Variable('ConfigDbName')]"
AdminContentDatabase = "[Variable('AdminDbName')]"
CentralAdminPort = 2016
CentralAdminAuth = 'NTLM' # oder Kerberos
ServiceApplicationPools = @{
Default = @{
Name = "[Parameter('ServiceApplicationPoolDefault')]"
Account = 'CONTOSO\sp_services'
}
UserProfile = @{
Name = "[Parameter('ServiceApplicationPoolUserProfileService')]"
Account = 'CONTOSO\sp_ups'
}
}
ServiceApplications = @{
ManagedMetadataService = @{
Provision = $true
Name = 'Managed Metadata Service'
ApplicationPool = 'SharePoint Service Applications'
DatabaseName = "[Concat(Variable('ServiceDbPrefix'), 'ManagedMetadata')]"
}
UserProfileService = @{
Provision = $true
Name = 'User Profile Service'
ApplicationPool = 'SharePoint User Profile Services'
ProfileDBName = "[Concat(Variable('ServiceDbPrefix'), 'UserProfile_ProfileDB')]"
SocialDBName = "[Concat(Variable('ServiceDbPrefix'), 'UserProfile_SocialDB')]"
SyncDBName = "[Concat(Variable('ServiceDbPrefix'), 'UserProfile_SyncDB')]"
}
SearchService = @{
Provision = $true
Name = 'Search Service Application'
ApplicationPool = 'SharePoint Service Applications'
DatabaseName = "[Concat(Variable('ServiceDbPrefix'),'Search')]"
}
StateService = @{
Provision = $true
DatabaseName = "[Concat(Variable('ServiceDbPrefix'),'StateService')]"
}
UsageAndHealthService = @{
Provision = $true
DatabaseName = "[Concat(Variable('ServiceDbPrefix'),'UsageAndHealth')]"
}
AppManagementService = @{
Provision = $true
DatabaseName = "[Concat(Variable('ServiceDbPrefix'),'AppManagement')]"
}
SubscriptionSettingsService = @{
Provision = $true
DatabaseName = "[Concat(Variable('ServiceDbPrefix'),'SubscriptionSettings')]"
}
SecureStoreService = @{
Provision = $true
Name = 'Secure Store Service'
ApplicationPool = 'SharePoint Service Applications'
DatabaseName = "[Concat(Variable('ServiceDbPrefix'),'SecureStore')]"
}
}
}
}
}
}
}
}