Skip to main content

Manage Shadow Group Membership - Powershell Function

Manage Shadow Group Membership - PowerShell Function
I looked at a quick script to update a shadow group here, and then thought, this would make a good function. I have used my function template (refer to that post if you want to know more about how to format a function), to build up a reusable script to update group membership based on user and computer object location in AD.

A traditional shadow group is all members of an OU. In my mind, there are a few assumptions to this statement. Being that you have taken the time to create an OU, put objects in it, and created a group to mirror those objects, you probably have only 1 type of object (i.e. user) and they are all similar in some respect (i.e. same office location). This function takes that in mind and only updates groups with users or computers - not other groups etc.

Moving away from the traditional meaning of shadow group, I have added (for my own benefit as I required the functionality) a parameter to change the searchscope from OneLevel (only objects from the OU specified) to subtree (all objects in the tree below the OU specified). Also, by default the function will get all users and computers (name -like *). If you add the -name parameter you can also filter by name, i.e. comp* (all users and computers that start with comp)

As with PowerShell v2 functions you can use get-help, copy and paste the following code in PowerShell, and then run get-help Update-ShadowGroup -detailed to see the examples I have added.


A couple of new commands (for me anyway) I have used here is write-error within  try - catch.  When a group or OU name is passed to the function, I try to get both the OU and the group.  If the catch grabs an error, it outputs the write-error cmdlet making the function look a bit more professional. 

Please send any comments!

function Update-ShadowGroup {
<#
.SYNOPSIS
   Updates a shadow group membership based on the user or computer objects within an ou
.DESCRIPTION
   Better description
.NOTES
   Function Name : Update-ShadowGroup
   Author : Adam Stone
   Requires : PowerShell V2 with AD management pack
.LINK
   http://adadmin.blogspot.com/
.EXAMPLE
   Simple usage
   PS C:\> Update-ShadowGroup -ou "OU=ShadowOU,OU=Test,DC=domain,DC=com" -group shadowgroup
.EXAMPLE
   Simple usage
   PS C:\> Update-ShadowGroup "OU=ShadowOU,OU=Test,DC=domain,DC=com" shadowgroup
.EXAMPLE
   Specify SearchScope
   PS C:\> Update-ShadowGroup "OU=ShadowOU,OU=Test,DC=domain,DC=com" shadowgroup -searchscope Subtree
.EXAMPLE
   Specify name filter
   PS C:\> Update-ShadowGroup "OU=ShadowOU,OU=Test,DC=domain,DC=com" shadowgroup -name srv*
.PARAMETER OU
   DN of the OU - required
.PARAMETER Group
   Name of the Group - required
.PARAMETER Name
   Search pattern to match for the object name
.PARAMETER SearchScope
   Can be either "Base" "OneLevel" or "Subtree" - default OneLevel
#>
param (
   [Parameter(Position=0, Mandatory=$true,HelpMessage="Enter the DN of the OU (eg ou=test,dc=domain,dc=com)")]
   [string] $ou = ""
   ,
   [Parameter(Position=1, Mandatory=$true,HelpMessage="Enter the name of the group (eg group1)")]
   [string] $Group = ""
   ,
   [Parameter(Mandatory=$false)]
   #valadate the input against defined set of attributes
   [string] $name = "*"
   ,
   [Parameter(Mandatory=$false)]
   #valadate the input against defined set of attributes
   [validateset("Base","OneLevel","Subtree")]
   [string] $searchscope = "OneLevel"
)
#the processes the function will complete
process {
   try{
      $getOU = Get-ADOrganizationalUnit $ou
   }
   catch {
      write-error -message "OU $ou does not exist" -RecommendedAction "Make sure the OU is spelt correctly and try again" ;break
   }
   try{
      $getgroup = get-adgroup $group
   }
   catch {
      write-error -message "Group $group does not exist" -RecommendedAction "Make sure the Group name is spelt correctly and try again" ;break
   }
   $objects = $(get-adobject -SearchBase $OU -SearchScope $searchscope -filter {((ObjectCategory -eq "user") -or (ObjectCategory -eq "computer")) -and (name -like $name)})
   $groupmembers = Get-ADGroupMember -Identity $Group
   If ($objects -eq $null -and $groupmembers -eq $null){break}
   If ($objects -eq $null){remove-adgroupmember -identity $group -member $groupmembers -confirm:$false;write-host "$OU was empty, all objects removed"}
   If ($groupmembers -eq $null){
add-adgroupmember -identity $group -member $objects;write-host "$group was empty, all objects added"
   }Else{
      switch (Compare-Object -ReferenceObject $groupmembers -DifferenceObject $objects -property name){
         {$_.SideIndicator -eq "=>"} {add-adgroupmember -identity $group -member $(get-adobject -LDAPFilter "(name=$($_.name)*)"); write-host "$($_.name) added to $group"}
         {$_.SideIndicator -eq "<="} {remove-adgroupmember -identity $group -member $(get-adobject -LDAPFilter "(name=$($_.name)*)") -confirm:$false; write-host "$($_.name) removed to $group"}
      }
   }
}

}#end function


Comments

  1. I would like to make this automated by task manager where the OU and subtree get added to an group. suggestions?

    ReplyDelete
    Replies
    1. This is precisely the way i use this function. Some of the things to consider...

      You will need an account to run the task with that has permission to update the group membership of the shadow groups, nothing more as all accounts have read only access to AD.

      It is best to run this sort of task on a server, preferably not a domain controller. If you do this, the account will need the ability to start a scheduled task. This can be achieved by adding them to the Admin group, but normally (depending on OS version) adding them to allow this account to start as a batch is good enough.

      hopefully this helps!

      cheers

      Delete

Post a Comment

Popular posts from this blog

PowerShell 3 behavioural change

It's taken me way too long to get into PowerShell 3, I guess opportunity hasn't shown it's self until now and so, here, my V3 journey begins.

I was asked to debug a script that would run fine in PS v2 and not in v3.  The issue was a that a variable length was being checked and was failing in v3.  This is why...

In v2 if a variable is undefined, this test returns false

PS C:\windows\system32> $var.length -eq 0
False

In v3 the same test returns true....

PS C:\windows\system32> $var.length -eq 0
True

Not a biggie, but as in this case, a script has broken so something to consider!

cheers

Adam

Enable Powershell Remoting (WinRM) via Group Policy

I have been doing some testing on enabling WinRM via group policy, being that WinRM is the service that Powershell v2 sets up it remoting capabilities. Here are the GPO settings that you need to configure WinRM ....


set the winrm service to auto start


Computer Configuration \ Policies \ Windows Settings \ Security Settings \ System Services


Windows Remote Management (WS-Management)  set Startup Mode to Automatic

start the service


incorporated in to the above - you may need a restart.


create a winrm listener


Computer Configuration / Policies / Administrative Templates / Windows Components / Windows Remote Management (WinRM) / WinRM Service / Allow automatic configuration of listeners


IPv4 filter: *


* is listen on all addresses, or if you only want a particular IP address to respond use an iprange eg 10.1.1.1-10.1.1.254 - don't forget that this IP range has to be valid for all hosts that fall in the scope of the GPO you are creating.  You can use 10.1.1.1 - 10.1.1.254,10.1.1.3 - 10.1.4.254 …

compare-object in Powershell - comparing mulitple values

I'm starting to use compare-object more and more, and one thing I noticed, is that you can compare 2 objects based on multiple attributes. here is how it is constructed...
Compare-Object -ReferenceObject $object1 -DifferenceObject $object2 -Property a,b,c,d,eIf a,b,c and d are the same, but e is different, compare object will return a difference. In the following example, I use "-eq $null" as a check because by default compare-object returns $null if the objects are the same.
#create an array of objects to check against

$collection = @()
foreach ($entry in ("aaaaa","bbbbb","ccccc","ddddd")){
   $store = "" | select "a","b","c","d","e"
   $store.a = $entry*1
   $store.b = $entry*2
   $store.c = $entry*3
   $store.d = $entry*4
   $store.e = $entry*5
   $collection += $store
}

#create an object similar to those in the array
$object = "" | select "a","b…