Skip to content

P03-T02 - AD Recovery and Time Baseline

Objective

Enable the forest recovery baseline, create the first System State recovery point on a dedicated disk, and configure the PDC Emulator to use external NTP peers.

Validated in the lab on Saturday, July 18, 2026, using QEMU Guest Agent only as execution transport. The production sysadmin runs the PowerShell directly on HQ-DC01.

Inputs

Key Validated value Configurable parameter
Target HQ-DC01 Run on the intended PDC Emulator
Domain corp.gntech.me DomainFqdn
Backup disk Proxmox scsi1, Windows Disk 1, 40 GB BackupDiskNumber
Backup volume E: / DCBackup BackupDriveLetter
NTP peers time.cloudflare.com,0x8, time.google.com,0x8 NtpPeers

Target State

Property Required result
AD Recycle Bin Enabled forest-wide
Windows Server Backup Installed
Backup storage Dedicated, non-boot, non-system disk
Recovery point At least one successful System State version
PDC time External manual peers; reliable time source
W32Time Running and synchronized; not Local CMOS Clock

Prechecks

  • Complete and validate P03-T01.
  • Run dcdiag /test:Advertising /test:Services /test:SysVolCheck /test:NetLogons; stop on any failure.
  • Confirm which server owns the PDC Emulator role.
  • Inspect the hypervisor before attaching storage. Do not assume Disk 1 exists.
  • Never point this workflow at the OS disk or a disk containing customer data.

Current State Verification

On Proxmox, inspect the VM and attach a dedicated disk only if one is absent:

qm config 4011

Validated lab correction:

qm set 4011 --scsi1 local-zfs:40

Proxmox is used only to attach the virtual disk. Run the Windows automation directly on HQ-DC01.

From elevated PowerShell on HQ-DC01:

Get-Disk | Select-Object Number,PartitionStyle,IsBoot,IsSystem,@{n='SizeGB';e={[math]::Round($_.Size/1GB,2)}}
Get-Volume | Select-Object DriveLetter,FileSystemLabel,FileSystem
(Get-ADDomain).PDCEmulator
w32tm /query /source

The validated new disk appeared as Disk 1, RAW, IsBoot=False, IsSystem=False, and 40 GB. Stop if the intended disk is not RAW or its identity is ambiguous.

Execution

Copy the complete script below to HQ-DC01 or save it as Configure-HQ-DC01-RecoveryTime.ps1:

Show full recovery and time script
[CmdletBinding()]
param(
    [string]$DomainFqdn = 'corp.gntech.me',
    [string]$BackupDriveLetter = 'E',
    [int]$BackupDiskNumber = 1,
    [string[]]$NtpPeers = @('time.cloudflare.com,0x8','time.google.com,0x8'),
    [switch]$StartSystemStateBackup,
    [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 Get-RecoveryState {
    $feature = Get-ADOptionalFeature -Identity 'Recycle Bin Feature'
    $backupFeature = Get-WindowsFeature Windows-Server-Backup
    $backupVolume = Get-Volume -ErrorAction Stop | Where-Object DriveLetter -eq $BackupDriveLetter
    $backupDisk = Get-Disk -Number $BackupDiskNumber -ErrorAction SilentlyContinue

    [pscustomobject]@{
        Domain = (Get-ADDomain).DNSRoot
        PdcEmulator = (Get-ADDomain).PDCEmulator
        RecycleBinEnabled = ($feature.EnabledScopes.Count -gt 0)
        BackupFeature = $backupFeature.InstallState
        BackupDiskNumber = if ($backupDisk) { $backupDisk.Number } else { $null }
        BackupDiskStyle = if ($backupDisk) { $backupDisk.PartitionStyle } else { 'Missing' }
        BackupDiskIsBoot = if ($backupDisk) { $backupDisk.IsBoot } else { $null }
        BackupDiskIsSystem = if ($backupDisk) { $backupDisk.IsSystem } else { $null }
        BackupVolume = if ($backupVolume) { "$($backupVolume.DriveLetter):" } else { 'Missing' }
        BackupVolumeLabel = if ($backupVolume) { $backupVolume.FileSystemLabel } else { '' }
        TimeSource = ((w32tm /query /source) | Out-String).Trim()
        W32TimeStatus = (Get-Service W32Time).Status
    }
}

function Assert-ExistingBackupTargetSafe {
    $volume = Get-Volume -ErrorAction Stop | Where-Object DriveLetter -eq $BackupDriveLetter
    if (-not $volume) { return }

    $partition = Get-Partition -DriveLetter $BackupDriveLetter -ErrorAction Stop
    $disk = Get-Disk -Number $partition.DiskNumber -ErrorAction Stop
    if ($partition.DiskNumber -ne $BackupDiskNumber) {
        throw "Drive ${BackupDriveLetter}: is on Disk $($partition.DiskNumber), not requested Disk $BackupDiskNumber."
    }
    if ($disk.IsBoot -or $disk.IsSystem) {
        throw "Drive ${BackupDriveLetter}: is backed by a boot or system disk. Refusing to continue."
    }
    if ($volume.FileSystemLabel -ne 'DCBackup') {
        throw "Drive ${BackupDriveLetter}: has label '$($volume.FileSystemLabel)', not 'DCBackup'. Refusing to continue."
    }
}

Assert-Elevated
Import-Module ActiveDirectory
Import-Module ServerManager

$domain = Get-ADDomain
if ($domain.DNSRoot -ne $DomainFqdn) {
    throw "Connected domain '$($domain.DNSRoot)' does not match requested domain '$DomainFqdn'."
}
if ($domain.PDCEmulator -ne "$env:COMPUTERNAME.$DomainFqdn") {
    throw "Run the time configuration on the PDC Emulator '$($domain.PDCEmulator)'."
}
if ($BackupDriveLetter.Length -ne 1 -or $BackupDriveLetter -notmatch '^[A-Za-z]$') {
    throw 'BackupDriveLetter must be one alphabetic character.'
}
Assert-ExistingBackupTargetSafe

Write-Output '=== Current State ==='
Get-RecoveryState | Format-List | Out-String | Write-Output

if ($ValidateOnly) {
    Write-Output 'Validation mode only. No recovery or time configuration was changed.'
    return
}

$recycleBin = Get-ADOptionalFeature -Identity 'Recycle Bin Feature'
if ($recycleBin.EnabledScopes.Count -eq 0) {
    Enable-ADOptionalFeature -Identity 'Recycle Bin Feature' -Scope ForestOrConfigurationSet -Target $DomainFqdn -Confirm:$false
}

if (-not (Get-WindowsFeature Windows-Server-Backup).Installed) {
    Install-WindowsFeature Windows-Server-Backup | Out-Null
}

$backupVolume = Get-Volume -ErrorAction Stop | Where-Object DriveLetter -eq $BackupDriveLetter
if (-not $backupVolume) {
    $backupDisk = Get-Disk -Number $BackupDiskNumber -ErrorAction Stop
    if ($backupDisk.IsBoot -or $backupDisk.IsSystem) {
        throw "Disk $BackupDiskNumber is a boot or system disk. Refusing to modify it."
    }
    if ($backupDisk.PartitionStyle -ne 'RAW') {
        throw "Disk $BackupDiskNumber is '$($backupDisk.PartitionStyle)', not RAW. Refusing to modify it."
    }

    $partition = $backupDisk |
        Initialize-Disk -PartitionStyle GPT -PassThru |
        New-Partition -DriveLetter $BackupDriveLetter -UseMaximumSize
    Format-Volume -Partition $partition -FileSystem NTFS -NewFileSystemLabel 'DCBackup' -Confirm:$false | Out-Null
}

$peerList = $NtpPeers -join ' '
w32tm /config /manualpeerlist:"$peerList" /syncfromflags:manual /reliable:yes /update | Out-Null
Restart-Service W32Time -Force
Start-Sleep -Seconds 5
w32tm /resync /rediscover | Out-Null

if ($StartSystemStateBackup) {
    wbadmin start systemstatebackup -backupTarget:"${BackupDriveLetter}:" -quiet
    if ($LASTEXITCODE -ne 0) { throw "wbadmin failed with exit code $LASTEXITCODE." }
}

Write-Output '=== Result ==='
Get-RecoveryState | Format-List | Out-String | Write-Output
if ($StartSystemStateBackup) {
    Write-Output '=== Backup Versions ==='
    wbadmin get versions
}

Run the read-only inspection first:

.\Configure-HQ-DC01-RecoveryTime.ps1 -ValidateOnly

Apply Recycle Bin, backup feature/disk preparation, and NTP configuration:

.\Configure-HQ-DC01-RecoveryTime.ps1

Start the first System State backup and wait for it to complete:

.\Configure-HQ-DC01-RecoveryTime.ps1 -StartSystemStateBackup

Do not launch a second backup while wbadmin or wbengine is active. Observe progress when needed:

wbadmin get status
Get-Process wbadmin,wbengine -ErrorAction SilentlyContinue
Get-Volume -DriveLetter E

Observed lab result:

  • the first backup ran from 20:52:19 through 21:08:50 local event time (about 16 minutes)
  • the 40 GB volume had approximately 29.7 GB free after completion
  • use those figures only as lab evidence, not as universal production sizing; size retention from the real directory, policy, and recovery objectives

Validation

$feature = Get-ADOptionalFeature -Identity 'Recycle Bin Feature'
$feature.EnabledScopes.Count -gt 0
Get-WindowsFeature Windows-Server-Backup
Get-Disk -Number 1
Get-Volume -DriveLetter E
w32tm /query /peers
w32tm /query /source
w32tm /query /status
wbadmin get versions
Get-Service W32Time

Pass only when:

  • Recycle Bin returns True
  • backup feature is installed
  • E: is DCBackup on the dedicated disk
  • time source is one of the configured external peers
  • wbadmin get versions returns at least one completed System State version

Validation boundary:

  • this runbook proves creation and catalog availability of a System State recovery point
  • it does not prove authoritative restore, non-authoritative restore, DSRM access, or bare-metal recovery
  • restoration remains a separate disruptive validation requiring an isolated recovery exercise

Evidence

  • qm config 4011 showing the dedicated scsi1
  • disk/volume output identifying non-system Disk 1 and E:\DCBackup
  • Recycle Bin enabled scopes
  • NTP peers, source, status, and W32Time service state
  • successful Windows Backup event and wbadmin get versions

Rollback

  • AD Recycle Bin cannot be disabled after enablement.
  • Correct NTP peers by rerunning the script with the approved NtpPeers values.
  • Do not detach or format the backup disk after a recovery point exists unless retention and replacement are explicitly approved.
  • Never use this runbook to restore AD; use an isolated recovery procedure.