Hey, Scripting Guy! Question

Hey, Scripting Guy! We have a number of Group Policy Objects (GPOs) that are not really doing anything. In fact, they are about as useless as an old hound dog with sinus problems. What I need is an easy way to find these old GPOs and produce a list so I can decide if I wish to delete them. Can you help me?

- JG

SpacerHey, Scripting Guy! Answer

Hi JG,

"That dog won't hunt." I am not sure exactly what that means, but it is the only dog expression I know. I guess because I am living in the Southern part of the United States, I should study Southern speakr more closely. After all they have words down here that are not used in the rest of the United States. Words like haintnarynone, which is a reply to the question, "Do you have any more milk?" When you pronounce haintnarynone , you say it in one syllable without moving your lips. You can learn this technique by grasping your upper and lower lip between the thumb and the forefinger, and then saying “haintnarynone.” However, I digress. (Let it never be said that a Scripting Guy became distracted during the process of writing a “Hey, Scripting Guy!” article. Well, okay, maybe once in a while: A quick search of the Script Center for the word “distracted” returns eight hits. So 9 times we became distracted. We’ll take that average.)

Where were we? Oh, yeah. I talked to my neighbor, and he said the kind of dog you want depends on what you are hunting. Oh wait, the dog won't hunt. Sorry. I don’t really know much about dogs at all, so let's talk about Group Policy scripting instead.

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.

To find Group Policy objects that do not have any security filtering associated with them, you can use the FindGPOsWithNoSecurityFiltering.ps1 script which is seen here. To obtain help on the script you can use the following syntax:

FindGPOsWithNoSecurityFiltering.ps1 –help

FindGPOsWithNoSecurityFiltering.ps1

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

# Begin Functions

function Get-HelpTopic()
{
 $descriptionText= `
@"
 NAME: FindGPOsWithNoSecurityFiltering.ps1
 DESCRIPTION:
 Finds GPOs that have no security filtering
 assigned, and therefore they apply to no one

 PARAMETERS: 
 -domain domain to query for orphaned gpos
 -query causes query to execute
 -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:
 FindGPOsWithNoSecurityFiltering.ps1 

 Displays an error missing query, and calls help

 FindGPOsWithNoSecurityFiltering.ps1  -query

 Queries current domain for GPOs with no security
 filter applied to them

 FindGPOsWithNoSecurityFiltering.ps1 -domain nwtraders.com -query
 
 Queries nwtraders.com domain for GPOs with no security
 filter applied to them

 FindGPOsWithNoSecurityFiltering.ps1 -help

 Prints the help topic for the script

 FindGPOsWithNoSecurityFiltering.ps1 -help -full

 Prints full help topic for the script

 FindGPOsWithNoSecurityFiltering.ps1 -help -examples

 Prints only the examples for the script

 FindGPOsWithNoSecurityFiltering.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-NoFilter()
{
 $gpm = new-object -comobject gpmGMT.gpm
 $constants = $gpm.getConstants()
 $gpmDomain = $gpm.GetDomain($domain,$null,$constants.useanydc)
 $gpmSearchCriteria = $gpm.CreateSearchCriteria()
 $gpoList = $gpmDomain.SearchGpos($gpmSearchCriteria)
 $perm = $Constants.PermGPOApply
 foreach($gpo in $gpoList)
  {
   $secInfo = $gpo.GetSecurityInfo()
    foreach($secPerm in $secInfo)
     {
      if($secPerm.Permission -ne $perm) 
         {
           $errorActionPreference = "SilentlyContinue"
           $hash += @{ $gpo.id = $gpo.DisplayName }
           $errorActionPreference = "continue"
         } #end if
     } #end foreach
  } #end foreach
  New-Line("No permissions were found $($hash.count) GPOs")
  format-table -inputobject $hash -autosize
 exit
} #end Get-NoFilter

# Entry Point

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

 

The FindGPOsWithNoSecurityFiltering.ps1 script is written in the same style as the other GPO scripts we have seen this week. Please refer to the articles from Monday and Tuesday to see information about the command-line parameters, the help function, and the New-Line function.

The core script functionality is the Get-NoFilter function, which is used to find GPOs that have no security filter applied. The first thing we do in the function is to create an instance of the gpmGMT.gpm object. Because the object is a COM object we use the –comobject parameter of the New-Object cmdlet. We store the returned object in the $gpm variable as seen here:

$gpm = new-object -comobject gpmGMT.gpm

After we have created the gpmGMT.gpm object, we have access to the methods seen in Table 1.

Table 1 Methods of the GpmGMT.gpm object
Name *
MemberType *
Definition *

CreateMigrationTable

Method

IGPMMigrationTable CreateMigrationTable ()

CreatePermission

Method

IGPMPermission CreatePermission (string, GPMPermissionType, bool)

CreateSearchCriteria

Method

IGPMSearchCriteria CreateSearchCriteria ()

CreateTrustee

Method

IGPMTrustee CreateTrustee (string)

GetBackupDir

Method

IGPMBackupDir GetBackupDir (string)

GetBackupDirEx

Method

IGPMBackupDirEx GetBackupDirEx (string, GPMBackupType)

GetClientSideExtensions

Method

IGPMCSECollection GetClientSideExtensions ()

GetConstants

Method

IGPMConstants GetConstants ()

GetDomain

Method

IGPMDomain GetDomain (string, string, int)

GetMigrationTable

Method

IGPMMigrationTable GetMigrationTable (string)

GetRSOP

Method

IGPMRSOP GetRSOP (GPMRSOPMode, string, int)

GetSitesContainer

Method

IGPMSitesContainer GetSitesContainer (string, string, string, int)

InitializeReporting

Method

void InitializeReporting (string)

InitializeReporting

Method

void InitializeReportingEx (string, int)

The next thing we need to do is to create the constants and to store them in a variable. We talked about Group Policy constants in Wednesday’s “Hey, Scripting Guy!” article, and the process and purpose is exactly the same as what we do here:

$constants = $gpm.getConstants()

Now we need to connect to the domain. To do that, we use the GetDomain method and pass it the name of the domain and the useanydc constant. We store the returned object in the $gpmDomain variable. Next we need to create the search criteria. To do this, we use the CreateSearchCriteria method and store the returned search criteria in the $gpmSearchCriteria variable. We then use the SearchGpos method from the domain we stored in the $gpmDomain variable. We give the SearchGpos method the search criteria we created in the previous line. This is seen here:

$gpmDomain = $gpm.GetDomain($domain,$null,$constants.useanydc)
$gpmSearchCriteria = $gpm.CreateSearchCriteria()
$gpoList = $gpmDomain.SearchGpos($gpmSearchCriteria)

We now need to retrieve a specific constant from all the constants we have stored in the $constants variable. The specific one we need is called PermGPOApply, and it is used to specify that the GPO has permissions applied. This is seen here, where we store the resultant constant in the $perm variable:

$perm = $Constants.PermGPOApply

We are now ready to begin examining each of the GPOs. We first use the ForEach statement to work through each of the GPOs that is stored in the $gpoList variable. We take each one of the GPOs and call the GetSecurityInfo method to retrieve the security information that is associated with each of the GPOs. We store the security information in the $secInfo variable:

foreach($gpo in $gpoList)
  {
   $secInfo = $gpo.GetSecurityInfo()

When we have the security information, we need to examine each permission that makes up the security information attached to the GPO. If the permission is not equal to the PermGPOApply constant, we add the name of the GPO to the hash table we create with the name of $hash. A hash table is similar to the VBScript concept of a dictionary object. Before and after each attempt to obtain the display name, we use $errorActionPreference = "SilentlyContinue", which is the same as VBScript On Error Resume Next. By setting $errorActionPreference equal to "continue", in this case it is similar to using On Error Goto 0, which is discussed here.

foreach($secPerm in $secInfo)
     {
      if($secPerm.Permission -ne $perm) 
         {
           $errorActionPreference = "SilentlyContinue"
           $hash += @{ $gpo.id = $gpo.DisplayName }
           $errorActionPreference = "continue"
         } #end if
     } #end foreach
  } #end foreach

It is time to produce our report. We use the count property from the hashtable object to tell us how many GPOs were found without permissions. We then call the Format-Table cmdlet to print out the hash table:

New-Line("No permissions were found $($hash.count) GPOs")
  format-table -inputobject $hash –autosize

 

Well, JG, I believe that "this dog will hunt!" This concludes our Group Policy scripts. The next time we write about Group Policy, Windows 2008 R2 will be out and the scripts will be much cleaner as we will also take advantage of the help features of Windows PowerShell 2.0. If you can't wait, go ahead and download Windows 2008 R2 and start playing around with it. I am running it on one of my laptops with Hyper-V and it rocks! Join us tomorrow for Quick-Hits Friday where we will tackle a grab bag of questions. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys