PowerShell: Administering Network Time Protocol Settings on Windows

Quick Script of Domain Joined Laptops and Desktops for Remote Users:

# Set Domain Joined computers to use Domain Hierarchy time source propagation protocol as well as manual as fall-back
# Features to be added later:
# 1. Verify that the host is able to reach upstream NTP server at port 123 (ntp service)
# 2. Only apply settings if (1) is True

# Manually specify Time Server Sources
# Note: certain machines will append '0x8' to config, which means client mode
$externalTimeSources='3.us.pool.ntp.org 2.us.pool.ntp.org 1.us.pool.ntp.org 0.us.pool.ntp.org'

Function configureNtp{
    param(
        $externalTimeSources='0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org'
        )
    # Determine PDC without using the activedirectory module
    $domain=(ipconfig /all | select-string -Pattern 'Primary Dns Suffix' | % {$_ -replace '   Primary Dns Suffix  . . . . . . . :'}).trim()
    $pdc=((nltest /dclist:$domain | ?{$_ -match '\[PDC\]'}).split('.')[0]).trim()
    # This method to get hostname is similar to $env:computername with the difference in allowing names longer than 15 characters (as limited by NetBIOS)
    $localhostName = [System.Net.Dns]::GetHostName()
    $thisMachineIsPdc=$pdc -match $localhostName
    if($thisMachineIsPdc){
        w32tm /config /syncfromflags:manual /manualpeerlist:"$externalTimeSources" /update /reliable:YES
     }else{
        $preferredPeerList=if($pdc){$pdc+".$ENV:USERDNSDOMAIN,0x9 "+$externalTimeSources}else{$externalTimeSources}
        w32tm /config /syncfromflags:all /manualpeerlist:"$preferredPeerList" /update     
    }
 
    # Disable time sync with Hyper-V host
    $vmHostTimeSyncRegistry='REGISTRY::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider'
    $syncWithHost=(get-itemproperty $vmHostTimeSyncRegistry).Enabled
    if($syncWithHost){
        write-host 'Disabling time synchronization with VM hosts...'
        Set-ItemProperty -Path $vmHostTimeSyncRegistry -Name "Enabled" -value 0 -Type DWord
    }
 
    # Enable NTP Service
    $ntpServiceRegKey='REGISTRY::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer'
    $ntpServiceEnabled=(get-itemproperty $ntpServiceRegKey).Enabled
    if(!$ntpServiceEnabled){
        Set-ItemProperty -Path $ntpServiceRegKey -Name "Enabled" -value 1 -Type DWord
    }
    # Set poll interval to 3600 seconds
    reg add "HKLM\system\CurrentControlSet\Services\W32Time\Parameters" /v SpecialPollInterval /t REG_DWORD /d 3600 /f
    $w32TimeStartType=(Get-Service w32time).StartType
    if ($w32TimeStartType -ne "Automatic"){
        write-host 'setting W32TIME to automatically start...'
        Set-Service –Name w32time –StartupType "Automatic"
        start-service w32time
        }
    function syncNtp{
        write-host "Triggering time sync..."
        #gpupdate /force
        $source=w32tm /query /source
        write-host "Synching from source: $source..."
        w32tm /resync
        w32tm /query /status
        }
    syncNtp
}

configureNtp $externalTimeSources
#######################################################################
# Accessory function to fix certain issues with Windows Time
function resetW32Time{
  write-host 'Resetting Windows Time Service...'
  net stop w32time
  w32tm.exe /unregister
  w32tm.exe /register
  net start w32time
}
Function checkNtpSettings{
  # NTP settings as stored in localhost’s registry
  $ntpRegistryKey = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters"
   
  # Determine PDC without using the activedirectory module
  $domain=(ipconfig /all | select-string -Pattern 'Primary Dns Suffix' | % {$_ -replace '   Primary Dns Suffix  . . . . . . . :'}).trim()
  $pdc=((nltest /dclist:$domain | ?{$_ -match '\[PDC\]'}).split('.')[0]).trim()
  # This method to get hostname is similar to $env:computername with the difference in allowing names longer than 15 characters (as limited by NetBIOS)
  $localhostName = [System.Net.Dns]::GetHostName()
  $thisMachineIsPdc=$pdc -match $localhostName

  # Get current time source
  # w32tm /query /source

  # If type = "NT5DS" then NTP is set as domain hierarchy (DOMHIER) and NTP source will be from PDC
  # If type = "NTP" then NTP is set to get time from a manually configured NTP source (MANUAL)
  $ntpType = (Get-ItemProperty -Path $ntpRegistryKey).Type

  # This value is active if NTP type is set to "NTP" (meaning MANUAL)
  $ntpValue = (Get-ItemProperty -Path $ntpRegistryKey).NTPserver

  Switch ($ntpType.toLower()){
  "nosync" {
      write-host "NTP is currently set as NOSYNC. This is NOT a recommended setting.";
      }
  "ntp" {
          If ($thisMachineIsPdc){
              write-host "NTP is currently set as MANUAL and syncing from $ntpValue on this PDC Emulator.";
          }
          else{
              write-host "NTP is currently set as MANUAL and syncing from $ntpValue.";
          }
      }
  "nt5ds" {
          If ($thisMachineIsPdc){
              write-host "NTP is currently set as DOMHIER and synching from $ntpValue - this setting is incorrect for this PDC Emulator!";
              }
          else{
              write-host "NTP is currently set as DOMHIER and synching from $ntpValue.";
          }
      }
  "allsync" {
      if (!$domain){
          write-host "NTP is currently set to ALLSYNC (sync from all available sources) with these values $ntpValue.";
      }else{
          If ($thisMachineIsPdc){
              write-host "NTP is currently set to ALLSYNC. It should be changed to MANUAL for this PDC Emulator!"
              }else{
                  write-host "NTP is currently set to ALLSYNC with these values $ntpValue."
              }
          }
      }
  }
}
checkNtpSettings
resetW32Time

Best Practices, according to Microsoft: https://social.technet.microsoft.com/Forums/windows/en-US/043b1ebe-e7bc-40ca-91e0-174a6854808e/time-sync-best-practices?forum=winserverDS

Old Notes:

$externalTimeSources='0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org,3.pool.ntp.org' # 0x8 means client mode

# This is similar to $env:computername with the difference in allowing names longer than 15 characters (as limited by NetBIOS)
$localhostName = [System.Net.Dns]::GetHostName()
"This local machine name is $localhostName"

# This works without the activedirectory module
$domain = (ipconfig /all | select-string -Pattern 'Primary Dns Suffix' | % {$_ -replace ' Primary Dns Suffix . . . . . . . :'}).trim()

# Ensure that script is ran in the context of an Administrator
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)

# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator

# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "Black"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator

# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";

# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;

# Indicate that the process should be elevated
$newProcess.Verb = "runas";

# Start the new process
[System.Diagnostics.Process]::Start($newProcess);

# Exit from the current, unelevated, process
exit
}

Function checkNtpSettings{
# NTP settings as stored in localhost’s registry
$ntpRegistryKey = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters"

# If type = "NT5DS" then NTP is set as domain hierarchy (DOMHIER) and NTP source will be from PDC
# If type = "NTP" then NTP is set to get time from a manually configured NTP source (MANUAL)
$GLOBAL:ntpType = (Get-ItemProperty -Path $ntpRegistryKey).Type

# This value is active if NTP type is set to "NTP" (meaning MANUAL)
$GLOBAL:ntpValue = (Get-ItemProperty -Path $ntpRegistryKey).NTPserver

Switch ($ntpType.toLower()){
"nosync" {
"NTP is currently set as NOSYNC. This is not a recommended setting.";
}
"ntp" {
If ($isPDC){
"NTP is currently set as MANUAL and syncing from $ntpValue.";
}
else{
"NTP is currently set as MANUAL and syncing from $ntpValue - this setting may be incorrect.";
}
}
"nt5ds" {
If ($isPDC){
"NTP is currently set as DOMHIER and synching from $ntpValue - this setting may be incorrect for this Primary Domain Controller.";
}
else{
"NTP is currently set as DOMHIER and synching from $ntpValue.";
}
}
"allsync" {
if (!($domain)){
"NTP is currently set to ALLSYNC (sync from all available sources). It should be changed to 'MANUAL,ALLSYNC' for this standalone PC.";
}else{
If ($isPDC){
"NTP is currently set to ALLSYNC. It should be changed to MANUAL for this Primary Domain Controller."
}
else{
"NTP is currently set to ALLSYNC. It should be changed to DOMHIER,MANUAL for this domain joined computer."
}
}
}
}
}

Function setPdcNtp{
# Set PDC Emulator to use external time sources
$clientNTPSetting = "w32tm /config /manualpeerlist:$externalTimeSources /reliable:yes /syncfromflags:MANUAL /update"
invoke-expression $clientNTPSetting
restart-service W32Time
}

Function setClientNTP{
# Set Domain Joined computers to use Domain Hierarchy time source propagation protocol
#$clientNTPSetting = "w32tm /config /update /manualpeerlist:$externalTimeSources /syncfromflags:DOMHIER,MANUAL"
$clientNTPSetting="w32tm /config /syncfromflags:domhier /manualpeerlist:$externalTimeSources /update"
Invoke-Expression $clientNTPSetting
reg add "HKLM\system\CurrentControlSet\Services\W32Time\Parameters" /v SpecialPollInterval /t REG_DWORD /d 3600 /f
restart-service W32Time
}

Function setStandaloneNTP{
# Set Standalone computer to sync with external time sources with CMOS clock as a backup method
$standaloneNTPSetting = "w32tm /config /update /manualpeerlist:$externalTimeSources /syncfromflags:MANUAL"
reg add "HKLM\system\CurrentControlSet\Services\W32Time\Parameters" /v SpecialPollInterval /t REG_DWORD /d 3600 /f
Invoke-Expression $standaloneNTPSetting
restart-service W32Time
}

Function setPollInterval{
# Set the poll interval to 1 hour
reg add "HKLM\system\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer\NtpClient" /v SpecialPollInterval /t REG_DWORD /d 3600 /f
net stop w32time
sleep 4
net start w32time
w32tm /resync /rediscover
}

Function getPdc{
try{
$GLOBAL:pdc=(nltest /dclist:$domain | select-string -Pattern 'PDC' | % {$_.line.split('.')[0]}).Trim()
}
catch{
$GLOBAL:pdc=$localHostName
}
if ($pdc -eq $localHostName){
$GLOBAL:isPDC=$True;
}else{
$GLOBAL:isPDC=$False;
}
}

Function checkDCReachability{
Try {
(nltest /dsgetdc:$domain /DS_6 /avoidself | findstr 'DC: ').split(" ") | %{
If($_ -like '\\*'){
$dcname= $_.replace('\\','') #DC Found
}
}
}
Catch {
$dcname = $false #No DC found
}

If ($dcname){
#$DC exists
write-host "Nearest Domain Controller: $($DC)."
}
Else{
#$DC is $false
write-host "Nearest Domain controller not found!"
}
}

Function startupW32time{
$w32TimeStartType=(Get-Service w32time).StartType
if ($w32TimeStartType -ne "Automatic"){
Set-Service –Name w32time –StartupType "Automatic"
start-service w32time
}
}

Function fixNTP{
startupW32time;
if (!($domain)){
"$env:computername is not on the domain. Setting NTP as Standalone..."
setStandaloneNTP;
setPollInterval;
}else{
If ($isPDC){
"$env:computername is a Primary Domain Controller. Setting NTP as PDC..."
setPdcNtp;
setPollInterval;
}else{
"$env:computername is a Domain Joined Computer. Setting NTP as a domain client..."
setClientNTP;
setPollInterval;
}
}
}

Function proceed{
$fix = Read-Host -Prompt "Input 'fixntp' to resolve possible NTP issues on this local machine."
cls;
if ($fix -eq "fixntp"){
fixNTP;
checkNtpSettings;
"Currently, the time source server(s) is/are $ntpValue. NTP functional setting is/are $ntpType."
"Triggering time sync...`n"
w32tm -resync
w32tm /query /status
pause;
}else{
"Currently, the time source server(s) is/are $ntpValue. NTP functional setting is/are $ntpType."
"No changes have been made.`n"
"Triggering time sync...`n"
w32tm -resync
w32tm /query /status
pause;
}
}
getPdc;
checkNtpSettings;
proceed;

<# Troubleshooting\
ERROR:
New-PSSession : [SERVER] Connecting to remote server SERVER failed with the following error message : WinRM cannot process the request. The following
error with errorcode 0x80090324 occurred while using Kerberos authentication: There is a time and/or date difference between the client and server.
Possible causes are:
-The user name or password specified are invalid.
-Kerberos is used when no authentication method and no user name are specified.
-Kerberos accepts domain user names, but not local user names.
-The Service Principal Name (SPN) for the remote computer name and port does not exist.
-The client and remote computers are in different domains and there is no trust between the two domains.
After checking for the above issues, try the following:
-Check the Event Viewer for events related to authentication.
-Change the authentication method; add the destination computer to the WinRM TrustedHosts configuration setting or use HTTPS transport.
Note that computers in the TrustedHosts list might not be authenticated.
-For more information about WinRM configuration, run the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help
topic.
At line:10 char:20
+ $session = New-PSSession -ComputerName $computer
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : 1398,PSSessionOpenFailed


Resolution:

# Attempt to start Windows time service
PS C:\Windows\system32> net start w32time
System error 1058 has occurred.
The service cannot be started, either because it is disabled or because it has no enabled devices associated with it.

# Check service startup type
PS C:\Windows\system32> Get-Service w32time | select -property name,starttype
Name StartType
---- ---------
w32time Disabled

# Change startup type, start service
Set-Service –Name w32time –StartupType "Automatic"
start-service w32time

# Force time sync
PS C:\Windows\system32> W32tm /resync /force
Sending resync command to local computer
The computer did not resync because no time data was available.

# Ran script to fix NTP
https://kimconnect.com/powershell-administering-network-time-protocol-settings-on-windows/
#>

The Output

PS H:\> \\komputer\kimconnect\Desktop\ntp-admin-script.ps1
This local machine name is komputer
NTP is currently set to ALLSYNC. It should be changed to DOMHIER,MANUAL for this domain joined computer.
Run fixNTP to resolve possible NTP issues on this local machine.

Leave a Reply

Your email address will not be published. Required fields are marked *