# Apply-GPOBaseline.ps1 # Orchestration script for applying GPO settings to Active Directory. # # Reads settings.ps1 from each GPO directory, compares against the live # GPO in AD, and applies changes. Works from any machine with RSAT. # # Usage: # .\Apply-GPOBaseline.ps1 # Apply all GPO settings # .\Apply-GPOBaseline.ps1 -TestOnly # Compare desired vs. current (no changes) [CmdletBinding()] param( [switch]$TestOnly, [switch]$GpUpdate, [switch]$NoBackup, [switch]$NoCleanup ) $ErrorActionPreference = 'Stop' $ScriptRoot = $PSScriptRoot # ------------------------------------------------------------------- # Load helper functions # ------------------------------------------------------------------- . (Join-Path $ScriptRoot 'lib\GPOHelper.ps1') # ------------------------------------------------------------------- # Groups that should have edit rights on ALL managed GPOs. # This ensures Tier 0 operational admins can manage GPOs without # Domain Admins membership (DA = break-glass only). # ------------------------------------------------------------------- $ManagementGroups = @('MasterAdmins') # ------------------------------------------------------------------- # Discover GPO directories (each must contain a settings.ps1) # ------------------------------------------------------------------- $gpoDirs = Get-ChildItem -Path $ScriptRoot -Directory | Where-Object { Test-Path (Join-Path $_.FullName 'settings.ps1') } if ($gpoDirs.Count -eq 0) { Write-Host 'No GPO directories with settings.ps1 found.' -ForegroundColor Red exit 1 } Write-Host "Found $($gpoDirs.Count) GPO configuration(s):`n" -ForegroundColor Cyan $totalDiffs = 0 # ------------------------------------------------------------------- # Process each GPO # ------------------------------------------------------------------- foreach ($dir in $gpoDirs) { $settingsPath = Join-Path $dir.FullName 'settings.ps1' $settings = & $settingsPath $gpoName = $settings.GPOName Write-Host "=== $gpoName ===" -ForegroundColor White # Verify GPO exists in AD — create if missing try { $gpo = Get-GPO -Name $gpoName -ErrorAction Stop Write-Host " GPO found: {$($gpo.Id)}" -ForegroundColor Green } catch { if ($TestOnly) { Write-Host " [DRIFT] GPO '$gpoName' does not exist yet (will be created on apply)" -ForegroundColor Yellow $totalDiffs++ Write-Host '' continue } else { Write-Host " GPO '$gpoName' not found - creating..." -ForegroundColor Yellow $newGpoParams = @{ Name = $gpoName } if ($settings.Description) { $newGpoParams.Comment = $settings.Description } $gpo = New-GPO @newGpoParams Write-Host " [CREATED] GPO: $gpoName {$($gpo.Id)}" -ForegroundColor Green } } # --- GPO Description --- if ($settings.Description) { if ($TestOnly) { if ($gpo.Description -ne $settings.Description) { Write-Host " [DRIFT] Description: '$($gpo.Description)' -> '$($settings.Description)'" -ForegroundColor Red $totalDiffs++ } } else { if ($gpo.Description -ne $settings.Description) { $gpo.Description = $settings.Description Write-Host " [UPDATED] Description set" -ForegroundColor Yellow } } } # --- GPO Status (enabled/disabled sections) --- $hasStatusConfig = $settings.ContainsKey('DisableUserConfiguration') -or $settings.ContainsKey('DisableComputerConfiguration') if ($hasStatusConfig) { $disableUser = if ($settings.ContainsKey('DisableUserConfiguration')) { $settings.DisableUserConfiguration } else { $false } $disableComputer = if ($settings.ContainsKey('DisableComputerConfiguration')) { $settings.DisableComputerConfiguration } else { $false } if ($TestOnly) { $statusDiff = Compare-GPOStatus -GPOName $gpoName ` -DisableUserConfiguration $disableUser ` -DisableComputerConfiguration $disableComputer if ($statusDiff) { $totalDiffs++ } } else { Ensure-GPOStatus -GPOName $gpoName ` -DisableUserConfiguration $disableUser ` -DisableComputerConfiguration $disableComputer } } # --- Pre-Apply Backup --- if (-not $TestOnly -and -not $NoBackup) { try { Backup-GPOState -GPOName $gpoName | Out-Null } catch { Write-Host " [WARN] Backup failed: $($_.Exception.Message)" -ForegroundColor Yellow Write-Host " Continuing with apply..." -ForegroundColor Yellow } } # --- Management Permissions --- foreach ($mgmtGroup in $ManagementGroups) { if ($TestOnly) { $permDiff = Compare-GPOManagementPermission -GPOName $gpoName -GroupName $mgmtGroup if ($permDiff) { $totalDiffs++ } } else { Ensure-GPOManagementPermission -GPOName $gpoName -GroupName $mgmtGroup } } # --- Version scope tracking (for deferred bump at end of GPO) --- $bumpMachine = $false $bumpUser = $false # --- Restricted Groups -> merge into SecurityPolicy --- if ($settings.RestrictedGroups -and $settings.RestrictedGroups.Count -gt 0) { if ($settings.SecurityPolicy -and $settings.SecurityPolicy.ContainsKey('Group Membership')) { throw "GPO '$gpoName': Use RestrictedGroups OR SecurityPolicy['Group Membership'], not both." } $gmEntries = ConvertTo-RestrictedGroupEntries -RestrictedGroups $settings.RestrictedGroups if (-not $settings.SecurityPolicy) { $settings.SecurityPolicy = @{} } $settings.SecurityPolicy['Group Membership'] = $gmEntries } # --- Security Policy (GptTmpl.inf) --- if ($settings.SecurityPolicy -and $settings.SecurityPolicy.Count -gt 0) { if ($TestOnly) { Write-Host " Comparing security policy..." -ForegroundColor Yellow $diffs = Compare-GPOSecurityPolicy -GPOName $gpoName -SecurityPolicy $settings.SecurityPolicy if ($diffs.Count -eq 0) { Write-Host " [OK] Security policy matches desired state" -ForegroundColor Green } else { Write-Host " [DRIFT] $($diffs.Count) difference(s) found:" -ForegroundColor Red foreach ($diff in $diffs) { Write-Host " [$($diff.Section)] $($diff.Setting): '$($diff.Current)' -> '$($diff.Desired)'" -ForegroundColor Red } $totalDiffs += $diffs.Count } } else { Write-Host " Applying security policy..." -ForegroundColor Yellow Set-GPOSecurityPolicy -GPOName $gpoName -SecurityPolicy $settings.SecurityPolicy $bumpMachine = $true } } else { Write-Host " No security policy settings defined." -ForegroundColor DarkGray } # --- Registry Settings (Administrative Templates) --- # Note: Set-GPRegistryValue handles version bumping internally — no manual bump needed if ($settings.RegistrySettings -and $settings.RegistrySettings.Count -gt 0) { if ($TestOnly) { Write-Host " Comparing registry settings..." -ForegroundColor Yellow $regDiffs = Compare-GPORegistrySettings -GPOName $gpoName -RegistrySettings $settings.RegistrySettings if ($regDiffs.Count -eq 0) { Write-Host " [OK] Registry settings match desired state" -ForegroundColor Green } else { Write-Host " [DRIFT] $($regDiffs.Count) registry difference(s) found:" -ForegroundColor Red foreach ($diff in $regDiffs) { Write-Host " $($diff.Key)\$($diff.ValueName): '$($diff.Current)' -> '$($diff.Desired)'" -ForegroundColor Red } $totalDiffs += $regDiffs.Count } } else { Write-Host " Applying registry settings..." -ForegroundColor Yellow Set-GPORegistrySettings -GPOName $gpoName -RegistrySettings $settings.RegistrySettings ` -Cleanup:(-not $NoCleanup) } } # --- GPO Link(s) --- if ($settings.LinkTo) { $links = Normalize-GPOLinkTo -LinkTo $settings.LinkTo foreach ($link in $links) { if ($TestOnly) { $linkDiffs = Compare-GPOLink -GPOName $gpoName -TargetOU $link.Target ` -Order $link.Order -Enforced $link.Enforced $totalDiffs += @($linkDiffs).Count } else { Ensure-GPOLink -GPOName $gpoName -TargetOU $link.Target ` -Order $link.Order -Enforced $link.Enforced } } } # --- Security Filtering (Deny Apply) --- if ($settings.SecurityFiltering -and $settings.SecurityFiltering.DenyApply) { if ($TestOnly) { Write-Host " Checking security filtering..." -ForegroundColor Yellow $filterDiffs = Compare-GPOSecurityFiltering -GPOName $gpoName -DenyApply $settings.SecurityFiltering.DenyApply $totalDiffs += @($filterDiffs).Count } else { Ensure-GPOSecurityFiltering -GPOName $gpoName -DenyApply $settings.SecurityFiltering.DenyApply } } # --- WMI Filter --- if ($settings.WMIFilter) { $wmiDesc = if ($settings.WMIFilter.Description) { $settings.WMIFilter.Description } else { '' } if ($TestOnly) { $wmiDiffs = Compare-GPOWmiFilter -GPOName $gpoName -FilterName $settings.WMIFilter.Name ` -Description $wmiDesc -Query $settings.WMIFilter.Query $totalDiffs += @($wmiDiffs).Count } else { Ensure-GPOWmiFilter -GPOName $gpoName -FilterName $settings.WMIFilter.Name ` -Description $wmiDesc -Query $settings.WMIFilter.Query } } # --- Scripts (Startup / Shutdown / Logon / Logoff) --- if ($settings.Scripts) { if ($TestOnly) { $scriptDiffs = Compare-GPOScripts -GPOName $gpoName -Scripts $settings.Scripts -SourceDir $dir.FullName $totalDiffs += @($scriptDiffs).Count } else { Set-GPOScripts -GPOName $gpoName -Scripts $settings.Scripts -SourceDir $dir.FullName foreach ($type in $settings.Scripts.Keys) { if ($type -like 'Machine*') { $bumpMachine = $true } if ($type -like 'User*') { $bumpUser = $true } } } } # --- Advanced Audit Policy (audit.csv) --- if ($settings.AdvancedAuditPolicy -and $settings.AdvancedAuditPolicy.Count -gt 0) { if ($TestOnly) { $auditDiffs = Compare-GPOAdvancedAuditPolicy -GPOName $gpoName -AuditPolicy $settings.AdvancedAuditPolicy $totalDiffs += @($auditDiffs).Count } else { Set-GPOAdvancedAuditPolicy -GPOName $gpoName -AuditPolicy $settings.AdvancedAuditPolicy $bumpMachine = $true } } # --- Group Policy Preferences --- if ($settings.Preferences) { if ($TestOnly) { $prefDiffs = Compare-GPOPreferences -GPOName $gpoName -Preferences $settings.Preferences $totalDiffs += @($prefDiffs).Count } else { Set-GPOPreferences -GPOName $gpoName -Preferences $settings.Preferences foreach ($typeName in $settings.Preferences.Keys) { switch ($typeName) { 'DriveMaps' { $bumpUser = $true } 'Printers' { $bumpUser = $true } 'Services' { $bumpMachine = $true } 'NetworkShares' { $bumpMachine = $true } 'LocalUsersAndGroups' { $bumpMachine = $true } default { foreach ($item in $settings.Preferences[$typeName]) { if ($item.Scope -eq 'User') { $bumpUser = $true } else { $bumpMachine = $true } } } } } } } # --- Firewall Profiles --- if ($settings.FirewallProfiles) { if ($TestOnly) { $fwProfileDiffs = Compare-GPOFirewallProfiles -GPOName $gpoName -FirewallProfiles $settings.FirewallProfiles $totalDiffs += @($fwProfileDiffs).Count } else { Set-GPOFirewallProfiles -GPOName $gpoName -FirewallProfiles $settings.FirewallProfiles } } # --- Firewall Rules --- if ($settings.FirewallRules -and $settings.FirewallRules.Count -gt 0) { if ($TestOnly) { $fwDiffs = Compare-GPOFirewall -GPOName $gpoName -FirewallRules $settings.FirewallRules $totalDiffs += @($fwDiffs).Count } else { Set-GPOFirewall -GPOName $gpoName -FirewallRules $settings.FirewallRules } } # --- AppLocker Policy --- if ($settings.AppLockerPolicy) { if ($TestOnly) { $alDiffs = Compare-GPOAppLockerPolicy -GPOName $gpoName -AppLockerPolicy $settings.AppLockerPolicy $totalDiffs += @($alDiffs).Count } else { Set-GPOAppLockerPolicy -GPOName $gpoName -AppLockerPolicy $settings.AppLockerPolicy } } # --- WDAC Policy --- if ($settings.WDACPolicy) { if ($TestOnly) { $wdacDiffs = Compare-GPOWdacPolicy -GPOName $gpoName -WDACPolicy $settings.WDACPolicy -SourceDir $dir.FullName $totalDiffs += @($wdacDiffs).Count } else { Set-GPOWdacPolicy -GPOName $gpoName -WDACPolicy $settings.WDACPolicy -SourceDir $dir.FullName $bumpMachine = $true } } # --- Folder Redirection --- if ($settings.FolderRedirection) { if ($TestOnly) { $frDiffs = Compare-GPOFolderRedirection -GPOName $gpoName -FolderRedirection $settings.FolderRedirection $totalDiffs += @($frDiffs).Count } else { Set-GPOFolderRedirection -GPOName $gpoName -FolderRedirection $settings.FolderRedirection $bumpUser = $true } } # --- Deferred Version Bump (single bump per GPO, scope-aware) --- if (-not $TestOnly -and ($bumpMachine -or $bumpUser)) { $scope = if ($bumpMachine -and $bumpUser) { 'Both' } elseif ($bumpMachine) { 'Machine' } else { 'User' } Update-GPOVersion -GPOName $gpoName -Scope $scope Write-Host " GPO version bumped ($scope)." -ForegroundColor Green } Write-Host '' } # ------------------------------------------------------------------- # Summary # ------------------------------------------------------------------- if ($TestOnly) { if ($totalDiffs -eq 0) { Write-Host 'All GPO settings match desired state.' -ForegroundColor Green } else { Write-Host "$totalDiffs difference(s) found." -ForegroundColor Red } Write-Host 'Compliance test complete. No changes were made.' -ForegroundColor Yellow } else { Write-Host 'All GPO settings applied.' -ForegroundColor Green } # ------------------------------------------------------------------- # Optional: Trigger group policy refresh # ------------------------------------------------------------------- if ($GpUpdate -and -not $TestOnly) { Write-Host '' Write-Host 'Running gpupdate /force to process updated policies...' -ForegroundColor Cyan $gpResult = gpupdate /force 2>&1 Write-Host ($gpResult | Out-String) -ForegroundColor Gray Write-Host 'Group Policy refresh complete.' -ForegroundColor Green } elseif (-not $TestOnly) { Write-Host '' Write-Host 'TIP: Run with -GpUpdate to trigger gpupdate /force after applying.' -ForegroundColor DarkGray Write-Host ' Or manually: gpupdate /force' -ForegroundColor DarkGray }