# ============================================================================
# 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-<timestamp>\ 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)"
