P03-T05 - Workstation Hardening GPO
Objective
Create a separate workstation-hardening GPO, keep the day-zero baseline small, and prove the effective settings and service connectivity from a real domain workstation.
Validated on Saturday, July 18, 2026 against HQ-DC01 and HQ-CL01.
Inputs
| Key | Validated value | Configurable parameter |
|---|---|---|
| GPO | GPO-Workstations-Hardening |
GpoName |
| Link target | OU=Workstations,OU=GNTECH,DC=corp,DC=gntech,DC=me |
WorkstationsOuDn |
| Transcript path | C:\ProgramData\PSLogs |
TranscriptionPath |
Target State
| Control | Required value |
|---|---|
| LLMNR | Disabled (EnableMulticast=0) |
| PowerShell module logging | Enabled |
| PowerShell transcription | Enabled with invocation header |
| SmartScreen | Enabled / Warn |
| Defender | Antivirus, real-time protection, and IOAV remain enabled |
The following remain outside this first hardening layer: ASR, BitLocker, LAPS, NTLM reduction, firewall redesign, and local administrator governance.
Prechecks
- Complete
P03-T01throughP03-T04. - Confirm
GPO-Workstations-Baselineapplies successfully. - Keep at least one real joined workstation available for effective-policy testing.
- Run the script directly on
HQ-DC01from elevated PowerShell.
Current State Verification
Import-Module GroupPolicy
Get-GPO -Name 'GPO-Workstations-Hardening' -ErrorAction SilentlyContinue
Get-GPInheritance -Target 'OU=Workstations,OU=GNTECH,DC=corp,DC=gntech,DC=me'
Execution
Show full workstation-hardening GPO script
[CmdletBinding()]
param(
[string]$WorkstationsOuDn = 'OU=Workstations,OU=GNTECH,DC=corp,DC=gntech,DC=me',
[string]$GpoName = 'GPO-Workstations-Hardening',
[string]$TranscriptionPath = 'C:\ProgramData\PSLogs',
[switch]$ValidateOnly
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Assert-Elevated {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this script from an elevated PowerShell session.'
}
}
function Ensure-GpoLink {
$links = @((Get-GPInheritance -Target $WorkstationsOuDn).GpoLinks | Where-Object DisplayName -eq $GpoName)
if ($links.Count -eq 0) {
New-GPLink -Name $GpoName -Target $WorkstationsOuDn -LinkEnabled Yes | Out-Null
return 'Created'
}
if (-not $links[0].Enabled) {
Set-GPLink -Name $GpoName -Target $WorkstationsOuDn -LinkEnabled Yes | Out-Null
return 'Enabled'
}
return 'Compliant'
}
Assert-Elevated
Import-Module ActiveDirectory
Import-Module GroupPolicy
Get-ADOrganizationalUnit -Identity $WorkstationsOuDn -ErrorAction Stop | Out-Null
$settings = @(
@{ Key='HKLM\Software\Policies\Microsoft\Windows NT\DNSClient'; Name='EnableMulticast'; Type='DWord'; Value=0 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging'; Name='EnableModuleLogging'; Type='DWord'; Value=1 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription'; Name='EnableTranscripting'; Type='DWord'; Value=1 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription'; Name='EnableInvocationHeader'; Type='DWord'; Value=1 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription'; Name='OutputDirectory'; Type='String'; Value=$TranscriptionPath },
@{ Key='HKLM\Software\Policies\Microsoft\Windows\System'; Name='EnableSmartScreen'; Type='DWord'; Value=1 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows\System'; Name='ShellSmartScreenLevel'; Type='String'; Value='Warn' },
@{ Key='HKLM\Software\Policies\Microsoft\Windows Defender'; Name='DisableAntiSpyware'; Type='DWord'; Value=0 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows Defender\Real-Time Protection'; Name='DisableRealtimeMonitoring'; Type='DWord'; Value=0 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows Defender\Spynet'; Name='SpynetReporting'; Type='DWord'; Value=2 },
@{ Key='HKLM\Software\Policies\Microsoft\Windows Defender\Spynet'; Name='SubmitSamplesConsent'; Type='DWord'; Value=1 }
)
$gpo = Get-GPO -Name $GpoName -ErrorAction SilentlyContinue
if (-not $gpo -and $ValidateOnly) {
Write-Output "GPO: Missing / Name=$GpoName"
Write-Output 'Validation mode only. No GPO changes were applied.'
return
}
if (-not $gpo) {
$gpo = New-GPO -Name $GpoName -Comment 'Validated workstation hardening controls'
}
if ($ValidateOnly) {
$link = @((Get-GPInheritance -Target $WorkstationsOuDn).GpoLinks | Where-Object DisplayName -eq $GpoName)
Write-Output "GPO: Present / Id=$($gpo.Id)"
Write-Output "Link: $(if ($link.Count -gt 0 -and $link[0].Enabled) { 'Enabled' } else { 'MissingOrDisabled' })"
foreach ($setting in $settings) {
$current = Get-GPRegistryValue -Name $GpoName -Key $setting.Key -ValueName $setting.Name -ErrorAction SilentlyContinue
[pscustomobject]@{
Key = $setting.Key
ValueName = $setting.Name
Expected = $setting.Value
Current = if ($current) { $current.Value } else { $null }
Compliant = [bool]($current -and $current.Value -eq $setting.Value)
}
}
Write-Output 'Validation mode only. No GPO changes were applied.'
return
}
$linkAction = Ensure-GpoLink
foreach ($setting in $settings) {
Set-GPRegistryValue -Name $GpoName -Key $setting.Key -ValueName $setting.Name -Type $setting.Type -Value $setting.Value | Out-Null
}
Write-Output "GPO: $GpoName"
Write-Output "Link: $linkAction"
Write-Output "SettingsApplied: $($settings.Count)"
Write-Output 'Workstation hardening GPO completed successfully.'
Preview without changing AD:
Apply or reconcile the GPO and its link:
The normal run is idempotent. The validated re-run reported an existing compliant link and reapplied all 11 desired registry settings.
Validation
On the workstation:
gpupdate /force
gpresult /scope computer /r
Get-ItemProperty 'HKLM:\Software\Policies\Microsoft\Windows NT\DNSClient'
Get-ItemProperty 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging'
Get-ItemProperty 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\Transcription'
Get-ItemProperty 'HKLM:\Software\Policies\Microsoft\Windows\System'
Get-MpComputerStatus | Select-Object AMRunningMode,AntivirusEnabled,RealTimeProtectionEnabled,IoavProtectionEnabled
Test-NetConnection hq-dc01.corp.gntech.me -Port 389
Test-NetConnection hq-fs01.corp.gntech.me -Port 445
Test-NetConnection github.com -Port 443
Validated result on HQ-CL01:
- both
GPO-Workstations-BaselineandGPO-Workstations-Hardeningapplied - LLMNR
0, module logging1, transcription1, SmartScreen1 - Defender
Normal, antivirus enabled, real-time protection enabled, IOAV enabled - LDAP 389, SMB 445, and external HTTPS 443 succeeded
- domain membership, site discovery, DNS, SMB, and Internet access showed no regression
Evidence
- server-side script
-ValidateOnlyshowing all 11 settings compliant gpresult /scope computer /rfromHQ-CL01- effective registry and Defender status
- successful LDAP, SMB, and HTTPS tests
Rollback
- Disable or remove the link before deleting the GPO.
- Prefer reverting one problematic registry setting and retesting over deleting the entire layer.
- Do not merge emergency exceptions into
GPO-Workstations-Baseline.