Find out if your AGPM archive needs updating
For those of you out there using Advanced Group Policy Management a.k.a. AGPM, I have a question: how do you know that your AGPM archive still reflects the reality in Active Directory?
Thought about it? Good. There is a thorny issue here that caused a lot of problems already. AGPM flat-out assumes that its archive is the truth and contains the most recent information about any Group Policy Object that it manages. But this assumption is not always valid. You may have changes to a GPO in AD directly, out of necessity or by accident. The most common variation is this:
- You edit and deploy a GPO using AGPM.
- You change the security filtering on the GPO, limiting the GPO to members in a certain security group. At this point, the AD version of the GPO differs from the one in AGPM.
- Time passes, and after a while you edit the GPO again in AGPM.
- After approval, you deploy to production. AGPM is leading, and overwrites whatever is there, including permissions. The custom security filtering is now removed and the GPO gets applied to the wrong set of users/computers.
You get the point. Any situation where the AD version of a GPO is more recent than the version in AGPM is a problem. The other way around is usually not an issue; this just reflects a GPO being edited.
How do you fix this problem? AGPM has a neat solution. In the APGM console (GPMC --> Change Management), you select the GPO and do right-click, Import, From Production. This will read whatever is in the production GPO right now and promotes this to the most recent version in the archive. If you now start an edit session, you will be based of the correct version. The trick to fixing the problem is, of course, in knowing which GPO is affected.
How to figure out which GPOs are more recent in AD than in AGPM is not so easy, short of checking all GPO's in the archive. In fact, that's exactly what I ended up doing. I wrote a Powershell script to parse the AGPM archive for all managed GPOs and to compare timestamps to the GPO version in AD. Let's have a look at the relevant parts.
Update: if you just want to run the script, go here: https://gallery.technet.microsoft.com/scriptcenter/Find-out-which-GPOs-in-7e798661. The remainder of this article is a discussion of how the script internals work.
The first step is to find out where the AGPM archive is. We require that the script is run on the AGPM server itself, so we can query the local registry directly to find the archive path. Then we apply some PowerShell magic to parse the AGPM state file as XML, ready for use.
[powershell]
$archivepath = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Agpm -Name archivepath
$path = $archivepath.archivepath
[[xml]] $archive = get-content (Join-Path $path "gpostate.xml")
[/powershell]
Although seldom used as such, AGPM has the possibility to manage all domains in a forest so we must assume that the AGPM archive has multiple domains. This loop iterates over the domains, gets all managed GPOs, and builds an array with the domain list ($domainlist), and a hash with GPO GUID as key and the AGPM timestamp as value ($agpmtime).
[powershell]
foreach ($gpodomain in $archive.Archive.GPODomain)
{
$domainlist += $gpodomain.domain
foreach ($gpo in $gpodomain.GPO) {
$agpmtime.Add($gpo.id, $gpo.state.time)
}
}
[/powershell]
Then we start looping over all GPOs, and the next item of interest is determining if an import of a particular GPO is needed. We take the difference between the timestamps of the AGPM Archive and the GPO in AD, corrected for daylight saving time ($dstcorrection, calculation not shown). In principle, an AGPM import of the GPO is needed if the AD timestamp is more recent. The reality is a bit more complex, as it turns out. AGPM needs some time to write the GPO to AD, and you may have time differences in the forest leading to different timestamps. So a fudge factor based on the time difference in seconds is needed, as shown below.
[powershell]
$delta = New-TimeSpan -Start $archivetime -End $adtime.AddHours(-$dstcorrection)
$needsimport = switch ($delta.TotalSeconds)
{
{ $_ -le 0 } { "no"; break }
{ $_ -le 5 } { "maybe"; break }
{ $_ -le 60 } { "probably"; break }
{ $_ -gt 60 } { "yes"; break }
}
[/powershell]
And that's basically it. While still looping over the GPOs we dump the result into the pipeline for further processing, using the neat method of [PSCustomObject] to cast the output into a PowerShell object that is easy to handle.
Usage is simple. The script has no argument and is invoked like this:
[powershell]
Find-AGPMImportNeeded | Out-Gridview
[/powershell]
When ran in my testlab of two domains, the result looks like this.
It shows you all the data you need to decide if an import is needed. Domain, name, timestamps, and a guesstimate for the import. This was tested using Powershell 3.0 and 4.0, and with AGPM 4.0 SP2 and SP3. You can find the full script on the TechNet Gallery: https://gallery.technet.microsoft.com/scriptcenter/Find-out-which-GPOs-in-7e798661