PowerShellNew

Invoke-WindowsHardening

Applies a configurable subset of CIS Level 1 and Level 2 controls to Windows 10/11 endpoints. Runs locally or via Intune remediation script. Generates a pre/post compliance delta report.

CISHardeningSecurityComplianceReal-world script

Overview

Invoke-WindowsHardening applies a configurable subset of CIS Level 1 and Level 2 controls to Windows 10/11 endpoints. It runs locally or via Intune remediation script and generates a pre/post compliance delta report showing exactly what changed and what was already compliant.

The script is designed for environments that need to apply hardening without a full GPO infrastructure, or as a validation and gap-fill tool alongside existing Group Policy baselines.

Tested environment: Windows 11 22H2 and 24H2, Windows 10 22H2. PowerShell 5.1 and PowerShell 7.4. Local and SYSTEM execution contexts. Tested against CIS Windows 11 Benchmark v3.0.0.

Safety First: Always Audit Before Applying

Important: This script modifies registry values, security policy settings, and Windows Defender configuration. Run -Mode Audit on a pilot machine first and review the delta report before running -Mode Apply in any production environment. Some controls — particularly NTLM restrictions and BitLocker settings — can break legacy applications and require a reboot to take effect.

The recommended sequence is:

  1. Run Invoke-WindowsHardening -Mode Audit on a representative pilot machine
  2. Review the HTML report and identify any controls likely to conflict with your environment
  3. Use -ExcludeCategories to exclude those controls
  4. Run Invoke-WindowsHardening -Mode Apply on the pilot machine
  5. Test all critical applications on the pilot machine
  6. Only then deploy via Intune remediation to a broader scope

Prerequisites

  • OS: Windows 10 22H2 / Windows 11 22H2 or later
  • PowerShell: 5.1 minimum; PowerShell 7 recommended for parallel execution and better error handling
  • Execution context: Local Administrator or SYSTEM. Many controls require elevated access to write HKLM registry paths and configure security policy.
  • Execution policy: The script must be unblocked or your execution policy must permit local scripts:
PowerShell
# Confirm execution policy before running
Get-ExecutionPolicy -List

# If RemoteSigned or AllSigned, unblock the script file:
Unblock-File -Path .\Invoke-WindowsHardening.ps1

Parameters

ParameterTypeDescription
-Mode[Audit|Apply]Audit reports gaps without making changes. Apply enforces settings. Default: Audit
-Level[CIS1|CIS2|Custom]CIS Level 1, Level 2, or custom control profile. Default: CIS1
-ReportPath[string]Full path for the HTML report file. Default: current directory
-ExcludeCategories[string[]]Skip specific control categories by name (see category list below)
-WhatIf[switch]Show what -Mode Apply would change, without writing anything

Usage Examples

Audit mode — generate compliance gap report without any changes:

PowerShell
Invoke-WindowsHardening -Mode Audit -Level CIS1 -ReportPath C:\Temp\HardeningAudit.html

Preview what Apply mode would change (safe — no writes):

PowerShell
Invoke-WindowsHardening -Mode Apply -Level CIS1 -WhatIf

Apply CIS Level 1 controls:

PowerShell
Invoke-WindowsHardening -Mode Apply -Level CIS1 -ReportPath C:\Temp\HardeningApplied.html

Apply hardening excluding BitLocker and Firewall categories (useful if managed separately):

PowerShell
Invoke-WindowsHardening -Mode Apply -Level CIS1 -ExcludeCategories @('BitLocker', 'WindowsFirewall') `
    -ReportPath C:\Temp\HardeningApplied.html

Run as Intune remediation script (SYSTEM context):

PowerShell
# Detection script — check for a recent report file
$ReportDir  = 'C:\ProgramData\AdminSignal'
$Threshold  = (Get-Date).AddDays(-30)
$Report     = Get-ChildItem -Path $ReportDir -Filter 'HardeningReport-*.html' -ErrorAction SilentlyContinue |
              Sort-Object LastWriteTime -Descending | Select-Object -First 1

if ($Report -and $Report.LastWriteTime -gt $Threshold) {
    Write-Output 'Compliant: recent hardening report found.'
    exit 0
} else {
    Write-Output 'Non-compliant: no recent hardening report.'
    exit 1
}
PowerShell
# Remediation script — apply and record
$ReportPath = "C:\ProgramData\AdminSignal\HardeningReport-$(Get-Date -Format yyyyMMdd).html"
New-Item -ItemType Directory -Path (Split-Path $ReportPath) -Force | Out-Null
Invoke-WindowsHardening -Mode Apply -Level CIS1 -ReportPath $ReportPath

What it Applies

Each category can be individually excluded via -ExcludeCategories:

CategoryControls Applied
AccountPoliciesPassword length (14+), lockout threshold (5), lockout duration
AuditPolicyLogon, account management, policy change, process creation events
WindowsFirewallEnable all three profiles (Domain/Private/Public), default inbound block
NetworkSecurityLM authentication level (NTLMv2 only), NTLM session security, SMBv1 disable
UserRightsAssignmentDeny network logon to built-in accounts, restrict debug privilege
SecurityOptionsUAC prompt settings, NTLMv2 enforcement, interactive logon messages
WindowsDefenderReal-time protection on, PUA blocking, cloud protection level
BitLockerRequire TPM, enable OS drive encryption
PowerShellLoggingScript block logging, module logging, transcription

Report Output

The HTML report includes:

  • Pre-hardening state: The actual value of each setting before any changes
  • Post-hardening state: The value after the script ran (Apply mode only; Audit shows current state in both columns)
  • Compliance status: Pass / Fail / Skipped per control
  • Summary table: Overall compliance percentage, count of passed and failed controls
  • Skipped controls: Controls excluded via -ExcludeCategories or marked as Intune-required

Validation

After running in Apply mode, verify key controls took effect:

PowerShell
# Verify NTLMv2-only enforcement (should return 5)
(Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa').LmCompatibilityLevel

# Verify Script Block Logging is enabled (should return 1)
(Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' `
    -ErrorAction SilentlyContinue).EnableScriptBlockLogging

# Verify SMBv1 is disabled (should return False)
(Get-SmbServerConfiguration).EnableSMB1Protocol

# Verify Windows Firewall Domain profile is enabled (should return True)
(Get-NetFirewallProfile -Profile Domain).Enabled

If any of these return unexpected values, re-check whether a competing Group Policy or Intune profile is overriding the settings.

Common Errors

Access to the path is denied / Set-ItemProperty: Cannot write to HKLM The script is not running as Administrator or SYSTEM. Right-click PowerShell and select "Run as administrator", or confirm the Intune remediation script is set to run in SYSTEM context.

The term 'secedit' is not recognized Security policy settings (Account Policies, User Rights Assignment) use secedit.exe. This is a Windows built-in — if it is missing, the Windows installation may be corrupted. Verify via where.exe secedit.

Report shows controls as Fail after Apply Some controls require a reboot to take full effect, particularly BitLocker initialisation and some SecurityOptions values. Reboot the machine and re-run in Audit mode to confirm.

LOB application breaks after Apply The most common culprits are NetworkSecurity (NTLMv2 enforcement) and WindowsFirewall (inbound block rules). Re-run with -ExcludeCategories @('NetworkSecurity') or -ExcludeCategories @('WindowsFirewall') to identify which category is causing the conflict.

Rollback Procedure

The script does not currently create an automatic system restore point. Before running Apply on a new machine class, create a manual checkpoint:

PowerShell
# Create a system restore point before hardening (requires System Protection enabled)
Checkpoint-Computer -Description "Pre-CIS-Hardening $(Get-Date -Format yyyy-MM-dd)" `
    -RestorePointType MODIFY_SETTINGS

Per-category rollback: The HTML report records the pre-hardening value of every modified setting. Use those values to manually revert specific controls. For registry-based controls:

PowerShell
# Example: revert LM Compatibility Level to its previous value (e.g. 3)
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' `
    -Name LmCompatibilityLevel -Value 3

BitLocker: Once BitLocker encryption begins, it cannot be reversed by removing the control — decryption requires Disable-BitLocker and takes time proportional to drive size. Do not include BitLocker in an Apply run on machines where full-disk decryption is not acceptable.

Security Notes

  • Audit mode is completely safe — it reads values and writes a report. No system state is changed.
  • Apply mode makes persistent changes to registry, security policy, and Windows Defender configuration. Test on a pilot machine before broad deployment.
  • The report file contains security configuration details of the endpoint. Store it in a path accessible only to IT administrators, not in a user-accessible location.
  • Running as SYSTEM via Intune remediation is the intended production path. The script does not require network access or cloud connectivity unless querying external telemetry (which it does not by default).

Known Limitations

  • Intune-required controls: Tamper Protection and some Defender settings require an Intune-enrolled device and an active Intune security configuration policy. These cannot be set via registry or PowerShell on a co-managed or standalone device. They are flagged as Intune-required in the report.
  • Reboot required for some controls: BitLocker encryption start, several SecurityOptions values, and PowerShell logging transcription paths do not take effect until the next reboot.
  • Application conflicts: NTLM restrictions can break older LOB applications that use Basic authentication or NTLM-only connections to file shares. Test with your critical application set before broad deployment. See the CIS Benchmark hardening guide for a detailed conflict table.
  • Level 2 controls: CIS Level 2 includes additional restrictions (e.g., disabling the Remote Registry service, restricting PowerShell execution more aggressively) that are more disruptive in general enterprise environments. Use Level 2 only after thorough testing.

Senior Enterprise Sysadmin · 12+ Years Windows & Intune

I've spent 12+ years managing Windows fleets, Intune tenants, and Active Directory environments for enterprise clients across finance, logistics, and professional services. AdminSignal exists because I got tired of docs that stop at "click Apply." Everything here is tested in production before it goes on the page.

AdminSignal content is produced independently. Editorial policy