202 lines
8.9 KiB
PowerShell
202 lines
8.9 KiB
PowerShell
#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")"
|
|
}
|
|
}
|