February 10th, 2009

Hey, Scripting Guy! How Can Get a List of All My Orphaned Group Policy Objects?

Hey, Scripting Guy! Question

Hey, Scripting Guy! I have a problem. I have been elected, given the honor, granted the opportunity to excel (whatever management euphemism you wish to use to describe giving me more work) to clean up our Group Policy strategy. The problem is that the previous Group Policy administrator had no strategy. As a result there are dozens of Group Policy objects (GPOs) that go nowhere. I mean it is like a visit to the Winchester Mystery House in San Jose, California. You know, that ghost house that has all the hallways that dead end into walls and stuff? As a result, the prospect of a cleanup operation scares me. My manager is not lying; it is an opportunity to excel, but at the same it is a bigger opportunity to fail. What I need to start off with is a list of all orphaned GPOs. Can you hook me up with a script?

– JP

SpacerHey, Scripting Guy! Answer

Hi JP,

You know Plato’s theory of forms comes to mind here. Perhaps the GPO you see is but the shadow of a real GPO. The real GPOs are the ones that are linked to objects in Active Directory and that have security filters applied to them. If this is true, all we need to do, according to Plato, is write a script that finds GPOs that are not linked anywhere.

This week is Group Policy week. We will spend the week looking at some of the things you can do using Windows PowerShell when you have access to the COM object that ships with the Group Policy Management Console. There are some good VBScripts that illustrate working with Group Policy in the Script Center Script Repository, and in the Community-Submitted Scripts Center. You can also download a collection of sample Group Policy management scripts.

As it turns out, we already have a script that does that very thing. I originally wrote it for the Windows Server 2008 Resource Kit, but I think it will serve your purposes perfectly. It is a little long (ok, it is very long), but we will focus our time on the more salient portions of this fine script. To see how to use the script, open Windows PowerShell and type the following command.

FindUnlinkedGPOs.ps1 -help

The FindUnlinkedGPOs.ps1 script is seen here.

FindUnlinkedGPOs.ps1

param(
      $domain = $env:userDNSdomain,
      [switch]$query,
      [switch]$help,
      [switch]$examples,
      [switch]$min,
      [switch]$full
      ) #end param

# Begin Functions

function Get-HelpTopic()
{
$descriptionText= `
@”
NAME: FindUnlinkedGPOs.ps1
DESCRIPTION: Finds GPOs that are not linked
anywhere in the domain. The domain can be
specified, or by default it runs against the
current domain.

PARAMETERS:
-domain Domain to query for unlinked GPOs
-query Executes the query
-help prints help description and parameters file
-examples prints only help examples of syntax
-full prints complete help information
-min prints minimal help. Modifies -help

“@ #end descriptionText

$examplesText= `
@”

SYNTAX:
FindUnlinkedGPOs.ps1

Displays missing query and calls help

FindUnlinkedGPOs.ps1 -query

Displays unlinked GPOs from the current domain

FindUnlinkedGPOs.ps1  -domain “nwtraders.com”

Displays unlinked GPOs from the nwtraders.com domain

FindUnlinkedGPOs.ps1 -help

Prints the help topic for the script

FindUnlinkedGPOs.ps1 -help -full

Prints full help topic for the script

FindUnlinkedGPOs.ps1 -help -examples

Prints only the examples for the script

FindUnlinkedGPOs.ps1 -examples

Prints only the examples for the script
“@ #end examplesText

$remarks = `

REMARKS
     For more information, type: $($MyInvocation.ScriptName) -help -full
” #end remarks

  if($examples) { $examplesText ; $remarks ; exit }
  if($full)     { $descriptionText; $examplesText ; exit }
  if($min)      { $descriptionText ; exit }
  $descriptionText; $remarks
  exit
} #end Get-HelpTopic function

function New-Line (
                  $strIN,
                  $char = “=”,
                  $sColor = “Yellow”,
                  $uColor = “darkYellow”,
                  [switch]$help
                 )
{
if($help)
  {
    $local:helpText = `
@”
     New-Line accepts inputs: -strIN for input string and -char for seperator
     -sColor for the string color, and -uColor for the underline color. Only
     the -strIn is required. The others have the following default values:
     -char: =, -sColor: Yellow, -uColor: darkYellow
     Example:
     New-Line -strIN “Hello world”
     New-Line -strIn “Morgen welt” -char “-” -sColor “blue” -uColor “yellow”
     New-Line -help
“@
   $local:helpText
   break
  } #end New-Line help
 
$strLine= $char * $strIn.length
Write-Host -ForegroundColor $sColor $strIN
Write-Host -ForegroundColor $uColor $strLine
} #end New-Line function

Function Get-UnlinkedGPO()
{
$gpm=New-Object -ComObject gpmgmt.gpm
$constants = $gpm.GetConstants()
$gpmDomain = $gpm.GetDomain($domain,$null,$constants.useanydc)
$gpmSearchCriteria = $gpm.CreateSearchCriteria()
$gpoList= $gpmDomain.SearchGPOs($gpmSearchCriteria)

New-Line(“GPO’s that are not linked anywhere in $domain”)
$unlinkedGPO = 0
foreach($objGPO in $gpoList)
{
  $gpmSearchCriteria = $gpm.CreateSearchCriteria()
  $gpmSearchCriteria.add($constants.SearchPropertySomLinks, $constants.SearchOpContains, $objGPO)
  $somList = $gpmDomain.SearchSoms($gpmSearchCriteria)
   if($somList.count -eq 0)
     {
      “$($objGPO.id) `t $($objGPO.displayname)”
      $unlinkedGPO +=1
     }
}
if($unlinkedGPO -eq 0)
  {
   New-Line -strin “There are no unlinked GPOs in $domain” -scolor green
  }
exit
} #end Get-UnlinkedGPO

# Entry Point

if($help)      { Get-HelpTopic }
if($examples)  { Get-HelpTopic }
if($full)      { Get-HelpTopic }
if($query)     { Get-UnlinkedGPO }
if(!$query)    { “Missing query.” ; Get-HelpTopic }
 

The FindUnlinkedGPOs.ps1 script begins with the parameters. To create parameters, we use the Param statement. To obtain a default value for the domain, we use the environmental variable userDNSdomain. We obtain this value from the environmental drive created by the Windows PowerShell environmental provider. Because this is a Windows PowerShell drive, we can actually use the dir command, as seen here:

Dir env:

We are using a shortcut to obtain the name of the users domain, and we assign it to the $domain variable. The param statement is seen here:

param(
      $domain = $env:userDNSdomain,
      [switch]$query,
      [switch]$help,
      [switch]$examples,
      [switch]$min,
      [switch]$full
      ) #end param

The next two functions are Get-HelpTopic and New-Line. For a good discussion of those two functions refer to yesterday’s “Hey, Scripting Guy!” article.

We now come to the meat of the script, the Get-UnlinkedGPO function. The first thing we need to do is to create an instance of the gpmgmt.gpm COM object. We use the New-Object cmdlet to create the object, and we store it in the $gpm variable as seen here:

$gpm=New-Object -ComObject gpmgmt.gpm

Next we need to create the constants. To create the Group Policy constants, we use the GetConstants method from the gpmgmt.gpm COM object. We store the constants in the $constants variable:

$constants = $gpm.GetConstants()

We connect to the domain by using the GetDomain method. When we connect to the domain, we need to tell it which domain to connect to, and we also need to tell it how to make the connection. In our example, we give it the domain that is contained in the $domain variable, and we use the useanydc constant to tell it to connect to any domain controller it can find within our specified domain. We store the domain in the $gpmDomain variable:

$gpmDomain = $gpm.GetDomain($domain,$null,$constants.useanydc)

Now we create the search criteria. To do this, we use the CreateSearchCriteria method, and we store the returned search criteria object in the $gpmSearchCriteria variable. We then call the SearchGPOs method and pass it the search criteria. This is seen here:

$gpmSearchCriteria = $gpm.CreateSearchCriteria()
 $gpoList= $gpmDomain.SearchGPOs($gpmSearchCriteria)

Now we need to create a new search criteria so that we can look for GPOs that are not linked. In the previous command we used a default search criteria. Now we need to use the Group Policy constants to tell the search criteria exactly what we are looking for. We tell it we are looking for SomLinks. We then use the new search criteria and call the SearchSoms method. The resultant SolList is stored in the $somList variable. This is seen here:

$gpmSearchCriteria = $gpm.CreateSearchCriteria()
$gpmSearchCriteria.add($constants.SearchPropertySomLinks, $constants.SearchOpContains, $objGPO)
$somList = $gpmDomain.SearchSoms($gpmSearchCriteria)

If the $SomList variable has a count of 0, it means there were no links found for the GPO. We print out the GPO display name and increment the $unlinkedGPO counter variable:

if($somList.count -eq 0)
     {
      "$($objGPO.id) `t $($objGPO.displayname)"
      $unlinkedGPO +=1
     }

When we are done, if there are no unlinked GPOs, we display a message in green:

if($unlinkedGPO -eq 0)
  {
   New-Line -strin "There are no unlinked GPOs in $domain" -scolor green
  }
 Exit

The entry point to the script is used to parse the command-line arguments and to call the appropriate functions:

if($help)      { Get-HelpTopic }
if($examples)  { Get-HelpTopic }
if($full)      { Get-HelpTopic }
if($query)     { Get-UnlinkedGPO }
if(!$query)    { "Missing query." ; Get-HelpTopic }

When the script is run and an unlinked GPO is found, the output is similar to what is shown here:

Image of the output produced when the script is run and an unlinked GPO is found

 

Well, JP, I hope I have shown you that you have nothing to be scared of in tackling a Group Policy cleanup project. Using the object model available to us from the Group Policy Management Console, it is rather easy to find orphaned GPOs. Join us tomorrow as Group Policy Week continues.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Author

0 comments

Discussion are closed.