Public/Create-TimeSourceGPO.ps1
Function Create-TimeSourceGPO { $TimeServers = "0.pool.ntp.org,0x8 1.pool.ntp.org,0x8 2.pool.ntp.org,0x8 3.pool.ntp.org,0x8" #======================================================== # Reset powershell session #======================================================== Clear-Host $error.Clear() ##################################################################################################################################################### #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # Function: Load-Module # Purpose : Import Powershell module with validation #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Function Report-Status { Param( [parameter(Mandatory = $true)][String]$Msg, [parameter(Mandatory = $true)][INT]$Lvl, [parameter(Mandatory = $true)][String]$Color ) Switch ($Lvl) { 0 { Write-Host -Foreground $Color $Msg } 1 { Write-Host -Foreground $Color " -" $Msg } 2 { Write-Host -Foreground $Color " *" $Msg } } } #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # Function: Load-Module # Purpose : Import Powershell module with validation #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Function Load-Module { Param( [parameter(Mandatory = $true)] [String] $ModuleName, [parameter(Mandatory = $true)] [String] $Description ) If ( ! (Get-Module $ModuleName)) { Report-Status "Importing $ModuleName module" 1 Green Import-Module $ModuleName -WarningAction SilentlyContinue } if ($Error.Count -ne 0) { Report-Status "Error while loading $Description module : $Error`n" 1 Red exit } } #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # Function: Convert-IpAddressToMaskLength #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= function Convert-IpAddressToMaskLength([string] $dottedIpAddressString) { $result = 0; # ensure we have a valid IP address [IPAddress] $ip = $dottedIpAddressString; $octets = $ip.IPAddressToString.Split('.'); foreach ($octet in $octets) { while (0 -ne $octet) { $octet = ($octet -shl 1) -band [byte]::MaxValue $result++; } } return $result; } #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # Function: ConvertTo-WMIFilter #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= function ConvertTo-WmiFilter([Microsoft.ActiveDirectory.Management.ADObject[]] $ADObject) { $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 { Report-Status "The WMI filter could not be found." 1 Red } 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 { Report-Status "Waiting $SleepTimer seconds for Active Directory replication to complete." 1 Yellow Start-Sleep -s $SleepTimer Report-Status "Trying again to retrieve the WMI filter." 1 Yellow ConvertTo-WmiFilter $ADObject } } } #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # Function: Create-TimeSyncGPO # Purpose : Create Custom GPO for NTP configuration #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Function Create-TimeSyncGPO { param($GPOName, $NtpServer, $AnnounceFlags, $Type, $WMIFilter) If ($AllowSystemOnlyChange) { New-ItemProperty "HKLM:\System\CurrentControlSet\Services\NTDS\Parameters" -name "Allow System Only Change" -value 1 -propertyType dword -EA 0 } $UseAdministrator = $False If ($UseAdministrator -eq $False) { $msWMIAuthor = (Get-ADUser $env:USERNAME).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," + $defaultNC $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," + $defaultNC) $ExistingWMIFilters = Get-ADObject -Filter 'objectClass -eq "msWMI-Som"' -Properties "msWMI-Name", "msWMI-Parm1", "msWMI-Parm2" $array = @() If ($ExistingWMIFilters -ne $NULL) { foreach ($ExistingWMIFilter in $ExistingWMIFilters) { $array += $ExistingWMIFilter."msWMI-Name" } } Else { $array += "no filters" } Report-Status "Creating the $msWMIName WMI Filter" 1 Green if ($array -notcontains $msWMIName) { $WMIFilterADObject = New-ADObject -name $WMICN -type "msWMI-Som" -Path $WMIPath -OtherAttributes $Attr } Else { Report-Status "The $msWMIName WMI Filter already exists." 2 Yellow } $WMIFilterADObject = $NULL # Get WMI filter $WMIFilterADObject = Get-ADObject -Filter 'objectClass -eq "msWMI-Som"' -Properties "msWMI-Name", "msWMI-Parm1", "msWMI-Parm2" | Where-Object { $_."msWMI-Name" -eq "$msWMIName" } Report-Status "Creating the $GPOName Group Policy Object" 1 Green $ExistingGPO = get-gpo $GPOName -ea "SilentlyContinue" If ($ExistingGPO -eq $NULL) { # Create new GPO shell $GPO = New-GPO -Name $GPOName # Disable User Settings $GPO.GpoStatus = "UserSettingsDisabled" # Add the WMI Filter $GPO.WmiFilter = ConvertTo-WmiFilter $WMIFilterADObject # Set the three 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 Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" -Type String -ValueName "NtpServer" -Value "$NtpServer" | Out-Null Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" -Type String -ValueName "Type" -Value "$Type" | Out-Null # Disable the Hyper-V time synchronization integration service. If ($DisableHyperVTimeSynchronization) { Set-GPPrefRegistryValue -Name $GPOName -Action Update -Context Computer -Key "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider" -Type DWord -ValueName "Enabled" -Value 0 | Out-Null } # Link the new GPO to the Domain Controllers OU Report-Status "Linking the $GPOName Group Policy Object to $DomainControllersOU" 1 Green $Result = New-GPLink -Name $GPOName -Target "$DomainControllersOU" } Else { Report-Status "The $GPOName Group Policy Object already exists." 2 Yellow Report-Status "Applying the $msWMIName WMI Filter" 1 Green $ExistingGPO.WmiFilter = ConvertTo-WmiFilter $WMIFilterADObject } $ObjectExists = $NULL } #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #======================================================== # Import the required modules #======================================================== Report-Status "Loading required modules" 0 cyan Load-Module "ActiveDirectory" "Active Directory" Load-Module "GroupPolicy" "Group Policy" #======================================================== # Global Variables #======================================================== # Set source path for all configuration files $WorkFolderPath = $PSScriptRoot # Set Active Directory Domain FQDN and NetBIOS names $ADDomain = Get-ADDomain $ADNetBIOS = $ADDomain.NetBIOSName $ADDNSRoot = $ADDomain.DNSRoot #======================================================== # Time Source Variables #======================================================== # This is the name of the GPO for the PDCe policy $PDCeGPOName = "SYST-DomainControllers_TimeSource-PDCe" # This is the WMI Filter for the PDCe Domain Controller $PDCeWMIFilter = @("Domain Controller (PDCe)", "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 = "SYST-DomainControllers_TimeSource" # This is the WMI Filter for the non-PDCe Domain Controllers $NonPDCeWMIFilter = @("Domain Controllers (Non-PDCe)", "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") # Set this to True to include the registry value to disable the Hyper-V Time Synchronization $DisableHyperVTimeSynchronization = $True # Set this to True if you need to set the "Allow System Only Change" value. $AllowSystemOnlyChange = $False # 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 # Set Target OU for Domain Controllers $defaultNC = ([ADSI]"LDAP://RootDSE").defaultNamingContext.Value $DomainControllersOU = "OU=Domain Controllers," + $defaultNC #======================================================== # Configure GPOs #======================================================== # Create Domain Controller GPOs and WMI Filters for Time Synchronisation Report-Status "Creating Time Sync GPOs for Domain Controllers" 0 cyan Create-TimeSyncGPO "$PDCeGPOName" "$TimeServers" 5 "NTP" $PDCeWMIFilter Create-TimeSyncGPO "$NonPDCeGPOName" "" 10 "NT5DS" $NonPDCeWMIFilter } |