This PowerShell script will create the Time Server GPOs and WMI Filters for the Domain Controllers to ensure your time server hierarchy remains correct for transfer and seizure of the PDC emulator FSMO role holder.
However, before I talk about the script it’s important to provide some background information on the required settings for the Windows Time Service (W32Time), as many tend to get it wrong.
The three (3) important settings are:
- NTPServer
- Type
- AnnounceFlags
NTPServer: Multiple (a pool) of NTP servers can be added by separating them with a space. This setting is used only when Type is set to NTP or AllSync. Additional options can be added to the end of each NTP server in the form of hex codes related to the associating mode it will run in.
- 0x01 – Special Interval – Specifies to use a special poll interval set by the “SpecialPollInterval” parameter.
- 0x02 – Use as Fallback Only
- 0x04 – (Default) Symmetric Active
- 0x08 – NTP Request in Client Mode
These values can be combined. For Example: 0x09 means SpecialInterval and NTP request in Client mode.
Most environments will typically use option 0x08.
Type: Indicates which peers to accept synchronization from:
- NoSync – The time service does not synchronize with other sources.
- NTP – The time service synchronizes from the servers specified in the NtpServer registry entry.
- NT5DS (Default) – The time service synchronizes from the domain hierarchy.
- AllSync – The time service uses all the available synchronization mechanisms. It uses the domain hierarchy first and will fall back to the value set for the NTP server if the domain hierarchy is not available.
The PDCe for the forest root domain should be set to NTP. All other DCs should be NT5DS.
The PDCe for the child (non-forest root) domains should be set to AllSync. All other DCs should be NT5DS.
AnnounceFlags: The value define how the DC announces itself as time server:
- 0x00 – Timeserv_Announce_No, Reliable_Timeserv_Announce_No. The domain controller does not advertise time service.
- 0x01 – Timeserv_Announce_Yes. The domain controller always advertises time service.
- 0x02 – Timeserv_Announce_Auto. The domain controller automatically determines whether it should advertise time service.
- 0x04 – Reliable_Timeserv_Announce_Yes. The domain controller will always advertise reliable time service.
- 0x08 – Reliable_Timeserv_Announce_Auto. The domain controller automatically determines whether it should advertise reliable time service.
This value combines flags using the bitwise OR operator. For each position in the binary value, if any flag has the value of 1 in the corresponding position, the combined value does as well. For example, the flags 1 and 8, which are 0001 and 1000 in binary, are 1001 when combined with bitwise OR.
0 simply means no announcements, and 1, 2, 4, 8 are the individual bits that decide whether the according function will be announced.
A value of 5 simply is 1+4, so “The domain controller always advertises time service.” and “The domain controller will always advertise reliable time service.”
10 (0xa) means 2+8, so “The domain controller automatically determines whether it should advertise time service.” and “The domain controller automatically determines whether it should advertise reliable time service.”
It’s been reported that even with Announceflags defaulting to 10 the PDCe may not announce itself as a reliable time server under certain scenarios. Therefore setting it to 5 on the PDCe is a considered best practice. All other DCs should be set to 10 as per default. Setting multiple DCs to 5 can cause problems, as explained by Dimitri Janczak in his NtpClient Error 0x800706E1 article.
Whilst Microsoft has published some Windows Time Service Tools and Settings on TechNet, the three (3) listed above are the most important.
Polling Intervals and Clock Corrections Explained:
- SpecialPollInterval: This value, expressed in seconds, controls how often a manually configured time source is polled when the time source is configured to use a special polling interval. If the SpecialInterval flag is enabled on the NTPServer setting, the client uses the value that is set as the SpecialPollInterval, instead of the MinPollInterval and MaxPollInterval values, to determine how frequently to poll the time source. The default value is 3600 seconds (1 hour).
If you’re going to use the SpecialPollInterval, you may consider setting it to 900 seconds (15 minutes).
- MinPollInterval: This value, expressed in log base-2 seconds, controls the minimum polling interval that defines the minimum amount of time between polls of a peer. The default value is 6, which is computed as 2 to the power of 6 and equals 64 seconds.
- MaxPollInterval: This value, expressed in log base-2 seconds, controls the maximum polling interval, which defines the maximum amount of time between polls of a peer. The default value is 10, which is computed as 2 to the power of 10 and equals 1,024 seconds (about 17 minutes). The time service itself is considered unsynchronized after 1.5 times the number of seconds that are specified by this entry have elapsed. NTP specifies that the maximum clock age is 86,400 seconds. Therefore, if the value of this entry is greater than 15 (32,768 seconds), peers will eventually ignore this server.
The MinPollInterval and MaxPollInterval values should not be changed, but be aware that as the actual value in seconds is calculated as a log base-2 number, the time will increase exponentially as you increase these values.
- MaxNegPhaseCorrection: This value, expressed in seconds, controls the maximum allowable clock correction that can be made in a reverse direction. If a time sample is received that indicates a time in the past (as compared to the client’s local clock) that has a time difference that is greater than the MaxNegPhaseCorrection value, the time sample is discarded. If this happens, the Windows Time source logs an event in the System log of Event Viewer. The default value is 172,800 seconds (48 hours).
- MaxPosPhaseCorrection: This value, expressed in seconds, controls the maximum allowable clock correction that can be made in a forward direction. If a time sample is received that indicates a time in the future (as compared to the client’s local clock) that has a time difference greater than the MaxPosPhaseCorrection value, the time sample is discarded. If this happens, the Windows Time source logs an event in the System log of Event Viewer. The default value is 172,800 seconds (48 hours).
Some consultants recommend changing the MaxNegPhaseCorrection and MaxPosPhaseCorrection. The Microsoft recommendation is to leave them at their default setting of 17,2800 seconds (48 hours).
Furthermore, it’s also extremely important to ensure your virtual Domain Controllers are correctly configured.
I highly recommend reading the following articles for Hyper-V environments:
- Time Sync Recommendations For Virtual DCs On Hyper-V – Change In Recommendations (AGAIN)
- Time Synchronization in Hyper-V
- Hyper V Time Synchronization on a Windows Based Network
The script is fully documented, and is based on a previous script written by Carl Webster.
The following screen shot shows the screen output.
You can then run the Get-GPO cmdlet to verify that the GPOs were created, User Settings Disabled, and the WMI Filters applied .
The following screen shot shows the GPOs linked to the Domain Controllers OU and WMI Filters .
It’s important to note that the script creates the GPOs with the Enabled (VMICTimeProvider) registry preference value disabled as per the following screen shot.
You then need to manually set Item-Level targeting before enabling the Enabled value so that this still gets applied to virtual machines ONLY as per best practice. We do it this way because there is no simple way to create/set item-level targeting settings via PowerShell without using a 3rd party product like SDM Software’s GP Automation Engine (GPAE).
- Right click on the Enabled preference setting
- Select Properties
- Select the Common tab
- Select Item-Level targeting to enable it
- Select Targeting…
- Add a Registry Match as per the following settings represented in the screen shot below:
- Match type: Match value data
- Value date match type: Substring match
- Hive: HKEY_LOCAL_MACHINE
- Key path: SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider
- Value name: DLLName
- Value type: REG_EXPAND_SZ
- Substring: vmictimeprovider.dll
- Select OK
- Select OK
- Right click on Enabled > All Tasks > Enable
You’ll need to do this for all 3 GPOs and then the implementation is complete.
Here is the CreateTimeServerGPOs.ps1 (1638 downloads) script:
#> This script will create the Time Server GPOs and WMI Filters for the Domain Controllers to ensure your time server hierarchy remains correct for transfer and seizure of the PDC(e) emulator FSMO role holder. The policies will apply on the next policy refresh or by forcing a group policy refresh. WMI Filters are created via the New-ADObject cmdlet in the Active Directory module, which makes them of type "Microsoft.ActiveDirectory.Management.ADObject". However, the Group Policy module requires that you use an object of type "Microsoft.GroupPolicy.WmiFilter" when adding a wmifilter using the New-GPO cmdlet. Therefore there is no default way to use the Group Policy PowerShell cmdlets to add WMI Filters to GPOs without a bit or trickery. As Carl documented there is a "Group Policy WMI filter cmdlet module" available for download from here: http://gallery.technet.microsoft.com/scriptcenter/Group-Policy-WMI-filter-38a188f3 But if you reverse engineer the code Bin Yi from Microsoft created, you'll see that he has simply and cleverly converted a "Microsoft.ActiveDirectory.Management.ADObject" object type to a "Microsoft.GroupPolicy.WmiFilter" object type. I didn't want to include the whole module for the simple task I needed, so have directly used the ConvertTo-WmiFilter function from the GPWmiFilter.psm1 module and tweaked it for my requirements. Many thanks to Bin. If your Active Directory is based on Windows 2003 or has been upgraded from Windows 2003, you may may have an issue with System Owned Objects. If you receive an error message along the lines of "The attribute cannot be modified because it is owned by the system", you'll need to set the following registry value: Key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\NTDS\Parameters Type: REG_DWORD Value: Allow System Only Change Data: 1 Disable the Hyper-V time synchronization integration service: - The time source of "VM IC Time Synchronization Provider" (vmictimeprovider.dll) is enabled on Virtual Machines as part of the Hyper-V Integration Services. The following articles explain it in more depth and how it should be configured: - Time Sync Recommendations For Virtual DCs On Hyper-V – Change In Recommendations (AGAIN) (2013-11-17) Time Sync Recommendations For Virtual DCs On Hyper-V – Change In Recommendations (AGAIN) - Time Synchronization in Hyper-V: http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/11/19/time-synchronization-in-hyper-v.aspx - Hyper V Time Synchronization on a Windows Based Network: http://kevingreeneitblog.blogspot.com.au/2011/01/hyper-v-time-synchronization-on-windows.html Recommended Default Values for: - MaxPosPhaseCorrection: 172800 - MaxNegPhaseCorrection: 172800 Recommended Default Values for Domain Controllers: - SpecialPollInterval: 3600 This is only initiated on workgroup servers and the PDCe when a flag of 0x1 or 0×9 is specified against any of the manually specified NTP servers. References: - KB2638243 to understand more about when SpecialPollInterval is used. - https://nchrissos.wordpress.com/2013/04/26/configuring-time-on-windows-2008-r2-servers/ Even after a GPUpdate has occurred and a restart of the Windows Time (W32Time) service you may find that the output of a "w32tm /query /source" and "w32tm /query /status" shows that it's source is the "Local CMOS Clock". Simply run the "w32tm /resync /rediscover" command to force the system to rediscover from its configured sources. This seems to address the issue immediately. Script Name: CreateTimeServerGPOs.ps1 Release 1.2 Written by Jeremy@jhouseconsulting.com 19/10/2015 Original script was written by Carl Webster: - Carl Webster, CTP and independent consultant - webster@carlwebster.com - @carlwebster on Twitter - http://www.CarlWebster.com - It can be found here:Creating a Group Policy using Microsoft PowerShell to Configure the Authoritative Time Server<# #------------------------------------------------------------- param([switch]$whatif) Set-StrictMode -Version 2.0 $VerbosePreference = 'Continue' $WarningPreference = 'Continue' $ErrorPreference = 'Continue' if ($whatif.IsPresent) { $WhatIfPreference = $True Write-Verbose "WhatIf Enabled" } Else { $WhatIfPreference = $False } #------------------------------------------------------------- # Define variables specific to your Active Directory environment # Set this to the NTP Servers the PDCe will sync with $TimeServers = "0.au.pool.ntp.org,0x8 1.au.pool.ntp.org,0x8 2.au.pool.ntp.org,0x8 3.au.pool.ntp.org,0x8" # This is the name of the GPO for the PDCe policy $PDCeGPOName = "+ SERVER Set PDCe Domain Controller as Authoritative Time Server v1.0" # This is the WMI Filter for the PDCe Domain Controller $PDCeWMIFilter = @("PDCe Domain Controller", "Queries for the domain controller that holds the PDC emulator FSMO role", "root\CIMv2", "Select * from Win32_ComputerSystem where DomainRole=5") # This is the name of the GPO for the non-PDCe policy $NonPDCeGPOName = "+ SERVER Set Time Settings on non-PDCe Domain Controllers v1.0" # This is the WMI Filter for the non-PDCe Domain Controllers $NonPDCeWMIFilter = @("Non-PDCe Domain Controllers", "Queries for all domain controllers except for the one that holds the PDC emulator FSMO role", "root\CIMv2", "Select * from Win32_ComputerSystem where DomainRole=4") # This is the name of the GPO for the Domain Member policy $DomainMembersGPOName = "+ COMPUTER Set Time Settings on all Domain Members v1.0" # Set this to True to include the registry value to disable the Virtual Host Time Synchronization provider (VMICTimeProvider) $DisableVirtualHostTimeSynchronization = $True # Set this to true to set the Allow System Only Change registry value $EnableAllowSystemOnlyChange = $True # Set this to the number of seconds you would like to wait for Active Directory replication # to complete before retrying to add the WMI filter to the Group Policy Object (GPO). $SleepTimer = 10 #------------------------------------------------------------- # Import the Active Directory Module Import-Module ActiveDirectory -WarningAction SilentlyContinue if ($Error.Count -eq 0) { Write-Verbose "Successfully loaded Active Directory Powershell's module" } else { Write-Error "Error while loading Active Directory Powershell's module : $Error" exit } # Import the Group Policy Module Import-Module GroupPolicy -WarningAction SilentlyContinue if ($Error.Count -eq 0) { Write-Verbose "Successfully loaded Group Policy Powershell's module" } else { Write-Error "Error while loading Group Policy Powershell's module : $Error" exit } #------------------------------------------------------------- # Get the Current Domain & Forest Information $DomainInfo = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $DomainName = $DomainInfo.Name $ForestName = $DomainInfo.Forest.Name # Get AD Distinguished Name $DomainDistinguishedName = $DomainInfo.GetDirectoryEntry() | select -ExpandProperty DistinguishedName If ($DomainName -eq $ForestName) { $IsForestRoot = $True } Else { $IsForestRoot = $False } #------------------------------------------------------------- function ConvertTo-WmiFilter([Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject) { # The concept of this function has been taken directly from the GPWmiFilter.psm1 module # written by Bin Yi from Microsoft. I have modified it to allow for the challenges of # Active Directory replication. It will return the WMI filter as an object of type # "Microsoft.GroupPolicy.WmiFilter". $gpDomain = New-Object -Type Microsoft.GroupPolicy.GPDomain $ADObject | ForEach-Object { $path = 'MSFT_SomFilter.Domain="' + $gpDomain.DomainName + '",ID="' + $_.Name + '"' $filter = $NULL try { $filter = $gpDomain.GetWmiFilter($path) } catch { write-Error "The WMI filter could not be found." } if ($filter) { [Guid]$Guid = $_.Name.Substring(1, $_.Name.Length - 2) $filter | Add-Member -MemberType NoteProperty -Name Guid -Value $Guid -PassThru | Add-Member -MemberType NoteProperty -Name Content -Value $_."msWMI-Parm2" -PassThru } else { write-Warning "Waiting $SleepTimer seconds for Active Directory replication to complete." start-sleep -s $SleepTimer write-warning "Trying again to retrieve the WMI filter." ConvertTo-WmiFilter $ADObject } } } #------------------------------------------------------------- function Enable-ADSystemOnlyChange([switch] $disable) { # This function has been taken directly from the GPWmiFilter.psm1 # module written by Bin Yi from Microsoft. $valueData = 1 if ($disable) { $valueData = 0 } $key = Get-Item HKLM:\System\CurrentControlSet\Services\NTDS\Parameters -ErrorAction SilentlyContinue if (!$key) { New-Item HKLM:\System\CurrentControlSet\Services\NTDS\Parameters -ItemType RegistryKey | Out-Null } $kval = Get-ItemProperty HKLM:\System\CurrentControlSet\Services\NTDS\Parameters -Name "Allow System Only Change" -ErrorAction SilentlyContinue if (!$kval) { New-ItemProperty HKLM:\System\CurrentControlSet\Services\NTDS\Parameters -Name "Allow System Only Change" -Value $valueData -PropertyType DWORD | Out-Null } else { Set-ItemProperty HKLM:\System\CurrentControlSet\Services\NTDS\Parameters -Name "Allow System Only Change" -Value $valueData | Out-Null } } #------------------------------------------------------------- Function Create-Policy { param($GPOName,$TargetOU,$NtpServer,$AnnounceFlags,$Type,$MaxPosPhaseCorrection,$MaxNegPhaseCorrection,$SpecialPollInterval,$WMIFilter) If ($WMIFilter -ne "none") { $UseAdministrator = $False If ($UseAdministrator -eq $False) { $msWMIAuthor = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name } Else { $msWMIAuthor = "Administrator@" + [System.DirectoryServices.ActiveDirectory.Domain]::getcurrentdomain().name } # Create WMI Filter $WMIGUID = [string]"{"+([System.Guid]::NewGuid())+"}" $WMIDN = "CN="+$WMIGUID+",CN=SOM,CN=WMIPolicy,CN=System,"+$DomainDistinguishedName $WMICN = $WMIGUID $WMIdistinguishedname = $WMIDN $WMIID = $WMIGUID $now = (Get-Date).ToUniversalTime() $msWMICreationDate = ($now.Year).ToString("0000") + ($now.Month).ToString("00") + ($now.Day).ToString("00") + ($now.Hour).ToString("00") + ($now.Minute).ToString("00") + ($now.Second).ToString("00") + "." + ($now.Millisecond * 1000).ToString("000000") + "-000" $msWMIName = $WMIFilter[0] $msWMIParm1 = $WMIFilter[1] + " " $msWMIParm2 = "1;3;10;" + $WMIFilter[3].Length.ToString() + ";WQL;" + $WMIFilter[2] + ";" + $WMIFilter[3] + ";" # msWMI-Name: The friendly name of the WMI filter # msWMI-Parm1: The description of the WMI filter # msWMI-Parm2: The query and other related data of the WMI filter $Attr = @{"msWMI-Name" = $msWMIName;"msWMI-Parm1" = $msWMIParm1;"msWMI-Parm2" = $msWMIParm2;"msWMI-Author" = $msWMIAuthor;"msWMI-ID"=$WMIID;"instanceType" = 4;"showInAdvancedViewOnly" = "TRUE";"distinguishedname" = $WMIdistinguishedname;"msWMI-ChangeDate" = $msWMICreationDate; "msWMI-CreationDate" = $msWMICreationDate} $WMIPath = ("CN=SOM,CN=WMIPolicy,CN=System,"+$DomainDistinguishedName) $array = @() $SearchRoot = [adsi]("LDAP://CN=SOM,CN=WMIPolicy,CN=System,"+$DomainDistinguishedName) $search = new-object System.DirectoryServices.DirectorySearcher($SearchRoot) $search.filter = "(objectclass=msWMI-Som)" $results = $search.FindAll() ForEach ($result in $results) { $array += $result.properties["mswmi-name"].item(0) } if ($array -notcontains $msWMIName) { write-Verbose "Creating the $msWMIName WMI Filter..." If ($EnableAllowSystemOnlyChange) { Enable-ADSystemOnlyChange } $SOMContainer = [adsi]("LDAP://CN=SOM,CN=WMIPolicy,CN=System,"+$DomainDistinguishedName) $NewWMIFilter = $SOMContainer.create('msWMI-Som',"CN="+$WMIGUID) $NewWMIFilter.put("msWMI-Name",$msWMIName) $NewWMIFilter.put("msWMI-Parm1",$msWMIParm1) $NewWMIFilter.put("msWMI-Parm2",$msWMIParm2) $NewWMIFilter.put("msWMI-Author",$msWMIAuthor) $NewWMIFilter.put("msWMI-ID",$WMIID) $NewWMIFilter.put("instanceType",4) $NewWMIFilter.put("showInAdvancedViewOnly","TRUE") $NewWMIFilter.put("distinguishedname",$WMIdistinguishedname) $NewWMIFilter.put("msWMI-ChangeDate",$msWMICreationDate) $NewWMIFilter.put("msWMI-CreationDate",$msWMICreationDate) If ($WhatIfPreference -eq $False) { $NewWMIFilter.setinfo() } write-Verbose "Waiting $SleepTimer seconds for Active Directory replication to complete." start-sleep -s $SleepTimer } Else { write-Warning "The $msWMIName WMI Filter already exists" } # Get WMI filter <# $SearchRoot = [adsi]("LDAP://CN=SOM,CN=WMIPolicy,CN=System,"+$DomainDistinguishedName) $search = new-object System.DirectoryServices.DirectorySearcher($SearchRoot) $search.filter = "(&(objectclass=msWMI-Som)(mswmi-name=$msWMIName))" $results = $search.FindAll() ForEach ($result in $results) { # To create a WmiFilter object using the ConvertTo-WmiFilter function we need to # first create an object with the following 7 properties: # DistinguishedName, msWMI-Name, msWMI-Parm1, msWMI-Parm2, Name, ObjectClass, ObjectGUID #$WMIFilterADObject = New-Object -TypeName Microsoft.ActiveDirectory.Management.ADObject # There is an Get-ADSIResult function written by Warren Frame that will achieve this: # - https://github.com/RamblingCookieMonster/PowerShell/blob/master/Get-ADSIObject.ps1 # - https://gallery.technet.microsoft.com/scriptcenter/Get-ADSIObject-Portable-ae7f9184 #$WMIFilterADObject | Add-Member -MemberType NoteProperty -Name "DistinguishedName" -value $result.properties["distinguishedname"].item(0) #$WMIFilterADObject | Add-Member -MemberType NoteProperty -Name "msWMI-Name" -value $result.properties["mswmi-name"].item(0) #$WMIFilterADObject | Add-Member -MemberType NoteProperty -Name "msWMI-Parm1" -value $result.properties["mswmi-parm1"].item(0) #$WMIFilterADObject | Add-Member -MemberType NoteProperty -Name "msWMI-Parm2" -value $($result.properties["mswmi-parm2"].item(0)) #$WMIFilterADObject | Add-Member -MemberType NoteProperty -Name "Name" -value $result.properties["name"].item(0) #$WMIFilterADObject | Add-Member -MemberType NoteProperty -Name "ObjectClass" -value "msWMI-Som" ## Convert the ObjectGUID property byte array to a GUID #[GUID]$GUID = $result.properties["ObjectGUID"].item(0) #$WMIFilterADObject | Add-Member -MemberType NoteProperty -Name "ObjectGUID" -value $GUID $WMIFilterADObject = New-Object -TypeName Microsoft.ActiveDirectory.Management.ADObject $WMIFilterADObject.DistinguishedName = $result.properties["distinguishedname"].item(0) $WMIFilterADObject."msWMI-Name" = $result.properties["mswmi-name"].item(0) $WMIFilterADObject."msWMI-Parm1" = $result.properties["mswmi-parm1"].item(0) $WMIFilterADObject."msWMI-Parm2" = ($result.properties["mswmi-parm2"].item(0)).ToString() #$WMIFilterADObject.Name = $result.properties["name"].item(0) $WMIFilterADObject.ObjectClass = "msWMI-Som" # Convert the ObjectGUID property byte array to a GUID [GUID]$GUID = $result.properties["ObjectGUID"].item(0) $WMIFilterADObject.ObjectGUID = $GUID } #> $WMIFilterADObject = Get-ADObject -Filter 'objectClass -eq "msWMI-Som"' -Properties "msWMI-Name","msWMI-Parm1","msWMI-Parm2" | Where {$_."msWMI-Name" -eq "$msWMIName"} #$WMIFilterADObject #$WMIFilterADObject | gm –Force #ConvertTo-WmiFilter $WMIFilterADObject } $ExistingGPO = get-gpo $GPOName -ea "SilentlyContinue" If ($ExistingGPO -eq $NULL) { write-Verbose "Creating the $GPOName Group Policy Object..." If ($WhatIfPreference -eq $False) { $GPO = New-GPO -Name $GPOName write-verbose "Disabling User Settings" $GPO.GpoStatus = "UserSettingsDisabled" } If ($WMIFilter -ne "none") { If ($WhatIfPreference -eq $False) { Write-Verbose "Adding the WMI Filter" $GPO.WmiFilter = ConvertTo-WmiFilter $WMIFilterADObject } } If ($WhatIfPreference -eq $False) { write-verbose "Setting the registry keys in the Preferences section of the new GPO" Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer ` -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" ` -Type DWord -ValueName "AnnounceFlags" -Value $AnnounceFlags | out-null Write-Verbose "Set AnnounceFlags to a value of $AnnounceFlags" If ($MaxPosPhaseCorrection -ne "default") { Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer ` -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" ` -Type DWord -ValueName "MaxPosPhaseCorrection" -Value $MaxPosPhaseCorrection | out-null Write-Verbose "Set MaxPosPhaseCorrection to a value of $MaxPosPhaseCorrection" } If ($MaxNegPhaseCorrection -ne "default") { Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer ` -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" ` -Type DWord -ValueName "MaxNegPhaseCorrection" -Value $MaxNegPhaseCorrection | out-null Write-Verbose "Set MaxNegPhaseCorrection to a value of $MaxNegPhaseCorrection" } If ($SpecialPollInterval -ne "default") { Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer ` -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient" ` -Type DWord -ValueName "SpecialPollInterval" -Value $SpecialPollInterval | out-null Write-Verbose "Set SpecialPollInterval to a value of $SpecialPollInterval" } Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer ` -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" ` -Type String -ValueName "NtpServer" -Value "$NtpServer" | out-null Write-Verbose "Set NtpServer to a value of $NtpServer" Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer ` -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" ` -Type String -ValueName "Type" -Value "$Type" | out-null Write-Verbose "Set Type to a value of $Type" If ($DisableVirtualHostTimeSynchronization) { # Disable the Hyper-V/ESX time synchronization integration service. Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer ` -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider" ` -Type DWord -ValueName "Enabled" -Value 0 -Disable | out-null Write-Verbose "Disabled the VMICTimeProvider" } # Link the new GPO to the specified OU write-Verbose "Linking the $GPOName Group Policy Object to the $TargetOU OU..." New-GPLink -Name $GPOName -Target "$TargetOU" | out-null } } Else { write-Warning "The $GPOName Group Policy Object already exists." If ($WMIFilter -ne "none") { write-Verbose "Adding the $msWMIName WMI Filter..." If ($WhatIfPreference -eq $False) { $ExistingGPO.WmiFilter = ConvertTo-WmiFilter $WMIFilterADObject } write-Verbose "Linking the $GPOName Group Policy Object to the $TargetOU OU..." If ($WhatIfPreference -eq $False) { Try { New-GPLink -Name $GPOName -Target "$TargetOU" -errorAction Stop | out-null } Catch { write-verbose "The GPO is already linked" } } } } write-Verbose "Completed." $ObjectExists = $NULL } #------------------------------------------------------------- If ($IsForestRoot) { $PDCeType = "NTP" } Else { $PDCeType = "AllSync" } $TargetDCOU = "OU=Domain Controllers," + $DomainDistinguishedName # Syntax: # Create-Policy <GPOName> <TargetOU> <NtpServer> <AnnounceFlags> <Type> <MaxPosPhaseCorrection> <MaxNegPhaseCorrection> <SpecialPollInterval> <WMIFilter> Write-Verbose "Creating the WMI Filters and Policies..." Create-Policy "$PDCeGPOName" "$TargetDCOU" "$TimeServers" 5 $PDCeType 172800 172800 3600 $PDCeWMIFilter Create-Policy "$NonPDCeGPOName" "$TargetDCOU" "time.windows.com,0x9" 10 "NT5DS" 172800 172800 "default" $NonPDCeWMIFilter Create-Policy "$DomainMembersGPOName" "$DomainDistinguishedName" "time.windows.com,0x9" 10 "NT5DS" 172800 172800 "default" "none"
References:
- Industry Blog: Creating a Group Policy using Microsoft PowerShell to Configure the Authoritative Time Server
- Microsoft TechNet Blog: Configuring an Authoritative Time Server with Group Policy Using WMI Filtering
- Industry Blog: Configuring And Managing The Windows Time Service (Part 1)
- Industry Blog: Configuring And Managing The Windows Time Service (Part 2)
- Industry Blog: Configuring And Managing The Windows Time Service (Part 3)
- Industry Blog: Configuring And Managing The Windows Time Service (Part 4)
- Microsoft TechNet: Windows Time Service Tools and Settings
- Microsoft MSDN Blog: Group Policy Settings Explained
- Microsoft TechNet Script Center: Group Policy WMI filter cmdlet module
- Industry Blog: Time Sync Recommendations For Virtual DCs On Hyper-V – Change In Recommendations (AGAIN)
- Microsoft MSDN Blog: Time Synchronization in Hyper-V
- Industry Blog: Hyper V Time Synchronization on a Windows Based Network
- Industry Blog: NtpClient Error 0x800706E1
Enjoy!