Scripts/VM/Windows/ntp-status.ps1
|
# NTP / Windows Time service status as JSON. # ISO 27001 evidence for A.8.17 (clock synchronisation). # # Read-only. Reads w32tm /query /status /verbose and parses the audit-relevant fields # into a JSON object on stdout: # # { NowUtc, TimeZone, Synchronized, NtpEnabled, Daemon, Source, Compliant } # # Synchronized = (W32Time running) AND (Stratum < 16) AND (Source != "Local CMOS Clock"). # Compliant = the A.8.17 policy verdict — Synchronized AND a non-empty Source. $ErrorActionPreference = 'Continue' $result = [ordered]@{ NowUtc = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') TimeZone = (Get-TimeZone).Id Synchronized = $false NtpEnabled = $false Daemon = 'W32Time' Source = '' Compliant = $false } $svc = Get-Service -Name W32Time -ErrorAction SilentlyContinue if ($svc -and $svc.Status -eq 'Running') { $result.NtpEnabled = $true $verbose = (& w32tm /query /status /verbose 2>&1) -join "`n" # "Source: time.windows.com,0x9" if ($verbose -match '(?m)^\s*Source:\s*(.+?)\s*$') { $result.Source = $matches[1].Trim() } # "Stratum: 3 (...)" - lower = closer to authoritative; 16 means unsynchronized. $stratum = $null if ($verbose -match '(?m)^\s*Stratum:\s*(\d+)') { $stratum = [int]$matches[1] } if ($stratum -and $stratum -gt 0 -and $stratum -lt 16 -and $result.Source -notmatch 'Local\s+CMOS\s+Clock') { $result.Synchronized = $true } } # A.8.17 verdict — same rule as the Linux script so the CSV column is comparable. $result.Compliant = ($result.Synchronized -and -not [string]::IsNullOrWhiteSpace($result.Source)) [pscustomobject]$result | ConvertTo-Json |