{"id":54423,"date":"2009-02-10T21:08:00","date_gmt":"2009-02-10T21:08:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/02\/10\/hey-scripting-guy-how-can-get-a-list-of-all-my-orphaned-group-policy-objects\/"},"modified":"2009-02-10T21:08:00","modified_gmt":"2009-02-10T21:08:00","slug":"hey-scripting-guy-how-can-get-a-list-of-all-my-orphaned-group-policy-objects","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-get-a-list-of-all-my-orphaned-group-policy-objects\/","title":{"rendered":"Hey, Scripting Guy! How Can Get a List of All My Orphaned Group Policy Objects?"},"content":{"rendered":"<h2><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\" \/><\/h2>\n<p>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 <a href=\"http:\/\/www.winchestermysteryhouse.com\/\" target=\"_blank\">Winchester Mystery House<\/a> 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?<\/p>\n<p>&#8211; JP<\/p>\n<p><img decoding=\"async\" border=\"0\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" height=\"5\" \/><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\" \/><\/p>\n<p>Hi JP,<\/p>\n<p>You know Plato&#8217;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.<\/p>\n<table id=\"EED\" class=\"dataTable\" cellspacing=\"0\" cellpadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" valign=\"top\">\n<td style=\"border-right: #cccccc 1px solid\">\n<p class=\"lastInCell\">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 <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/scripts\/default.mspx?mfr=true\">Script Center Script Repository<\/a>, and in the <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/csc\/scripts\/policy\/default.mspx\">Community-Submitted Scripts Center<\/a>. You can also download a collection of <a href=\"http:\/\/www.microsoft.com\/downloads\/details.aspx?FamilyID=38c1a89b-a6d2-4f2a-a944-9236999aee65&amp;DisplayLang=en\">sample Group Policy management scripts<\/a>.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>As it turns out, we already have a script that does that very thing. I originally wrote it for the <a href=\"http:\/\/www.microsoft.com\/learning\/en\/us\/Books\/10345.aspx\" target=\"_blank\">Windows Server 2008 Resource Kit<\/a>, 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.<\/p>\n<pre class=\"codeSample\">FindUnlinkedGPOs.ps1 -help<\/pre>\n<p>The <b>FindUnlinkedGPOs.ps1<\/b> script is seen here.<\/p>\n<p><b>FindUnlinkedGPOs.ps1<\/b><\/p>\n<p>param(<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $domain = $env:userDNSdomain,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$query,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$help,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$examples,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$min,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$full<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ) #end param<\/p>\n<p># Begin Functions<\/p>\n<p>function Get-HelpTopic()<br \/>{<br \/>$descriptionText= `<br \/>@&#8221;<br \/>NAME: FindUnlinkedGPOs.ps1<br \/>DESCRIPTION: Finds GPOs that are not linked <br \/>anywhere in the domain. The domain can be <br \/>specified, or by default it runs against the<br \/>current domain. <\/p>\n<p>PARAMETERS: <br \/>-domain Domain to query for unlinked GPOs<br \/>-query Executes the query<br \/>-help prints help description and parameters file<br \/>-examples prints only help examples of syntax<br \/>-full prints complete help information<br \/>-min prints minimal help. Modifies -help<\/p>\n<p>&#8220;@ #end descriptionText<\/p>\n<p>$examplesText= `<br \/>@&#8221;<\/p>\n<p>SYNTAX:<br \/>FindUnlinkedGPOs.ps1 <\/p>\n<p>Displays missing query and calls help<\/p>\n<p>FindUnlinkedGPOs.ps1 -query<\/p>\n<p>Displays unlinked GPOs from the current domain<\/p>\n<p>FindUnlinkedGPOs.ps1&nbsp; -domain &#8220;nwtraders.com&#8221;<\/p>\n<p>Displays unlinked GPOs from the nwtraders.com domain <\/p>\n<p>FindUnlinkedGPOs.ps1 -help<\/p>\n<p>Prints the help topic for the script<\/p>\n<p>FindUnlinkedGPOs.ps1 -help -full<\/p>\n<p>Prints full help topic for the script<\/p>\n<p>FindUnlinkedGPOs.ps1 -help -examples<\/p>\n<p>Prints only the examples for the script<\/p>\n<p>FindUnlinkedGPOs.ps1 -examples<\/p>\n<p>Prints only the examples for the script<br \/>&#8220;@ #end examplesText<\/p>\n<p>$remarks = `<br \/>&#8220;<br \/>REMARKS<br \/>&nbsp;&nbsp;&nbsp;&nbsp; For more information, type: $($MyInvocation.ScriptName) -help -full<br \/>&#8221; #end remarks<\/p>\n<p>&nbsp; if($examples) { $examplesText ; $remarks ; exit }<br \/>&nbsp; if($full)&nbsp;&nbsp;&nbsp;&nbsp; { $descriptionText; $examplesText ; exit } <br \/>&nbsp; if($min)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { $descriptionText ; exit }<br \/>&nbsp; $descriptionText; $remarks <br \/>&nbsp; exit<br \/>} #end Get-HelpTopic function<\/p>\n<p>function New-Line (<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $strIN,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $char = &#8220;=&#8221;,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $sColor = &#8220;Yellow&#8221;,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $uColor = &#8220;darkYellow&#8221;,<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [switch]$help<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )<br \/>{<br \/>if($help)<br \/>&nbsp; {<br \/>&nbsp;&nbsp;&nbsp; $local:helpText = `<br \/>@&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; New-Line accepts inputs: -strIN for input string and -char for seperator<br \/>&nbsp;&nbsp;&nbsp;&nbsp; -sColor for the string color, and -uColor for the underline color. Only <br \/>&nbsp;&nbsp;&nbsp;&nbsp; the -strIn is required. The others have the following default values:<br \/>&nbsp;&nbsp;&nbsp;&nbsp; -char: =, -sColor: Yellow, -uColor: darkYellow<br \/>&nbsp;&nbsp;&nbsp;&nbsp; Example:<br \/>&nbsp;&nbsp;&nbsp;&nbsp; New-Line -strIN &#8220;Hello world&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; New-Line -strIn &#8220;Morgen welt&#8221; -char &#8220;-&#8221; -sColor &#8220;blue&#8221; -uColor &#8220;yellow&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; New-Line -help<br \/>&#8220;@<br \/>&nbsp;&nbsp; $local:helpText<br \/>&nbsp;&nbsp; break<br \/>&nbsp; } #end New-Line help<br \/>&nbsp; <br \/>$strLine= $char * $strIn.length<br \/>Write-Host -ForegroundColor $sColor $strIN <br \/>Write-Host -ForegroundColor $uColor $strLine<br \/>} #end New-Line function<\/p>\n<p>Function Get-UnlinkedGPO()<br \/>{<br \/>$gpm=New-Object -ComObject gpmgmt.gpm<br \/>$constants = $gpm.GetConstants()<br \/>$gpmDomain = $gpm.GetDomain($domain,$null,$constants.useanydc)<br \/>$gpmSearchCriteria = $gpm.CreateSearchCriteria()<br \/>$gpoList= $gpmDomain.SearchGPOs($gpmSearchCriteria)<\/p>\n<p>New-Line(&#8220;GPO&#8217;s that are not linked anywhere in $domain&#8221;)<br \/>$unlinkedGPO = 0<br \/>foreach($objGPO in $gpoList)<br \/>{<br \/>&nbsp; $gpmSearchCriteria = $gpm.CreateSearchCriteria()<br \/>&nbsp; $gpmSearchCriteria.add($constants.SearchPropertySomLinks, $constants.SearchOpContains, $objGPO)<br \/>&nbsp; $somList = $gpmDomain.SearchSoms($gpmSearchCriteria)<br \/>&nbsp;&nbsp; if($somList.count -eq 0)<br \/>&nbsp;&nbsp;&nbsp;&nbsp; { <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;$($objGPO.id) `t $($objGPO.displayname)&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $unlinkedGPO +=1<br \/>&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>}<br \/>if($unlinkedGPO -eq 0) <br \/>&nbsp; { <br \/>&nbsp;&nbsp; New-Line -strin &#8220;There are no unlinked GPOs in $domain&#8221; -scolor green<br \/>&nbsp; }<br \/>exit<br \/>} #end Get-UnlinkedGPO<\/p>\n<p># Entry Point<\/p>\n<p>if($help)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { Get-HelpTopic }<br \/>if($examples)&nbsp; { Get-HelpTopic }<br \/>if($full)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { Get-HelpTopic }<br \/>if($query)&nbsp;&nbsp;&nbsp;&nbsp; { Get-UnlinkedGPO }<br \/>if(!$query)&nbsp;&nbsp;&nbsp; { &#8220;Missing query.&#8221; ; Get-HelpTopic }<br \/>&nbsp;<\/p>\n<p>The <b>FindUnlinkedGPOs.ps1<\/b> script begins with the parameters. To create parameters, we use the <b>Param<\/b> statement. To obtain a default value for the domain, we use the environmental variable <b>userDNSdomain<\/b>. 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 <b>dir<\/b> command, as seen here:<\/p>\n<pre class=\"codeSample\">Dir env:<\/pre>\n<p>We are using a shortcut to obtain the name of the users domain, and we assign it to the <b>$domain<\/b> variable. The <b>param<\/b> statement is seen here:<\/p>\n<pre class=\"codeSample\">param(\n      $domain = $env:userDNSdomain,\n      [switch]$query,\n      [switch]$help,\n      [switch]$examples,\n      [switch]$min,\n      [switch]$full\n      ) #end param\n<\/pre>\n<p>The next two functions are <b>Get-HelpTopic<\/b> and <b>New-Line<\/b>. For a good discussion of those two functions refer to <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/feb09\/hey0209.mspx\" target=\"_blank\">yesterday&#8217;s &ldquo;Hey, Scripting Guy!&rdquo; article<\/a>.<\/p>\n<p>We now come to the meat of the script, the <b>Get-UnlinkedGPO<\/b> function. The first thing we need to do is to create an instance of the <b>gpmgmt.gpm<\/b> COM object. We use the <b>New-Object<\/b> cmdlet to create the object, and we store it in the <b>$gpm<\/b> variable as seen here:<\/p>\n<pre class=\"codeSample\">$gpm=New-Object -ComObject gpmgmt.gpm<\/pre>\n<p>Next we need to create the constants. To create the Group Policy constants, we use the <b>GetConstants<\/b> method from the <b>gpmgmt.gpm<\/b> COM object. We store the constants in the <b>$constants<\/b> variable:<\/p>\n<pre class=\"codeSample\">$constants = $gpm.GetConstants()<\/pre>\n<p>We connect to the domain by using the <b>GetDomain<\/b> 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 <b>$domain<\/b> variable, and we use the <b>useanydc<\/b> constant to tell it to connect to any domain controller it can find within our specified domain. We store the domain in the <b>$gpmDomain<\/b> variable:<\/p>\n<pre class=\"codeSample\">$gpmDomain = $gpm.GetDomain($domain,$null,$constants.useanydc)<\/pre>\n<p>Now we create the search criteria. To do this, we use the <b>CreateSearchCriteria<\/b> method, and we store the returned search criteria object in the <b>$gpmSearchCriteria<\/b> variable. We then call the <b>SearchGPOs<\/b> method and pass it the search criteria. This is seen&nbsp;here:<\/p>\n<pre class=\"codeSample\">$gpmSearchCriteria = $gpm.CreateSearchCriteria()\n $gpoList= $gpmDomain.SearchGPOs($gpmSearchCriteria)\n<\/pre>\n<p>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 <b>SomLinks<\/b>. We then use the new search criteria and call the <b>SearchSoms<\/b> method. The resultant <b>SolList<\/b> is stored in the <b>$somList<\/b> variable. This is seen here:<\/p>\n<pre class=\"codeSample\">$gpmSearchCriteria = $gpm.CreateSearchCriteria()\n$gpmSearchCriteria.add($constants.SearchPropertySomLinks, $constants.SearchOpContains, $objGPO)\n$somList = $gpmDomain.SearchSoms($gpmSearchCriteria)\n<\/pre>\n<p>If the <b>$SomList<\/b> 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 <b>$unlinkedGPO<\/b> counter&nbsp;variable:<\/p>\n<pre class=\"codeSample\">if($somList.count -eq 0)\n     {\n      \"$($objGPO.id) `t $($objGPO.displayname)\"\n      $unlinkedGPO +=1\n     }\n<\/pre>\n<p>When we are done, if there are no unlinked GPOs, we display a message in&nbsp;green:<\/p>\n<pre class=\"codeSample\">if($unlinkedGPO -eq 0)\n  {\n   New-Line -strin \"There are no unlinked GPOs in $domain\" -scolor green\n  }\n Exit\n<\/pre>\n<p>The entry point to the script is used to parse the command-line arguments and to call the appropriate&nbsp;functions:<\/p>\n<pre class=\"codeSample\">if($help)      { Get-HelpTopic }\nif($examples)  { Get-HelpTopic }\nif($full)      { Get-HelpTopic }\nif($query)     { Get-UnlinkedGPO }\nif(!$query)    { \"Missing query.\" ; Get-HelpTopic }\n<\/pre>\n<p>When the script is run and an unlinked GPO is found, the output is similar to what is shown here:<\/p>\n<p><img decoding=\"async\" border=\"0\" alt=\"Image of the output produced when the script is run and an unlinked GPO is found\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/february\/hey0210\/hsg-02-10-9-01.jpg\" width=\"500\" height=\"356\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>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.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[152,3,45],"class_list":["post-54423","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-group-policy","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54423","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=54423"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54423\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=54423"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=54423"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=54423"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}