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
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:
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
0 comments