Set-NTPConfig.psm1
<#
.SYNOPSIS Configures Windows NTP client with secure, reliable time synchronization. .DESCRIPTION Sets NTP servers, configures polling intervals, and ensures Windows Time service is properly configured. Includes error handling, validation, and geographic detection. .PARAMETER NtpServers Comma-separated list of NTP servers. If not specified, automatically detects region. .PARAMETER Region Geographic region for NTP pool selection. Options: NorthAmerica, Europe, Asia, Oceania, SouthAmerica, Africa, Auto. Default is Auto (detects based on timezone). .PARAMETER SpecialPollInterval Poll interval in seconds. Default is 900 (15 minutes) for workstations, 300 (5 minutes) for servers. Valid range: 64-86400. Lower values increase accuracy but also network traffic. .PARAMETER ServerType System type. Options: Workstation, Server. Adjusts default poll interval accordingly. .PARAMETER Force Skip confirmation prompts. .EXAMPLE Set-NTPConfig Automatically detects region and configures with appropriate defaults. .EXAMPLE Set-NTPConfig -Region Europe -SpecialPollInterval 300 Uses European NTP pool servers with 5-minute polling. .EXAMPLE Set-NTPConfig -NtpServers "time.cloudflare.com,0x9 time.google.com,0x9" -Force Uses custom NTP servers without confirmation. .EXAMPLE Set-NTPConfig -ServerType Server Configures for server with 5-minute default polling interval. .NOTES Requires Administrator privileges. Author: Collin George Version: 2.0.8 Last Updated: October 7, 2025 License: MIT License #> function Write-Log { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$Message, [Parameter()] [ValidateSet('Info','Warning','Error','Success')] [string]$Level = 'Info' ) $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $color = switch ($Level) { 'Info' { 'Cyan' } 'Warning' { 'Yellow' } 'Error' { 'Red' } 'Success' { 'Green' } } Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color } function Get-RegionFromTimezone { try { $timezoneId = (Get-TimeZone).Id switch -Regex ($timezoneId) { 'Pacific|Mountain|Central|Eastern|Alaska|Hawaii|US|Canada|Mexico' { return 'NorthAmerica' } 'Europe|GMT|UTC|London|Paris|Berlin|Rome|Madrid' { return 'Europe' } 'Asia|Tokyo|Seoul|Shanghai|Hong Kong|Singapore|India' { return 'Asia' } 'Australia|New Zealand|Pacific/Auckland' { return 'Oceania' } 'South America|Argentina|Brazil|Chile' { return 'SouthAmerica' } 'Africa|Cairo|Johannesburg' { return 'Africa' } default { Write-Log "Could not detect region from timezone: $timezoneId. Defaulting to NorthAmerica." -Level Warning; return 'NorthAmerica' } } } catch { Write-Log "Error detecting timezone: $_. Defaulting to NorthAmerica." -Level Warning return 'NorthAmerica' } } function Get-NtpServersForRegion { param([string]$Region) $ntpPools = @{ 'NorthAmerica' = "0.north-america.pool.ntp.org,0x9 1.north-america.pool.ntp.org,0x9 2.north-america.pool.ntp.org,0x9 3.north-america.pool.ntp.org,0x9" 'Europe' = "0.europe.pool.ntp.org,0x9 1.europe.pool.ntp.org,0x9 2.europe.pool.ntp.org,0x9 3.europe.pool.ntp.org,0x9" 'Asia' = "0.asia.pool.ntp.org,0x9 1.asia.pool.ntp.org,0x9 2.asia.pool.ntp.org,0x9 3.asia.pool.ntp.org,0x9" 'Oceania' = "0.oceania.pool.ntp.org,0x9 1.oceania.pool.ntp.org,0x9 2.oceania.pool.ntp.org,0x9 3.oceania.pool.ntp.org,0x9" 'SouthAmerica' = "0.south-america.pool.ntp.org,0x9 1.south-america.pool.ntp.org,0x9 2.south-america.pool.ntp.org,0x9 3.south-america.pool.ntp.org,0x9" 'Africa' = "0.africa.pool.ntp.org,0x9 1.africa.pool.ntp.org,0x9 2.africa.pool.ntp.org,0x9 3.africa.pool.ntp.org,0x9" } return $ntpPools[$Region] } function Test-NtpServer { param([string]$Server) try { Write-Log "Testing connectivity to $Server..." -Level Info $result = w32tm /stripchart /computer:$Server /samples:1 /dataonly 2>&1 if ($LASTEXITCODE -eq 0) { Write-Log "Successfully contacted $Server" -Level Success; return $true } else { Write-Log "Could not reach $Server" -Level Warning; return $false } } catch { Write-Log "Error testing $Server : $_" -Level Warning return $false } } function Set-NTPConfig { [CmdletBinding(SupportsShouldProcess=$true)] param( [string]$NtpServers, [ValidateSet('NorthAmerica','Europe','Asia','Oceania','SouthAmerica','Africa','Auto')] [string]$Region = 'Auto', [Nullable[int]]$SpecialPollInterval = $null, [ValidateSet('Workstation','Server')] [string]$ServerType = 'Workstation', [switch]$Force ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' try { Write-Log "=== Windows NTP Configuration Script v2.0.8 ===" -Level Info if (-not $SpecialPollInterval) { $SpecialPollInterval = if ($ServerType -eq 'Server') { 300 } else { 900 } Write-Log "Using default poll interval for ${ServerType}: $SpecialPollInterval seconds" -Level Info } elseif ($SpecialPollInterval -lt 64 -or $SpecialPollInterval -gt 86400) { throw "SpecialPollInterval must be between 64 and 86400 seconds." } if (-not $NtpServers) { if ($Region -eq 'Auto') { $Region = Get-RegionFromTimezone; Write-Log "Auto-detected region: $Region" -Level Info } $NtpServers = Get-NtpServersForRegion -Region $Region Write-Log "Using $Region NTP pool servers" -Level Info } else { Write-Log "Using custom NTP servers" -Level Info } Write-Log "NTP Servers: $NtpServers" -Level Info Write-Log "Poll Interval: $SpecialPollInterval seconds" -Level Info $firstServer = ($NtpServers -split ' ')[0] -replace ',0x9','' Test-NtpServer -Server $firstServer # Registry paths $w32timeParams = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" $ntpClientPath = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient" $configPath = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" if ($PSCmdlet.ShouldProcess("Windows Time Service", "Configure NTP settings")) { # Configure parameters Set-ItemProperty -Path $w32timeParams -Name "NtpServer" -Value $NtpServers Set-ItemProperty -Path $w32timeParams -Name "Type" -Value "NTP" Set-ItemProperty -Path $ntpClientPath -Name "SpecialPollInterval" -Value $SpecialPollInterval -Type DWord Set-ItemProperty -Path $ntpClientPath -Name "Enabled" -Value 1 -Type DWord Set-ItemProperty -Path $configPath -Name "MaxPosPhaseCorrection" -Value 3600 -Type DWord Set-ItemProperty -Path $configPath -Name "MaxNegPhaseCorrection" -Value 3600 -Type DWord Set-ItemProperty -Path $configPath -Name "UpdateInterval" -Value 100 -Type DWord # Service $service = Get-Service -Name w32time if ($service.StartType -ne 'Automatic') { Set-Service -Name w32time -StartupType Automatic } if ($service.Status -eq 'Running') { Stop-Service -Name w32time -Force; Start-Sleep 2 } Start-Service -Name w32time Start-Sleep 5 w32tm /config /update w32tm /resync /rediscover } Write-Log "=== NTP configuration complete ===" -Level Success Write-Host "Configured NTP Servers: $NtpServers" -ForegroundColor Green Write-Host "Poll Interval: $SpecialPollInterval seconds" -ForegroundColor Green Get-Service w32time | Format-Table -Property Name, Status, StartType w32tm /query /status } catch { Write-Log "Error: $_" -Level Error throw } } # Export the main function Export-ModuleMember -Function Set-NTPConfig |