Updating Group Policy Objects Remotely

One of the recommendations I always give people who ask my opinion on updating to new versions of Windows is that if you do upgrade or deploy new servers to always do your Active Directory Domain Controllers first. By updating the DCs first one can start implementing stronger authentication as clients are migrated and also start implementing policies that address the new versions of Windows as they start joining the domain.

One of the feature I like on Windows 2012 and Windows 2012 R2 is the starter GPO for allowing the PowerShell cmdlet Invoke-GPUpdate to remotely schedule gpupdate.exe so as to update GPO settings at a time of our choosing. This works all version of windows starting with Windows 2008 and Windows 7 and above from a Windows 2012 or Windows 8 host with the latest Remote Administration Tools. If you are like me you probably had a bash script with PsExec from Sysinternals, a PowerShell script that created a process with WMI or use Invoke-Command with PowerShell remoting. As you can see there are many ways to achieve this but the simplest I have found using command line using Invoke-GPUpdate. Before we are able to use it we first need to make sure we open the correct ports. We will need to enable the following firewall rules:

  • Remote Scheduled Tasks Management (RPC)
  • Remote Scheduled Tasks Management (RPC-ERMAP)
  • Windows Management Instrumentation (WMI-IN)

Thankfully for this Windows 2012 and Windows 2012 R2 come with a started GPO to server as a base for the configuration.

Configuring Windows Firewall

To configure the Windows Firewall we start first by creating all the Starter GPOs from the Group Policy Management console expanding the domain, selecting Starters GPOs and clicking on Create Starter GPOs Folder.

Once create we can use PowerShell to create a new GPO based on the Group Policy Remote Update Firewall Ports starter GPO and link the GPO to the OU or domain we want to apply this rule to. In a lab setting you may want to apply it to the whole domain in one single command.

In production I would recommend first creating the GPO, modifying the firewall rule so only management hosts can connect to the ports so as to minimize the risk of use of WMI for lateral movement by an attacker.

In the example bellow we list the starter GPO names and create a new GPO from it an link it to the root of the domain.

List Starter GPO names:

 Get-GPStarterGPO -all | Select-Object -Property DisplayName | Sort-Object -Property DisplayName

Create GPO and link it to root domain:

PS C:\> New-GPO -Name "Remote Management Automation" -StarterGpoName "Group Policy Remote Update Firewall Ports" | New-GPLink -Target "DC=acopr,DC=local" -LinkEnabled yes

Sadly we will have to wait 90 minutes to make sure the policy is applied to all hosts we want to update GPOs remotely.

Updating GPOs Remotely

Once the firewall rules have been enabled we can now use the cmdlet Invoke-GPUpdate from the GroupPolicy module installed on all Domain Controllers or hosts running the latest version of the Remote Server Administration Tools (RSAT). to schedule a task that will run gpupdate.exe at the time of our choosing and with the parameters we want. The real flexibility is it s nor so much on the Invoke-GPUdate cmdlet but in that we can pipe in to it the objects from the Get-ADComputer cmdlet in the ActiveDirectory PowerShell module from the Remote Server Administration Tools allowing us to filter by all sorts of properties and selecting only certain OUs in the domain structure. The Invoke-GPUpdate cmdlet is a simple one:

PS C:\Users\Administrator> help Invoke-GPUpdate

NAME
    Invoke-GPUpdate

SYNOPSIS
    Schedule a remote Group Policy refresh (gpupdate) on the specified computer.


SYNTAX
    Invoke-GPUpdate [[-Computer] < String >] [[-RandomDelayInMinutes] < Int32 >] [-AsJob] [-Boot] [-Force] [-LogOff]
    [-Target < String >] []

    Invoke-GPUpdate [[-Computer] < String >] [[-RandomDelayInMinutes] < Int32 >] [-AsJob] [-Boot] [-LogOff] [-Sync]
    [-Target < String >] []


DESCRIPTION
    The Invoke-GPUpdate cmdlet refreshes Group Policy settings, including security settings that are set on remote
    computers by scheduling the running of the Gpupdate command on a remote computer. You can combine this cmdlet in a
    scripted fashion to schedule the Gpupdate command on a group of computers.

    The refresh can be scheduled to immediately start a refresh of policy settings or wait for a specified period of
    time, up to a maximum of 31 days. To avoid putting a load on the network, the refresh times will be offset by a
    random delay.


RELATED LINKS
    Online Version: http://go.microsoft.com/fwlink/?linkid=287723

REMARKS
    To see the examples, type: "get-help Invoke-GPUpdate -examples".
    For more information, type: "get-help Invoke-GPUpdate -detailed".
    For technical information, type: "get-help Invoke-GPUpdate -full".
    For online help, type: "get-help Invoke-GPUpdate -online"

The parameters we will use in our example are:

  • RandomDelayInMinutes - Specifies the time that the task will wait once created to run. A random interval will be added to this number so as to prevent all hosts from hitting the Domain Controller at once and updating their policy. If a value of 0 is given the task will run immediately after creation. Great care should be taken when a value of 0 is used to as to prevent flooding the domain controller with requests.
  • Force - the switch will force an update of the entire GPO settings and not only those that have changed or are new.

There are other parameters to specify the target to update (Computer or User) and to also loggoff users or reboot the machine. To see a full list of options and example you can use the Get-Help cmdlet with the -Full switch.

 Get-Help Invoke-GPUpdate -Full

Since the lab domain I'm using is a fairly small one of only 3 hosts at the moment I will enumerate all hosts in the domain, save them in to a variable and loop over them to update the GPO. Do to that the Computer parameter in Invoke-GPUpdate only takes from the pipeline by Value we have to use the ForEach-Object cmdlet to go one by one of the onjects on the pipeline and run the cmdlet serially.

You will be able to see the task running on the host and updating the policy in the example bellow.

PS C:\> $computers = Get-ADComputer -Filter *
PS C:\> $computers | ForEach-Object -Process {Invoke-GPUpdate -Computer $_.name -RandomDelayInMinutes 0 -Force}

As always I hope you find the information useful.