Active Directory Shadow Group Script – will let you spend less time on updating group memberships
Introduction
If you are just looking for a free shadow group script, either click here for a nice simple one or go to the bottom of this post for the full AD administrated script.
I was looking into Shadow Groups, inspired by a customer migrating from Novell to Active Directory. Apparently in Novell you can use Organizational Units as security groups, and by just moving a user to another OU when they change departments, they will automatically update their security permissions given by their department OU placement.
So what is so great about shadow groups you might ask. Simply put if you have OU’s for departments, where you place users depending on department membership, shadow groups, will shadow the members of the OU in the security group, I assume that is where the name shadow group comes from. This allows you to setup security permissions for a group that is linked to an organizational unit. So when you move user A from department sales, into department accounting, the user A will automatically be removed from the sales security group and added to the accounting security group, effectively updating user A’s permissions automatically. Saves time for large organizations, now a user moving OU does not need to have his groups manually updated.
The first hit on google was a blog post by John Policelli (MVP) explaining shadow groups is not a new type of group in Active Directory, it is rather a concept, when you automatically update the members of a security group from the objects placed in an Organizational Unit. Also he points out that this automatic synchronization is not an existing feature in Windows Server, we need to add it our self. The example he uses with dsquery, dsget and dsmod, works if you manually set it in a script for each OU/Group, I was looking for something easier to manage, that preferably did not require editing of the script that needed to run. I strongly believe scripts that can be maintained from Active Directory will always have a longer life time, since less updates and potential errors happen in the script.
After some more searching I found an article by Jakob H. Heidelberg (MVP and fellow Dane) this one also had a good explanation about what Shadow Groups are and also a download link to a simple VBScript to populate a group with the users in an Organizational Unit. If You are looking for a script to feed the OU and Group and then update the group from the users of the OU, that script will do you just fine and I would recommend you take a look at the article and script he wrote, as it is simpler and less prone to errors by being simple.
My idea of a Shadow Group Script
Using this as inspiration I sat down and wrote a couple of lines about what I would like from the ideal Shadow Group script, what I got was something like this:
- After initial setup of script and scheduling, no editing should be necesary to add/edit/remove shadow groups.
- Create shadow groups automatically from information in a organizational unit
- Update multiple shadow groups with information from one organizational unit (i.e. one with computers, one with users, one with all ou and sub ou’s users, etc.)
- High level of customization of shadow group names and content
- Logging of all editing by script to logfiles
- Security safe with the ability to avoid a user to change security groups where user should not be able to
From that I have created a VBScript that can be scheduled to run, preferably in the security context of a service account, with only permissions to create and edit groups within the area of Active Directory where deemed safe. The script is free for anyone to use, edit, improve, under the creative commons license, so basicly all I ask is that credit is given if used or changes are made.
Please feel free to comment how you used the script, any improvements or additions you make are also welcomed. Find a bug? Please let me know. Having trouble getting it to work? Feel free to write a comment with your questions.
How to setup and install the script.
- Create a service account with only the permissions you trust the script with. Preferably only able to create and edit security groups within the OU’s needed.
- Edit the sections of the script with “EDIT THIS”.
- On a server with access to a domain controller, create a sheduled task to run every x minutes, i.e. 15 minutes, that runs “cscript.exe shadowgroups.vbs”.
How to use the script to create shadow groups.
Option #1 – Shadow group created from Organizational Unit
- Edit the description field of the Organizational Unit with the parameters to create and maintain a connected shadow group
Option #2 – Shadow group connected to an Organizational Unit
- Create a Security Group – dont forget any required word in the group name i.e. “SHADOWGROUP”/”SG” etc.
- Edit the description field of the security group with the parameters to update its members from an Organizational Unit
This allows multiple groups to update from the same target.
Parameters
The parameters to use in the description field of a security group / Organizational Unit, must be written comma seperated in the following format:
ValueName=Value,ValueName2=Value2,etc.
ValueName = Values – Description
Scope = OneLevel – Default value, only gets members from the target OU
Scope = SubTree – Gets members from targeted OU and all sub OU’s
OU = OUa/OUb/OUc – The OU path to the target OU, the example before is from a full path like this LDAP://ou=OUa,ou=OUb,ou=OUc,dc=my,dc=domain,dc=local
Notice only “real” OU’s can be used, no builtin OU’s, also do not add the domain.
GroupOU = OUa/OUb/OUc – The path to the OU where the shadow groups is to be placed, same format as OU.
GroupName = SHADOWGROUP Department Sales – The full custom name of the shadow group (if not set defaults to standard prefix + OU name).
GroupPrefix = SHADOWGROUP Department – The prefix to use for the group name + the source OU name. Usefull for using the same description for multiple sub OU’s.
OBJ = CUG – A combination of letters designating what objects to include in the shadow group. C = Computers, U = Users, G = Groups.
# = Text – Text written here is ignored, usefull for descriptions for humans and other uses.
Required parameters and defaults
It is not required to use all the parameters each time you setup a shadow group, but you must start the description field with SHADOWGROUP.
The following defaults will be used unless specifically set to something else when using an OU as source:
- Source OU will be the OU with the description set to SHADOWGROUP
- Group name will be standard prefix (from script config) + OU name.
- Group location will be within the source OU.
- Scope will be OneLevel.
- Objects will be users only.
When using a group as the shadow group initiator, you must set source OU, other than that the following defaults will be used:
- Source OU must be set manually or group processing will be aborted.
- Group name will be source group.
- Group location will be source group location.
- Scope will be OneLevel
- Objects will be users only.
The actual script
Download the full Active Directory Shadow Group Script from here [shadowgroups.vbs.txt]. Also sourcecode visible here (sorry the blog automatically changes some stuff including links).
'************************************************************************************************* ' Name: Active Directory Shadow Groups Script ' File: ShadowGroups.vbs ' Description: Creates and maintains security groups based on OU members ' ' Created by Sole Viktor - sole@sole.dk - www.sole.dk ' http://creativecommons.org/licenses/by-sa/3.0/ ' ' On Error Resume Next ' uncomment this to see errors that werent supposed to happen Dim LogFileName ' ' Set constants - EDIT THIS! ' Const str_Domain = "dc=my,dc=domain,dc=local" Const str_BaseOU = "ou=Departments," ' limit where to look for ShadowGroup OU/Groups Const str_DefaultGroupPrefix = "Prefix SHADOWGROUP" ' default prefix to use for shadow groups Const str_Default_ObjectCategory = "(objectCategory=user)" ' default objects to include in a shadow group Const str_Default_Scope = "OneLevel" ' OneLevel current ou, SubTree recursive ou search Const const_DefaultGroupDescription = "Autogenerated shadowgroup - do not edit members manually" ' default description for auto created shadow groups LogFileName = "\\fileserver.my.domain.local\logshare\logfolder\logfile-" & Date & ".log" ' daily log file Const boolDebug = False ' Logs all OU and Group members, increases run time considerably ' ' SECURITY SETTINGS - EDIT THIS! (ensures script can not do anything bad) ' ' Impportant if user running script has access to more than shadowgroups, ensure you set below to restrict access! ' ' Enter word that must be present in shadowgroup name Const sec_GroupName_MustContain = "SHADOWGROUP" ' avoids pointing a shadowgroup destination to an existing security group, thereby controlling its members, i.e. Domain Admins ' Only allow shadowgroup destination in or below source OU Const sec_GroupOU_equal_targetOU = True '' '' Aditional information '' '' To use this script, run it with a scheduled process, '' it will use Description fields in OU and Group objects to automatically create Shadow Groups '' Be carefull to avoid duplicate group names, i.e. in auto named groups, with sub OU's having same names '' '' Notice hardcoded limit on 1000 objects (dictionary and ldap searches for shadowgroup OU/Group) '' Notice do not run this script in a loop, it is likely to memory leak. Restart the script instead. '' Notice special characters used in Active Directory objects, may interfere with the scripts ability to successfully run. '' '' Shadow Groups can be created by adding parameters to the description field of an existing security group '' or by using the description field of an OU. The description field must begin with SHADOWGROUP, '' additional parameters may be given comma seperated after the SHADOWGROUP word, with name=value. '' Parameter = Values '' '' "Scope" = "OneLevel" (default), only gets members of the targeted OU '' "SubTree" gets targeted OU and all sub OU's members '' '' "OU" = "OU/OU/OU" the OU Path to target, a full dn of LDAP://ou=A,ou=B,dc=mydomain,dc=local, '' must be written as "A/B", notice only real OU's may be in path, no builtin OU's, also no domain. '' '' "GroupOU" = "OU/OU/OU" the path to the OU where the shadow group is to be placed, same format as "OU" '' '' "GroupName" = "SHADOWGROUP Department Sales" the full custom name for the shadow group '' '' "GroupPrefix" = "SHADOWGROUP Department" group prefix, will be appended with the source OU name '' '' "OBJ" = "CUG" A combination of letters designating what objects to include in the Shadow Group '' C = Computers, U = Users, G = Groups '' '' "#" = "Description text" this parameter is ignored, but can be used to add description text to the OU/Group for other uses. '' '' SCRIPT START - DO NOT EDIT BELOW THIS LINE ---------------------------------------------------- '' ----------------------------------------------------------------------------------------------- ' Script constants, dont edit this Const ScriptVersion = "0.2" Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2 Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000 Const ADS_PROPERTY_APPEND = 3 Const ADS_PROPERTY_DELETE = 4 ' setting time of script start for logging purposes Dim startTime, endTime startTime = Timer ' Set vars used for counting what changes where made and logging Dim count_GroupCreated, count_GroupProcessed, count_GroupAborted, count_MemberAdded, count_MemberRemoved, str_LogText count_GroupCreated = 0 count_GroupProcessed = 0 count_GroupAborted = 0 count_MemberAdded = 0 count_MemberRemoved = 0 WriteLog "Script started - version " & ScriptVersion & " - " & Date & " " & Time 'Setting global dictionaries for ou and group objects Dim dictOUObjects, dictGroupObjects Set dictOUObjects = CreateObject("Scripting.Dictionary") dictOUObjects.CompareMode = TextMode Set dictGroupObjects = CreateObject("Scripting.Dictionary") dictGroupObjects.CompareMode = TextMode ' Find shadow groups to create/update ' Find OU's with shadow group enabled Start_Update_OU Find_OU_ShadowGroup() WriteLog "Done getting ShadowGroup OU's" ' Find Group's that are shadow group enabled Start_Update_Group Find_Group_ShadowGroup() WriteLog "Done getting ShadowGroup Groups" '' Script finished WriteLog "" WriteLog "Groups processed: " & vbTab & count_GroupProcessed WriteLog "Groups created: " & vbTab & count_GroupCreated WriteLog "Groups aborted: " & vbTab & count_GroupAborted WriteLog "Members added to group: " & vbTab & count_MemberAdded WriteLog "Members removed from group:" & vbTab & count_MemberRemoved WriteLog "Script finished - version " & ScriptVersion & " - " & Date & " " & Time WriteLogFile WScript.Quit ''' MAIN FUNCTIONS Function Start_Do_Parameters (Parameters, OUName, OUPath, GroupName, GroupCN) Dim arr_Parameters, str_Parameters, str_Scope, str_ObjectCategory str_ObjectCategory = str_Default_ObjectCategory str_Scope = str_Default_Scope str_GroupPrefix = str_DefaultGroupPrefix ' Go thru parameters arr_Parameters = Split(Parameters,",") For Each str_Parameter In arr_Parameters arr_tmp_parm = Split(str_Parameter, "=") Select Case UCase(arr_tmp_parm(0)) Case "SHADOWGROUP" 'Ignore Case "#" 'Ignore Case "SCOPE" ' Check if we should search subtree of ou If UCase(arr_tmp_parm(1)) = "SUBTREE" Or UCase(arr_tmp_parm(1)) = "SUB" Or UCase(arr_tmp_parm(1)) = "1" Then str_Scope = "SubTree" End If Case "OU" ' Check if we have a OU path OUPath = "LDAP://ou=" & Replace(arr_tmp_parm(1), "/", ",ou=") & "," & str_Domain arr_tmp_ouname = Split(arr_tmp_parm(1), "/") OUName = arr_tmp_ouname(0) Case "GROUPOU" ' Check if we have a OU path GroupPath = "LDAP://ou=" & Replace(arr_tmp_parm(1), "/", ",ou=") & "," & str_Domain Case "GROUPNAME" ' Check if we have a OU path GroupName = arr_tmp_parm(1) Case "GROUPPREFIX" ' Check if we have a OU path str_GroupPrefix = arr_tmp_parm(1) Case "OBJ" str_ObjectCategory = "" If InStr(arr_tmp_parm(1), "U") Then str_ObjectCategory = str_ObjectCategory & "(objectCategory=User)" If InStr(arr_tmp_parm(1), "C") Then str_ObjectCategory = str_ObjectCategory & "(objectCategory=Computer)" If InStr(arr_tmp_parm(1), "G") Then str_ObjectCategory = str_ObjectCategory & "(objectCategory=Group)" If Len(arr_tmp_parm(1)) > 1 Then str_ObjectCategory = "(|" & str_ObjectCategory & ")" Case Else WriteLog "Unknown parameter: " & str_Parameter End Select Next ' Figure out defaults If GroupName = "" Then GroupName = str_GroupPrefix & " " & OUName End If If GroupPath = "" Then GroupPath = OUPath End If If GroupCN = "" Then GroupCN = "LDAP://CN=" & GroupName & "," & Right(GroupPath,Len(GroupPath)-7) End If WriteLog "GroupName: " & GroupName ' WriteLog "GroupPath: " & GroupPath WriteLog "GroupCN: " & GroupCN ' WriteLog "OUName: " & OUName WriteLog "OUPath: " & OUPath WriteLog "Scope: " & str_Scope WriteLog "ObjectCategory: " & str_ObjectCategory ''' SECURITY CHECK IF GROUPNAME CONTAINS If InStr(GroupName, sec_GroupName_MustContain) = 0 Or InStr(GroupCN, sec_GroupName_MustContain) = 0 Then ' Group name does not contain our security word WriteLog "XXX - This group does not contain the security word, this group will not be updated!" WriteLog "XXX - This security word must be in all Shadow Group names: " & sec_GroupName_MustContain count_GroupAborted = count_GroupAborted + 1 Exit Function End If ''' SECURITY CHECK IF GroupOU is same as target OU If sec_GroupOU_equal_targetOU Then ' This means they must be same, otherwise we ignore If InStr(GroupCN, Right(OUPath, Len(OUPath)-7)) = 0 Then ' target OU is not part of group ou WriteLog "XXX - This group is not placed below the target OU, this group will not be updated!" count_GroupAborted = count_GroupAborted + 1 Exit Function End If End If '' Start the updating of the defined shadow group Start_Update_ShadowGroup OUPath, GroupCN, str_Scope, str_ObjectCategory End Function Function Start_Update_ShadowGroup (ByVal dnOU, dnGroup, str_Scope, strObjectCategory) count_GroupProcessed = count_GroupProcessed + 1 dictGroupObjects.RemoveAll dictOUObjects.RemoveAll WriteLog "Updating group: " & dnGroup Find_OU_Objects dnOU, strObjectCategory, str_Scope, dnGroup Find_Group_Objects dnGroup Add_Group_Objects dnGroup Remove_Group_Objects dnGroup End Function Function Remove_Group_Objects (ByVal dnGroup) For Each removeObject In dictGroupObjects.Items If Not dictOUObjects.Exists(removeObject) Then ' object is not in OU lets remove it Set objGroup = GetObject(dnGroup) objGroup.PutEx ADS_PROPERTY_DELETE,"member",Array(removeObject) objGroup.SetInfo WriteLog vbTab & " - removing object from group: " & removeObject count_MemberRemoved = count_MemberRemoved + 1 Set objGroup = Nothing End If Next End Function Function Add_Group_Objects (ByVal dnGroup) For Each addObject In dictOUObjects.Items If Not dictGroupObjects.Exists(addObject) Then ' object is not in group lets add it Set objGroup = GetObject(dnGroup) objGroup.PutEx ADS_PROPERTY_APPEND,"member",Array(addObject) objGroup.SetInfo WriteLog vbTab & " + adding object to group: " & addObject count_MemberAdded = count_MemberAdded + 1 Set objGroup = Nothing End If Next End Function Function Find_OU_Objects (ByVal dnOU, strObjectCategory, str_Scope, dnGroup) Set objConnection = CreateObject("ADODB.Connection") Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection objCommand.Properties("Page Size") = 1000 objCommand.CommandText = _ "<"_ & dnOU &_ ">;"_ & strObjectCategory &_ ";AdsPath;"_ & str_Scope Set objRecordSet = objCommand.Execute If objRecordSet.EOF = False Then objRecordSet.MoveFirst Do Until objRecordSet.EOF tmp_object = Right(objRecordSet.Fields("AdsPath").Value, Len(objRecordSet.Fields("AdsPath").Value) -7) ' we dont want the actual shadow group as a member If objRecordSet.Fields("AdsPath").Value <> dnGroup Then ' we do not like special chars in names, add more if you find them tmp_object = Replace(tmp_object, "\/", "/") ' add object to dictionary dictOUObjects.Add tmp_object, tmp_object If boolDebug Then WriteLog vbTab & "OU member " & tmp_object End If objRecordSet.MoveNext Loop Set objCommand = Nothing Set objConnection = Nothing End Function Function Find_Group_Objects (Byval dnGroup) On Error Resume Next ' In case group doesnt exist Dim member, arrMemberOf Set objGroup = GetObject(dnGroup) If Err.Number = -2147016656 Then ' Group does not exist Err.Clear ' Create new Group arr_tmp_ou = Split ( dnGroup, ",", 2 ) arr_tmp_cn = Split ( arr_tmp_ou(0), "//", 2) arr_tmp_name = Split ( arr_tmp_cn(1), "=", 2) WriteLog "Creating group " & arr_tmp_name(1) & " / " & arr_tmp_cn(1) & " in ou " & arr_tmp_ou(1) & " err: " & Err.Number Set objOU = GetObject("LDAP://" & arr_tmp_ou(1)) Set objGroup = objOU.Create("Group", arr_tmp_cn(1)) objGroup.Put "sAMAccountName", arr_tmp_name(1) objGroup.Put "description", Array(const_DefaultGroupDescription) objGroup.Put "groupType", ADS_GROUP_TYPE_GLOBAL_GROUP Or ADS_GROUP_TYPE_SECURITY_ENABLED Err.Clear objGroup.SetInfo If Err.Number <> 0 Then WriteLog "Problem creating group, possibly allready exists or sam account name used: " & arr_tmp_name(1) Err.Clear Else WriteLog "Created group successfully" count_GroupCreated = count_GroupCreated + 1 End If End If objGroup.GetInfo arrMemberOf = objGroup.GetEx("member") If Err.Number = -2147463155 Then ' Group has no members Err.Clear Exit Function End If For Each member In arrMemberOf 'Add the user to a dictionary object dictGroupObjects.Add member, member If boolDebug Then WriteLog vbTab & "Group Member " & member Next Set objGroup = Nothing End Function Function WriteLog (strLog) currentTime = Timer - startTime If currentTime < 0.1 Then currentTime = "0,0" If Len(currentTime) > 5 Then currentTime = Left(currentTime, 5) currentTime = currentTime & "s" While Len(currentTime) < 6 currentTime = currentTime & " " Wend WScript.Echo currentTime & vbTab & strLog str_LogText = str_LogText & currentTime & vbTab & strLog & vbCrLf End Function Function WriteLogFile () WriteLog "Appending log to file: " & LogFileName Set fso = CreateObject("Scripting.FileSystemObject") ' used for file operations Set theLog = fso.OpenTextFile(LogFileName, 8, True) theLog.WriteLine(str_LogText) theLog.Close Set theLog = Nothing Set fso = Nothing str_LogText = "" End Function Function Start_Update_OU(arr_OU) Dim i i = 0 While arr_OU(i,0) <> "" Err.Clear WriteLog "SG Object " & arr_OU(i,2) WriteLog "SG Parameter " & arr_OU(i,1) 'Start_Do_Parameters Parameters, OUName, OUpath, GroupName, GroupOU Start_Do_Parameters arr_OU(i,1), arr_OU(i,0), arr_OU(i,2), "", "" If Err.Number <> 0 Then WriteLog "ErrNumber:" & Err.Number WriteLog "ErrDescription:" & Err.Description WriteLog "ErrSource:" & Err.Source End If WriteLog "" i = i + 1 Wend End Function Function Start_Update_Group(arr_Group) Dim i i = 0 While arr_Group(i,0) <> "" Err.Clear WriteLog "SG Object " & arr_Group(i,2) WriteLog "SG Parameter " & arr_Group(i,1) 'Start_Do_Parameters Parameters, OUName, OUpath, GroupName, GroupOU Start_Do_Parameters arr_Group(i,1), "", "", arr_Group(i,0), arr_Group(i,2) If Err.Number <> 0 Then WriteLog "ErrNumber:" & Err.Number WriteLog "ErrDescription:" & Err.Description WriteLog "ErrSource:" & Err.Source End If WriteLog "" i = i + 1 Wend End Function ' Find OU's with Shadow Group properties Function Find_OU_ShadowGroup () Dim arr_tmp_object(1000,2) On Error Resume Next WriteLog "Getting OU's with shadowgroup in description" Set objConnection = CreateObject("ADODB.Connection") Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection objCommand.Properties("Page Size") = 1000 objCommand.CommandText = _ "<LDAP://" & str_BaseOU & str_Domain &_ ">;(&(objectCategory=OrganizationalUnit)(description=SHADOWGROUP*));Name,AdsPath,description;Subtree" Set objRecordSet = objCommand.Execute objRecordSet.MoveFirst count_obj = 0 Do Until objRecordSet.EOF arr_tmp_object(count_obj,0) = objRecordSet.Fields("Name").Value arrDescription = objRecordSet.Fields("description").Value arr_tmp_object(count_obj,1) = arrDescription(0) arr_tmp_object(count_obj,2) = objRecordSet.Fields("AdsPath").Value count_obj = count_obj + 1 objRecordSet.MoveNext Loop Find_OU_ShadowGroup = arr_tmp_object Set objCommand = Nothing Set objConnection = Nothing End Function ' Find Group's with Shadow Group properties Function Find_Group_ShadowGroup () Dim arr_tmp_object(1000,2) On Error Resume Next WriteLog "Getting Groups with shadowgroup in description" Set objConnection = CreateObject("ADODB.Connection") Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection objCommand.Properties("Page Size") = 1000 objCommand.CommandText = _ "<LDAP://" & str_BaseOU & str_Domain &_ ">;(&(objectCategory=Group)(description=SHADOWGROUP*));Name,AdsPath,description;Subtree" Set objRecordSet = objCommand.Execute objRecordSet.MoveFirst count_obj = 0 Do Until objRecordSet.EOF arr_tmp_object(count_obj,0) = objRecordSet.Fields("Name").Value arrDescription = objRecordSet.Fields("description").Value arr_tmp_object(count_obj,1) = arrDescription(0) arr_tmp_object(count_obj,2) = objRecordSet.Fields("AdsPath").Value count_obj = count_obj + 1 objRecordSet.MoveNext Loop Find_Group_ShadowGroup = arr_tmp_object Set objCommand = Nothing Set objConnection = Nothing End Function
The fine print
Feel free to comment, make suggestions for improvements optimizations, use for commercial purposes allowed, etc. just requires you give credit in the script used.The script is made available under the creative commons license. Any questions, need some more information, help with using it, etc. leave a comment or e-mail me.
My hope is that the script will be put to good use. But all use in a production environment is at own risk and liability.
Nice script 🙂
Thanks 🙂
I actually created another version that can also use other fields than the comments to set a ShadowGroup parameter. Available on request if needed.
add line 76-77
Dim Shell
Set Shell = CreateObject(“Wscript.Shell”)
replace line
WScript.Echo currentTime & vbTab & strLog
with
Shell.Popup currentTime & vbTab & strLog,1,,64
then you will have a 1 sec popup instead echo when running booldebug = true
Excellent script – thanks for putting the work into creating this!
I’m curious why you chose to include the function at lines 192-201. We have a very logically named OU structure with a few nested levels to apply GPO effectively, and so I’ve created a separate OU to hold all of the Shadow Groups. Because I know exactly which OU is referenced by looking at the group name, this gives me the best visibility of all Shadow Groups at once. Without commenting 192-201, I was unable to operate in this fashion.
Thanks again!
Hey
Super nice script 🙂
Is it possible to add the parent OU name to the Shadow group name?
For example:
Company -> Dep1 -> Users (Prefix Shadowgroup Users – Dep1)
Company -> Dep2 -> Users (Prefix Shadowgroup Users – Dep2)
Michael
Superb 😉
How to get the full OU-name as name in the group name?
Loil
If you look at line 51-52
‘ Only allow shadowgroup destination in or below source OU
Const sec_GroupOU_equal_targetOU = True
You just have to set that last line to false, this will allow the functionality you are using with an OU somewhere outside the path of the Shadow Group OU.
The reason behind it. It ensures that the script will NOT edit any group that is not at or below the point with the Shadow Group Setting. Since the script is most likely running with full permissions and someone might have delegated rights to a OU at some lower level. I did not want them to be able to create a shadow group setting that would make changes in a group the “script owner” has permission too, but the delegated user does not, i.e. the Administrators group.
Not out of the box, but nothing preventing you from adding it.
I am unsure what you mean, but I assume you mean the full OU path.
In my own experience I always hit the groupname length limit so this didn’t work for me.
Thats why I didnt use it, but nothing prevents you from just taking the full path and using it for the name, however it does not do that out of the box.
Great Script! Works great!
The only issue I ran into was with this line:
LogFileName = “\\fileserver.my.domain.local\logshare\logfolder\logfile-” & Date & “.log” ‘ daily log file
Cannot create a file name with the “/” character. My date format is 6/16/2011. Changed the script to write to a file called results.txt. This works well because there is a timestamp on the file itself and also in the log.
LogFileName = “\\fileserver.my.domain.local\logshare\logfolder\results.txt”‘ daily log file
Update to my earlier post. I was able to produce a date named logfile by adding/editing the following.
dtmThisDay = Day(Date)
dtmThisMonth = Month(Date)
dtmThisYear = Year(Date)
LogFileName = “d:\scripts\logfile-” & dtmThisMonth & “_” & dtmThisDay & “_” & dtmThisYear & “.log” ‘ daily log file
Hello,
I am looking for script or automation that will automatically update the Users office field with OU description. We have many Sub-OU’s depends on the location and like OU Newyork(Admin, computers, Secrutiy group, distribution group, Users).
In the Users OU, we want to update the office field with Description present in OU NewYork. Whenever someone add or move user in this OU. The office field should update automatically.
I will thankful to you.
Best Wishes,
Venkat
Great script! Since I’m not big into scripts it wasn’t 100% intuitive but I still got it working quickly. A few changes I made:
The date thing that Phil mentioned. I just used the “WeekdayName(Weekday(Now))” command instead.
Also changed the second security check “GroupOU is same as target OU”
case sensitivity was a problem so I modified the script to use:
“If InStr(ucase(GroupCN), ucase(Right(OUPath, Len(OUPath)-7))) = 0 Then”
The only question I have is about using the script on a DC vs. a member server. Does anyone have input on where it’s best to create a scheduled task for this to run?
Thanks for sharing your fixes 🙂
Hi Venkat,
You would probally need to program a new script to do that specific task, most of the code is present in this script so it would be an easy task for someone with a little knowledge of VB Scripting to reuse some of the code for your purpose.
I used this script to create a Shadowgroup and initially populate the group. Now I created a few test users and ran the script again to see if those new users get added to the group and they don’t. I did notice in the comments about a hardcoded 1000 limit on the dictionary and LDAP searches. I have about 20,000 users in my OU. How can I increased the 1000 limit?
Hi Swensc,
I would recommend not having 20.000 users in one OU, you should have the same issue when using other LDAP tools like ADUC (Active Directory Users and Computers MMC console). The limit of results from LDAP searches of 1000 is not from the scripts end, but LDAP searches in MS. I am sorry but I do not have any easy fixes for this issue. You would somehow need to make the script able to work with the users in portions, to split up the very large amount of results.
Hi,
Thanks for this well-commented script. It worked really well. Here is a little feedback.
– The fact that it is needed to put the word “shadowgroup” in each UO we want to process wasn’t obvious for me. When I found it out, I looked for a way to delete this obligation, but it looked a bit tricky and dangerous for the script.
– I didn’t know if the comma was intended in the value ‘Const str_BaseOU = “ou=Departments,’ (it looks like it is)
– I experienced great performances on a very low configuration (one processor and 512Mb Ram). When the script execution is scheduled, it takes less than 2s for 700 operations (add/delete members/groups).
Oh, and thanks to Phil for his date troubleshooting (for I use french date format).
I am glad the script was usefull and that you took the time to give feedback others can use 🙂
-Sole
Just a quick note, I do not remember it as required that the OU’s name includes the ShadowGroup Word, but it was required for the Group names, to ensure we didn’t mess with any admin/security sensitive Groups.
so to avoid a scenario where someone has rights to create OU’s and use the shadowgroup functions, they could make an OU pointing to the domain admins Group, updating its members… that is why the Word shadowgroup should be in the Group name.
I have the same problem as Phil with the line “LogFileName = “\\fileserver.my.domain.local\logshare\logfolder\logfile-” & Date & “.log” ‘ daily log file”
Logfiles are not created. I want to edit/update the script with the the solution phil also suggest. But do i have to add the lines to the script.Where do i place them?? Or do i have to replace some lines??
Thanx
Don’t mind my last post. Got it working..