#Requires -Modules 'powershell-yaml' Param( [Parameter(Mandatory)] [hashtable]$Parameter ) # Only executed on primary or standalone Domain Controller If (@('primary','standalone') -contains $Parameter['deployment.type']) { $NewPSSessionSplat = @{ ComputerName = $Parameter['guestinfo.hostname'] Credential = New-Object System.Management.Automation.PSCredential( (Get-ADUser -Filter * | Where-Object {$_.SID -match '-500'}).SamAccountName, (ConvertTo-SecureString $Parameter['addsconfig.administratorpw'] -AsPlainText -Force) ) } $PSSession = New-PSSession @NewPSSessionSplat $ParseErrors = @() $GetItemSplat = @{ Path = "$($PSScriptRoot)\$($MyInvocation.MyCommand)".Replace('.ps1', '.*.yml') } ForEach ($File in (Get-Item @GetItemSplat)) { Try { Write-Host "Loading/parsing file '$($File)' ..." $GetContentSplat = @{ Path = $File Raw = $True } $RawContent = Get-Content @GetContentSplat $ConvertFromYamlSplat = @{ Yaml = $RawContent AllDocuments = $True } $YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat } Catch { $ParseErrors += "While processing '$($File)': $($_.Exception.Message)" Continue } # Check if the respective .yml file declared substitutions which need to be parsed If (($YamlDocuments.Count -gt 1) -and $YamlDocuments[-1].Variables) { Try { ForEach ($Pattern in $YamlDocuments[-1].Variables) { $RawContent = $RawContent -replace "\{\{ ($($Pattern.Name)) \}\}", [string](Invoke-Expression -Command $Pattern.Expression) } # Perform conversion to Yaml again, now with parsed file contents $ConvertFromYamlSplat = @{ Yaml = $RawContent AllDocuments = $True } $YamlDocuments = ConvertFrom-Yaml @ConvertFromYamlSplat } Catch { $ParseErrors += "While processing '$($File)' (after substitutions): $($_.Exception.Message)" Continue } $GroupPolicies = $YamlDocuments[0..($YamlDocuments.Count - 2)] } Else { $GroupPolicies = $YamlDocuments } ForEach ($GroupPolicy in $GroupPolicies) { Write-Host "Initiating policy '$($GroupPolicy.Name)' ..." $NewGPOSplat = @{ Name = $GroupPolicy.Name ErrorAction = 'SilentlyContinue' ErrorVariable = 'Failure' } $NewGPO = New-GPO @NewGPOSplat If ($Failure) { Continue } Switch ($GroupPolicy.Type) { 'Object' { ForEach ($ValueSet in $GroupPolicy.RegistryEntries) { Write-Host "Adding key/value to policy '$($NewGPO.DisplayName)' ...`n [$($ValueSet.Key)/$($ValueSet.ValueName)]" $SetGPRegistryValueSplat = @{ Name = $NewGPO.DisplayName Key = $ValueSet.Key ValueName = $ValueSet.ValueName Type = $ValueSet.Type Value = Switch ($ValueSet.Type) { 'Binary' { # Accepted formats: # 000A0F0100 # 00 0A 0F 01 00 # 00,0A,0F,01,00 [byte[]]([regex]::split(($ValueSet.Value -replace '[ ,]'), '([0-9a-eA-E]{2})') | Where-Object {$_} | ForEach-Object {'0x{0}' -f $_}) } 'DWord' { [uint32]$ValueSet.Value } 'QWord' { [uint64]$ValueSet.Value } Default { $ValueSet.Value } } ErrorAction = 'SilentlyContinue' } Set-GPRegistryValue @SetGPRegistryValueSplat | Out-Null } } 'Preference' { ForEach ($ValueSet in $GroupPolicy.RegistryEntries) { Write-Host "Adding key/value to policy '$($NewGPO.DisplayName)' ...`n [$($ValueSet.Key)/$($ValueSet.ValueName)]" $SetGPPrefRegistryValueSplat = @{ Name = $NewGPO.DisplayName Key = $ValueSet.Key Context = $ValueSet.Context Action = $ValueSet.Action ValueName = $ValueSet.ValueName Type = $ValueSet.Type Value = Switch ($ValueSet.Type) { 'Binary' { # Accepted formats: # 000A0F0100 # 00 0A 0F 01 00 # 00,0A,0F,01,00 [byte[]]([regex]::split(($ValueSet.Value -replace '[ ,]'), '([0-9a-eA-E]{2})') | Where-Object {$_} | ForEach-Object {'0x{0}' -f $_}) } 'DWord' { [uint32]$ValueSet.Value } 'QWord' { [uint64]$ValueSet.Value } Default { $ValueSet.Value } } Disable = [Convert]::ToBoolean($ValueSet.Disable) ErrorAction = 'SilentlyContinue' } Set-GPPrefRegistryValue @SetGPPrefRegistryValueSplat | Out-Null } } } ForEach ($Filter in $GroupPolicy.WMIFilters) { $InvokeCommandSplat = @{ Session = $PSSession ArgumentList = $Filter, $Parameter, $NewGPO ScriptBlock = { #Requires -Modules 'GPWmiFilter' Param( $Filter, $Parameter, $NewGPO ) $GetGPWmiFilterSplat = @{ Name = $Filter Server = $Parameter['addsconfig.domainname'] ErrorAction = 'SilentlyContinue' } If (Get-GPWMIFilter @GetGPWmiFilterSplat) { $SetGPWmiFilterAssignmentSplat = @{ Policy = $NewGPO Filter = $Filter EnableException = $True ErrorAction = 'SilentlyContinue' } Set-GPWmiFilterAssignment @SetGPWmiFilterAssignmentSplat } } } Invoke-Command @InvokeCommandSplat } ForEach ($OU in $GroupPolicy.LinkedOUs) { If (Test-Path "AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))") { Try { Write-Host "Linking policy '$($NewGPO.DisplayName)' to OU '$($OU)' ..." $NewGPLinkSplat = @{ Name = $NewGPO.DisplayName Target = $OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext) } New-GPLink @NewGPLinkSplat | Out-Null } Catch { $ParseErrors += "Could not link GPO '$($NewGPO.DisplayName)' to OU '$($OU)'" Continue } } Else { $ParseErrors += "Path not accessible (referred to by '$($NewGPO.DisplayName)'): 'AD:\$($OU + (',{0}' -f (Get-ADRootDSE).rootDomainNamingContext))'" Continue } } } } If ($ParseErrors) { Throw "One or more errors occurred:`n$($ParseErrors -join "`n")" } }