# ============================================================================ # Entra Connect Pre-flight Remediation - CS-SERVER # ---------------------------------------------------------------------------- # Applies the three items from the 2026-04-22 readiness check: # 1. Time sync (w32tm) - immediate, no reboot # 2. TLS 1.2 enforcement (.NET + Schannel) - requires reboot # 3. Install Windows Server Backup feature - no reboot # # Then schedules a reboot at 18:00 local time (Arizona) for Schannel changes # to take effect. Snapshot of all pre-change state is written to # D:\Backups\pre-entra-connect-\ for rollback. # # To cancel the scheduled reboot at any time before 18:00: shutdown /a # ---------------------------------------------------------------------------- # Prepared: 2026-04-22 # Source: docs/migration/scripts/entra-connect-preflight-remediation.ps1 # ============================================================================ $ErrorActionPreference = 'Continue' $TargetRebootHour = 18 # 6 PM local (Arizona) function Section($n) { Write-Output '' Write-Output ('=' * 72) Write-Output "== $n" Write-Output ('=' * 72) } function Status($ok, $msg) { $tag = if ($ok) { '[OK] ' } else { '[FAIL]' } Write-Output "$tag $msg" } # ---------------------------------------------------------------------------- Section '0. Pre-flight snapshot (rollback material)' # ---------------------------------------------------------------------------- $ts = Get-Date -Format 'yyyy-MM-dd-HHmm' $backupDir = "D:\Backups\pre-entra-connect-$ts" try { New-Item -Path $backupDir -ItemType Directory -Force | Out-Null Status $true "Backup dir: $backupDir" } catch { Status $false "Could not create $backupDir - $_" exit 1 } # Export registry keys we're about to modify reg export "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319" "$backupDir\dotnet-64.reg" /y 2>&1 | Out-Null reg export "HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319" "$backupDir\dotnet-32.reg" /y 2>&1 | Out-Null reg export "HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" "$backupDir\schannel-protocols-pre.reg" /y 2>&1 | Out-Null Status $true 'Registry pre-state exported (.NET + Schannel Protocols)' # Snapshot w32tm state w32tm /query /configuration 2>&1 | Out-File "$backupDir\w32tm-config-pre.txt" w32tm /query /status 2>&1 | Out-File "$backupDir\w32tm-status-pre.txt" w32tm /query /peers 2>&1 | Out-File "$backupDir\w32tm-peers-pre.txt" Status $true 'w32tm pre-state captured' # Snapshot recent events (for post-reboot comparison) try { Get-WinEvent -LogName System -MaxEvents 200 -ErrorAction Stop | Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, Message | Export-Csv "$backupDir\events-system-pre.csv" -NoTypeInformation Status $true "Event log snapshot saved (200 System events)" } catch { Status $false "Event log snapshot: $_" } Write-Output '' Write-Output ("To roll back TLS changes after reboot: " + "`nreg import $backupDir\dotnet-64.reg `nreg import $backupDir\dotnet-32.reg " + "`nreg import $backupDir\schannel-protocols-pre.reg `nRestart-Computer") # ---------------------------------------------------------------------------- Section '1. Time sync fix (w32tm) - no reboot' # ---------------------------------------------------------------------------- try { # PDC emulator should be an authoritative time source + sync from external pool $ntpPeers = 'time.windows.com,0x8 pool.ntp.org,0x8 time.nist.gov,0x8' & w32tm /config /manualpeerlist:$ntpPeers /syncfromflags:manual /reliable:yes /update | Out-Null Status $true "Configured w32tm peer list: $ntpPeers" Restart-Service w32time -Force Status $true 'Restarted w32time service' Start-Sleep 4 & w32tm /resync /force 2>&1 | Out-Null Start-Sleep 3 $statusOut = & w32tm /query /status 2>&1 $statusOut | Out-File "$backupDir\w32tm-status-post.txt" $statusOut | Select-Object -First 20 | ForEach-Object { Write-Output " $_" } $refIdLine = $statusOut | Where-Object { $_ -match 'ReferenceId' } | Select-Object -First 1 if ($refIdLine -match 'LOCL') { Status $false "ReferenceId still LOCL - w32tm has not synced yet. Check firewall for outbound UDP 123." } else { Status $true "ReferenceId: $refIdLine (no longer LOCL)" } } catch { Status $false "Time sync fix: $_" } # ---------------------------------------------------------------------------- Section '2. TLS 1.2 enforcement (.NET + Schannel) - requires reboot' # ---------------------------------------------------------------------------- # .NET framework: enable strong crypto + system default TLS versions for both # 64-bit and 32-bit runtimes. These take effect immediately for new processes. foreach ($p in 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319') { try { if (-not (Test-Path $p)) { New-Item -Path $p -Force | Out-Null } New-ItemProperty -Path $p -Name 'SchUseStrongCrypto' -PropertyType DWord -Value 1 -Force | Out-Null New-ItemProperty -Path $p -Name 'SystemDefaultTlsVersions' -PropertyType DWord -Value 1 -Force | Out-Null Status $true ".NET TLS keys set at $p" } catch { Status $false ".NET TLS at $p - $_" } } # Schannel: disable TLS 1.0 and 1.1 (both Client and Server). Enable TLS 1.2 # explicitly. Takes effect only after reboot. $base = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols' foreach ($proto in 'TLS 1.0','TLS 1.1') { foreach ($role in 'Client','Server') { $k = "$base\$proto\$role" try { if (-not (Test-Path $k)) { New-Item -Path $k -Force | Out-Null } New-ItemProperty -Path $k -Name 'Enabled' -PropertyType DWord -Value 0 -Force | Out-Null New-ItemProperty -Path $k -Name 'DisabledByDefault' -PropertyType DWord -Value 1 -Force | Out-Null Status $true "Disabled $proto $role" } catch { Status $false "Disable $proto $role - $_" } } } foreach ($role in 'Client','Server') { $k = "$base\TLS 1.2\$role" try { if (-not (Test-Path $k)) { New-Item -Path $k -Force | Out-Null } New-ItemProperty -Path $k -Name 'Enabled' -PropertyType DWord -Value 1 -Force | Out-Null New-ItemProperty -Path $k -Name 'DisabledByDefault' -PropertyType DWord -Value 0 -Force | Out-Null Status $true "Enabled TLS 1.2 $role" } catch { Status $false "Enable TLS 1.2 $role - $_" } } # Snapshot post-state for audit reg export "HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" "$backupDir\schannel-protocols-post.reg" /y 2>&1 | Out-Null # ---------------------------------------------------------------------------- Section '3. Install Windows Server Backup feature - no reboot' # ---------------------------------------------------------------------------- try { $feature = Get-WindowsFeature -Name Windows-Server-Backup if ($feature.InstallState -eq 'Installed') { Status $true 'Windows-Server-Backup already installed' } else { $result = Install-WindowsFeature -Name Windows-Server-Backup -IncludeManagementTools if ($result.Success) { Status $true "Installed Windows-Server-Backup. RestartNeeded: $($result.RestartNeeded)" } else { Status $false "Install-WindowsFeature returned Success=False" } } } catch { Status $false "WSB install: $_" } # ---------------------------------------------------------------------------- Section "4. Schedule reboot for $TargetRebootHour`:00 local" # ---------------------------------------------------------------------------- $now = Get-Date $target = $now.Date.AddHours($TargetRebootHour) if ($target -lt $now) { $target = $target.AddDays(1) } $seconds = [int]($target - $now).TotalSeconds Write-Output "Current time: $now" Write-Output "Target reboot: $target" Write-Output "Delay: $seconds seconds ($([math]::Round($seconds/60,1)) minutes)" if ($seconds -lt 60) { Status $false "Target reboot is less than 1 minute away - not scheduling. Run again closer to window." } elseif ($seconds -gt 86400) { Status $false "Target reboot more than 24 hours away - $seconds seconds. Rejecting." } else { # /r restart, /t timeout, /d p:4:2 = Planned / Application: Maintenance # /c comment (<512 chars) shows in user popup + event log # No /f - we want graceful shutdown of services (AD, Hyper-V, QB VSS) $comment = 'Entra Connect preflight: TLS 1.2 + time sync enforcement. Cancel: shutdown /a' shutdown /r /t $seconds /d p:4:2 /c $comment if ($LASTEXITCODE -eq 0) { Status $true "Reboot scheduled for $target (in $seconds sec). Cancel with: shutdown /a" } else { Status $false "shutdown command returned exit $LASTEXITCODE" } } # ---------------------------------------------------------------------------- Section '5. Summary' # ---------------------------------------------------------------------------- Write-Output "Backup/rollback dir: $backupDir" Write-Output "Reboot scheduled: $target" Write-Output 'Cancel reboot: shutdown /a' Write-Output '' Write-Output 'Post-reboot verification:' Write-Output ' 1. w32tm /query /status - confirm ReferenceId is no longer LOCL' Write-Output ' 2. nltest /dsgetdc:cascades.local - confirm DC is responding' Write-Output ' 3. Re-run entra-connect-readiness.ps1 for full pass/fail sweep' Write-Output '' Write-Output "Completed at $(Get-Date)"