Active Directory Shadow Groups: How To Automatically Add OU Users To Security Groups

Remember Novell? Remember NDS or eDirectory as it later became known? NDS might be mostly dead, in favor of AD (Active Directory), but NDS did have many advantages over AD, and one of them was the ability to assign rights (permissions) via OU membership. Want to give users in a specific OU access to a particular file system folder? No problem in NDS, just assign the OU (container) to the folder. But there is no direct way to do this kind of OU (container) assignment in Active Directory. Some applications with integrated access to AD allow for OU assignments, but that doesn’t help you with your day-to-day maintenance of your Active Directory domain.

What cannot be done out-of-the-box in Active Directory can be accomplished with a simple PowerShell script (below). You can use this script to automatically add members to a “shadow group.” This is not an actual type of group, but more or less an adopted term for the process of automatically assigning users to a group. It isn’t just limited to OUs. You could, for example, create a script that assigns all users with a specific attribute value to a group. But this article will just focus on OU shadow groups.

Members Only - photo by momopeche


Assign all members of an Organizational Unit (OU) to a Security Group automatically, without manual intervention. This will not be a real-time sync, but this group should reflect the current OU user list within a reasonable time frame (for example: every 24 hours).


The PowerShell script requires access to the Active Directory cmdlets for PowerShell.

Run this command in PowerShell to install the cmdlets:

Import-Module ActiveDirectory

Sell Art Online

If that doesn’t work, you will need to follow these instructions.




Get-ADGroupMember –Identity $ShadowGroup | Where-Object {$_.distinguishedName –NotMatch $OU} | ForEach-Object {Remove-ADPrincipalGroupMembership –Identity $_ –MemberOf $ShadowGroup –Confirm:$false}

Get-ADUser –SearchBase $OU –SearchScope OneLevel –LDAPFilter "(!memberOf=$ShadowGroup)" | ForEach-Object {Add-ADPrincipalGroupMembership –Identity $_ –MemberOf $ShadowGroup}


First you set the variables. — $OU defines the “distinguishedName” of the organization unit. You can find the distinguishedName in the Attribute Editor tab in the properties of the OU. Make sure you are in “Advanced Features” view in Active Directory Users and Computers. This is set in the View menu. — $ShadowGroup defines the distinguishedName of the Security Group you intend to use as your “shadow group.” This group MUST EXIST before you run the PowerShell script.

The Get-ADGroupMember portion of the script will parse all existing users who are a member of the “shadow group” and remove users who no longer are in the OU. The Get-ADUser portion of the script will parse all users in the OU and add them to the “shadow group.”

After you run the script, your shadow group user membership will be identical to the OU user membership.


I did say this process would not require manual intervention and for that to be true you need to schedule this PowerShell script to run via Task Scheduler. You can copy the above script, paste it into a text file with a “ps1” file extension, and edit it for your environment. You can run this scheduled task on a domain controller, although it is typically frowned upon to run scripts on DCs. So you might want to pick another domain member server for this task. How often the task runs will depend on your environment. In most cases every 24 hours is fine, but if you need something that is closer to real-time, you might need to schedule it to run every hour (or less).

Below is an example Action on a scheduled task to run a PowerShell script on a Windows 2008 Server.

Action: Start a program

Program/script: C:\Windows\system32\windowspowershell\v1.0\powershell.exe

Add arguments (optional): -command C:\scripts\shadow-group.ps1


PLEASE NOTE: Some of our readers have offered great suggestions for customizing this script. Check out the comments section below for their ideas, and please share your own ideas as well.

photo by momopeche

Special thanks to blogger Andreas (Talk nerdy to me) for the PowerShell script.

Tweet about this on TwitterShare on Google+Share on FacebookShare on RedditPin on PinterestShare on TumblrDigg thisShare on StumbleUponShare on LinkedInEmail this to someone

Please share your thoughts


  • Matt Montgomery

    How do you bypass the sizelimit or maxresults? I have an OU with about 7,000 users in it and I want to use this script.

    • I can’t say I’ve ever encountered this (don’t have any OUs with more than a few hundred users). Have you tried?…

      -ResultSetSize $null

      …on the Get-ADUser command?

      • Rafael Dorville

        Good morning Matt and David,

        Regarding your question Matt, you just need to replace the –SearchScope OneLevel to –SearchScope subtree on the Get-ADUser command and it should do the trick.

        Thank you David for this great script.

        • Biddyman

          You actually have to use .NET LDAP to get past the size limit Matt. Of course, I’m still researching what I need to do as I’m in same boat with over 10,000 users in OUs. The only other way to overcome size limit that I know of is to change setting on DC(s) web config.

  • Guest

    Hi, Thanks for this script. Do you think there’s a way to add users from a trusted domain, which is not part of the same forest? When I try to do this using the DN of the OU in the trusted domain I get “The supplied distinguishedName must belong to one of the following partitions” and it just lists the current domain.

  • Sebastian Grigoleit

    I am a script noob, but is there a way to add more OUs in same script? insert another line or something?

    • I assume you are asking to include users from multiple OUs in one Shadow Group? I’m not sure of the syntax to make that happen with this script, but I can offer an alternative approach. You could create Shadow Groups for each OU, then for the OUs that you want to have combined, create another group and add the corresponding Shadow Groups to that group. Hopefully that made sense.

      • Guest

        This may be too late to help you, but for future reference I just tested this exact thing. If the other OU’s are nested under the one you’ve set as $OU, you simply need to remove the “–SearchScope OneLevel” from the last command (the default is “subtree” so it will then include everything under your OU you specify).

        For a totally separate OU, you need to add a few things:

        1. Declare variables for the other OU’s to include.

        2. In the Get-ADGroupMember command, incorporate the OUs into the Where-Object filter. Using “-and” here means it will find group members that do not exist in ANY of the included OUs.

        Get-ADGroupMember –Identity $ShadowGroup | Where-Object {($_.distinguishedName –NotMatch $OU1) -and ($_.distinguishedName -notmatch $OU2)} …

        3. Duplicate the Get-ADUser command for each OU, replacing the variable. No need to combine them into one command, since the group is intended to be a combination of all the OUs anyway.

        • Thanks for taking the time to reply with your findings. It’s great to give people options beyond the scope of this article. In fact, there are now several great replies to this article, so I’m going to edit the original article to mention that people should check out the comments section for more ideas and suggestions. Thanks again.

        • Gunna

          When you say declare variable for other OU’s do you mean:




          If I have more than 1 OU how do I create additional $OU=?

          edit: Never mind, i totally didn’t even see the example you posted. Appreciate the example

  • tech5709

    Hello David,
    This is a great script which I’m happy to be using, but I’ve run into a little snag. Our auditors would like a file generated each day when the script runs to identify which users were added and/or removed from each group, with a file name in the format of Output-Log-YYYY-MM-DD. I’m no powershell expert so I’m turning to you and the community for assistnace. Thanks in advance!

  • Inboots

    Hello is there a way to convert this to add computer accounts instead of users?

    • Have you tried this script on an OU that has computer objects? I think it should work with computer objects as is. I know the “Add-ADPrincipalGroupMembership” and “Remove-ADPrincipalGroupMembership” commands work with computer objects as well as user objects.

      • Okay, after looking at this again, clearly the script as is will not work for computer objects because of the Get-ADUser command. If you want to include both users and computers in the Shadow Group, I believe you could simply duplicate the entire “Get-ADUser” section, and in the duplicate section, replace the Get-ADUser command with the Get-ADComputer command. If you just want a Shadow Group for only computer objects, simply replace Get-ADUser with Get-ADComputer. There might be some syntax changes in the usage of that command, not sure, but that should point you in the right direction.

  • myconnects

    @TheLeftCall:disqus Hi David. I saved the script as a PS1 file and when i run it in powershell i keep getting this error

    At C:Scriptsshadowgroup.ps1:5 char:96
    + … –NotMatch $OU} | ForEach-Object {Remove-ADPrincipalGroupMembership –Identity .
    + ~
    Unexpected token ‘}’ in expression or statement.
    At C:Scriptsshadowgroup.ps1:5 char:98
    + … “NotMatch $OU} | ForEach-Object {Remove-ADPrincipalGroupMembership –Identity $ .
    + ~
    An empty pipe element is not allowed.
    At C:Scriptsshadowgroup.ps1:5 char:115
    + … ForEach-Object {Remove-ADPrincipalGroupMembership –Identity $_ –MemberOf $Sh .
    + ~
    Missing closing ‘}’ in statement block.
    + CategoryInfo : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : UnexpectedToken

    • I’m thinking something got lost or added in the copy/paste. I assume you tried to copy the entire script and paste it into the file a second time just to make sure? Make note that those last two big sections of the script are single lines. They are wrapping as shown on the website here, and they will paste as single lines into a text file, just make sure you don’t add any spaces or returns.

    • AutumninVT

      I realize this is 3 years old but I had this same issue. For some reason when we copy+paste, some dashes “-” end up as a weird character combination: – . I can even see it in the code you pasted up above. I used the Powershell ISE to find and replace the characters. For some reason, they look fine in Notepad++

  • Ben

    Great script. It should be noted that this script will not remove accounts in sub OUs.
    Two users,

    The shadow group for OU “OU=TheOUName,DC=yourdomain,DC=com” should not contain user2 but the script posted above does not delete user2 (say the user was moved from ‘TheOUName’ to ‘ASubOU’). The slightly modified Get-ADGroupMember line below however will,

    Get-ADGroupMember –Identity $Group | Where-Object {$_.DistinguishedName.Substring($_.DistinguishedName.IndexOf(“,”)) -ne $OU} | ForEach-Object {Remove-ADPrincipalGroupMembership –Identity $_ –MemberOf $Group –Confirm:$false}

  • Wesley

    I am trying to get another domain in our forest AD users into a Universal security group in our AD. but I am getting

    Add-ADPrincipalGroupMembership : Cannot find an object with identity: ‘Group OU’ under:
    ‘Different domain’.

    Any suggestions?

  • Martin Weber

    Well I can for the love of god not get this to work. I would be happe to share my results in private by e-mail if anyone want to take a look at it. 🙂

  • Swarna Singh

    Hi, It works well when I add users to the shadow group using the above script but it doesn’t work for removing users from the group. When I run the script it gives me an error that the term “._distinguisedName” is not recognised as the name of a cmdlet…

  • Pingback: Add Ou To Security Group | Your GE Home Security()

  • Pingback: Active Directory Ou Security | Your GE Home Security()

  • Sam Bloom

    Running it as a scheduled task even every hour can result in an up-to-59-minutes, so it might me quite critical for come solutions. There is a tool that can trigger adding users to groups (and not only that) on certain events like user creation/update/movement, etc. So once something happens, shadow group membership would be managed instantly, no waiting time.

    Also with Adaxes you might not even need shadow groups for delegation. It features RBAC and you can add OUs to roles’ activity scope.

    Give it a try. We’ve had it for several months now and it’s a joy to use

  • Thomas

    If you want to set up an interval and define group filters freely, you can use DynamicGroup.

    It’s a software designed to exactly meet the issue of how to automate AD groups
    (OU groups, departments groups etc.)

    You can define LDAP filters as you want, schedule it as you wish and flat nested groups…

  • Thomas

    If you want to create and schedule automated groups have a look at DynamicGroup

    It is created to automate group membership by LDAP filters,
    (OU groups, department groups,…)

    Set the schedule as you wish and update the groups whenever you want.
    You can set black/white lists and even flat nested groups….

  • Ray

    Thanks for the great script. How can I check it against a txt file, so if the computer name exist in the file, it’ll copy it to the shadow group?

  • Orlando

    Hi, The scripts works great! is there a way to log what users where added or removed?

  • Shmuel Chayempour

    So first off THANK YOU! Great article works like a charm.
    Just want to make three side notes.
    1. If you keep all your groups in the default user folder like I do you would replace OU with CN. Here is an example for the staff group in the default user folder:
    Some of you may also have users in sub OU. You always start from the bottom of the OU and work your way up so for instance my current tree looks like this:
    *Top* JIQ.ORGSchoolUsersStaff *Bottom*
    would translate in to:

    2. Another point concerning Task scheduler. You need to be sure to select “Run with Highest Privileges” otherwise it will just give you an error message.

    3. Now you also may want to select “Run weather user is logged in or not” HOWEVER user credentials (even administrator) will NOT work for in this case. Task Scheduler will report a successful execution but it will not update the users. In order to get this to work with “Run weather user is logged in or not” you need to change the account to “System” in the properties box of the task (will not show up in the wizard when you select “create basic task”)
    Good luck everyone and once again Thank you David!

  • Armand Pansegrauw

    Hi Guys.

    I made a big mess with our AD.

    I’m a noob with scripting, and I dont know any script junky to ask for help.

    I’m not sure if the script above was actually intended to do this, but for some reason, I’ve ran the script, and then security groups were all removed from their previous users, and only added to the particular group. Is that what it was supposed to do?

    Our AD is a mess, and I’m slowly but surely trying to redo it properly.

    Also, if this is the intention of the script, would you please be so kind as to send me the script that only adds the security groups to the OU, and not remove them from the other users account in other groups.


    • Sorry to hear about your AD issues. The script only acts on one security group at a time. It will remove users from that one group if they are not in the OU specified. And it will add users to that one group if they are in the OU specified. It would NOT remove users from any other group. Good luck!

  • _n345

    Thank you for this CONCISE script.

  • _n345

    back again. i wanted to let you and your readers know that Get-ADGroupMember has a 5000 object limit which we hit and the script errored. There is an easy workaround with:

    (Get-ADGroup -Identity $ShadowGroup).Members | Get-ADUser | Where-Object {$_.distinguishedName –NotMatch $OU} | …

    thanks again, best solution thus far. i’ve expanded a bit and added email support:

    • Bc Hale

      Couldn’t get the (Get-ADGroup -Identity $ShadowGroup).Members to work,
      but this worked for me.
      (Get-ADGroup -Identity $ShadowGroup -Properties Member).Member | Get-ADUser | …