0 votes

Is it possible with Adaxes to update a distribution groups membership based on the users in an OU? I know this is exactly what a dynamic distribution group is for, but for certain reasons I need this to be a normal distribution group not dynamic.

by (150 points)

1 Answer

0 votes
by (216k points)
selected by
Best answer

Update 2020

Starting with version 2020.1, Adaxes supports rule based groups. For details, have a look at the corresponding section in the following tutorial: https://www.adaxes.com/tutorials_AutomatingDailyTasks_AddUsersToGroupsByDepartment.htm#dynamicgroups.

Original

Hello,

Yes, this is possible. For this purpose, you can use Scheduled Tasks. Scheduled Tasks allow you to perform certain actions on a regular basis in unattended mode. So, you can create a Scheduled Task that updates the group membership, say, once a day.

To create such a Scheduled Task:

  1. Create a new Scheduled Task.

  2. On step 3 of the Create Scheduled Task wizard, select the Group object type.

  3. On step 4, add the Run a program or PowerShell script action and paste the following script in the Script field. The script will update the group membership with children of the necessary OU(s).

     $ouDNs = @(
         "OU=MyOU_1,DC=domain,DC=com",
         "OU=MyOU_2,DC=domain,DC=com"
     ) # TODO: modify me
     $onlyDirectChildren = $False # TODO: modify me
    
     function GetUsers($dn, $currentMembers, $usersToAdd, $onlyDirectChildren)
     {
         $searcher = $Context.BindToObjectByDN($dn)
         $searcher.SearchFilter = "(sAMAccountType=805306368)"
         $searcher.PageSize = 500
         if ($onlyDirectChildren)
         {
             $searcher.SearchScope = "ADS_SCOPE_ONELEVEL"
         }
         else
         {
             $searcher.SearchScope = "ADS_SCOPE_SUBTREE"
         }
    
         $searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
         $searcher.SetPropertiesToLoad(@("objectGuid"))
    
         try
         {
             $searchResult = $searcher.ExecuteSearch()
             $users = $searchResult.FetchAll()
    
             foreach ($userId in $users)
             {
                 $userGuid = [Guid]$userId.Properties["objectGuid"].value
                 if ($currentMembers.Remove($userGuid))
                 {
                     continue
                 }
    
                 $usersToAdd.Add($userGuid) | Out-Null
             }
         }
         finally
         {
             $searchResult.Dispose()
         }
     }
    
     # Get current group members
     $currentMembers = New-Object "System.Collections.Generic.HashSet[Guid]"
     try
     {
         $groupMemberGuidsBytes = $Context.TargetObject.GetEx("adm-DirectMembersGuid")
     }
     catch
     {
         $groupMemberGuidsBytes = @()
     }
    
     foreach ($guidBytes in $groupMemberGuidsBytes)
     {
         $guid = [Guid]$guidBytes
         $member = $Context.BindToObject("Adaxes://<GUID=$guid>")
         if ($member.Class -ne "user")
         {
             continue
         }
         $currentMembers.Add($guid) | Out-Null
     }
    
     $usersToAdd = New-Object "System.Collections.Generic.HashSet[Guid]"
     foreach ($dn in $ouDNs)
     {
         GetUsers $dn $currentMembers $usersToAdd $onlyDirectChildren
     }
    
     # Remove group members who are not children of the Organizational Unit
     foreach ($userGuid in $currentMembers)
     {
         $Context.TargetObject.Remove("Adaxes://<GUID=$userGuid>")
     }
    
     # Add new members to the group
     foreach ($userGuid in $usersToAdd)
     {
         $Context.TargetObject.Add("Adaxes://<GUID=$userGuid>")
     }
  4. In the script, modify the following to meet your requirements:

    • $ouDNs - specifies the Distinguished Names (DNs) of the OUs children of which must be members of the group,
    • $onlyDirectChildren - specifies whether to include direct children of the OUs only. When this parameter is set to $True, the script will add direct children of the specified OUs only, ignoring objects located in any sub-OUs.
  5. Enter a short description for the script and click OK.

  6. On step 5, add the group you want to update to the Activity Scope of the Scheduled Task. When adding it, select the This Group object option.

0

Exactly what I was looking for. Thank you.

I had a follow up question. I'm going to need to do this for about 15 different distribution groups. Is setting up 15 different scheduled tasks with exactly the same action/script with changed OU's to look at the best way to do this or would their be a more optimal configuration?

Thanks!

0

Hello,

Having 15 Scheduled Tasks is not the best way to accomplish what you want. As far as we understand, these are going to be 15 different groups and 15 different OUs, is that correct?

0

That's is correct. There will be 15 different distribution groups that all reside in the same OU. Each DG has to add users from a different OU. So there is 1 OU for the DGs and 15 for the users.

Thanks

0

Hello,

An updated script is ready, find it below. To use it with Adaxes, you'll need a bit different Scheduled Task than the one that you've created before. To create a Scheduled Task that updates memberships of multiple groups with users located in certain OUs:

  1. Create a new Scheduled Task.

  2. On the 3rd step of the Create Scheduled Task wizard, select the Show all object types option.

  3. Select the Domain-DNS object type. Running the Task on a domain allows to run the script only once per a Task run.

  4. On the 4th step of the wizard, add the Run a program or PowerShell script action and paste the following script in the Script field.

     $groupInfos = @{
         "CN=Group1,OU=Groups,DC=domain,DC=com" = "OU=MyOU_1,DC=domain,DC=com"
         "CN=Group2,OU=Groups,DC=domain,DC=com" = "OU=MyOU_2,DC=domain,DC=com"
     } # TODO: modify me.
    
     $onlyDirectChildren = $False # TODO: modify me
    
     function GetUsers($dn, $currentMembers, $usersToAdd, $onlyDirectChildren)
     {
         $searcher = $Context.BindToObjectByDN($dn)
         $searcher.SearchFilter = "(sAMAccountType=805306368)"
         $searcher.PageSize = 500
         if ($onlyDirectChildren)
         {
             $searcher.SearchScope = "ADS_SCOPE_ONELEVEL"
         }
         else
         {
             $searcher.SearchScope = "ADS_SCOPE_SUBTREE"
         }
    
         $searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
         $searcher.SetPropertiesToLoad(@("objectGuid"))
    
         try
         {
             $searchResult = $searcher.ExecuteSearch()
             $users = $searchResult.FetchAll()
    
             foreach ($userId in $users)
             {
                 $userGuid = [Guid]$userId.Properties["objectGuid"].value
                 if ($currentMembers.Remove($userGuid))
                 {
                     continue
                 }
    
                 $usersToAdd.Add($userGuid) | Out-Null
             }
         }
         finally
         {
             $searchResult.Dispose()
         }
     }
    
     foreach ($groupDN in $groupInfos.Keys)
     {
         # Get current group members
         $currentMembers = New-Object "System.Collections.Generic.HashSet[Guid]"
         $group = $Context.BindToObjectByDN($groupDN)
         try
         {
             $groupMemberGuidsBytes = $group.GetEx("adm-DirectMembersGuid")
         }
         catch
         {
             $groupMemberGuidsBytes = @()
         }
    
         foreach ($guidBytes in $groupMemberGuidsBytes)
         {
             $guid = [Guid]$guidBytes
             $member = $Context.BindToObject("Adaxes://<GUID=$guid>")
             if ($member.Class -ne "user")
             {
                 continue
             }
             $currentMembers.Add($guid) | Out-Null
         }
    
         # Get users located in the linked OU
         $usersToAdd = New-Object "System.Collections.Generic.HashSet[Guid]"
         GetUsers $groupInfos[$groupDN] $currentMembers $usersToAdd $onlyDirectChildren
    
         # Remove group members who are not children of the Organizational Unit
         foreach ($userGuid in $currentMembers)
         {
             $group.Remove("Adaxes://<GUID=$userGuid>")
         }
    
         # Add new members to the group
         foreach ($userGuid in $usersToAdd)
         {
             $group.Add("Adaxes://<GUID=$userGuid>")
         }
     }
    
  5. In the script, modify the following to meet your requirements:

    • $groupInfos - specifies a map of the Distinguished Names (DNs) of the groups to the DNs of the OUs where the group members are located;
    • $onlyDirectChildren - specifies whether to include direct children of the OUs only. When this parameter is set to $True, the script will add direct children of the specified OUs only, ignoring objects located in any sub-OUs.
  6. Add a short description for the script and click OK.

  7. On the 5th step, assign the Scheduled Task over any of your AD domains.

  8. Click Finish.

0

That's working perfectly, thank you.

I had a follow up question, purely for my inquisitiveness, why run the command against Domain-DNS? What does this object actually link to?

Thanks!

0

Hello,

A Domain-DNS object represents an Active Directory domain. For more information, have a look at the following article by Microsoft: https://msdn.microsoft.com/en-us/librar ... 85%29.aspx.

As to why we chose this object type for the Task, the object type doesn't really matter for this particular Task. The thing is that everything that the script needs is already included in the script: the group DNs and the OU DNs are hard-coded in the hash table. So, the choice of the target object for this particular task is more a matter of convenience.

An advantage of using the Domain-DNS object type for the task is that the task will be run only once each time it is triggered. If you create a Scheduled Task for e.g. the Group object type and assign it over your domain, it will be run as many times as many groups you have in your AD domain. Assigning the Task over a specific object inside your domain (e.g. a user or group) can also cause issues, because the user/group can be deleted or moved somewhere, whereas a domain is more or less stable.

Related questions

0 votes
1 answer

Hello We have the need to create a home page action for creating groups, security and distribution. We would like to see if this script https://www.adaxes.com/script-repositor . ... 3 (whatever is typed) Thank you for taking the time to look at this. Jay

asked Mar 12, 2018 by willy-wally (3.2k points)
0 votes
1 answer

Is there any documentation to create &amp; manage DDG's in Adaxes?

asked Jan 17, 2013 by mdeflice (350 points)
0 votes
1 answer

Rule-based membership fails for security enabled distribution group with error "The term 'Add-DistributionGroupMember' is not recognized as the name of a cmdlet, function, ... Exchange Online before running this PowerShell command. Here is rule based set up.

asked Mar 7, 2023 by KIT (960 points)
0 votes
1 answer

Could I please get some best practice tips on how to automate Distribution Group Membership in Adaxes please? For example: I have a group "UK Staff" I have Tom and Dick who have the AD ... which is where I'm not clear of the right/neat way to do it? Thanks :)

asked Apr 21, 2017 by hutchingsp (240 points)
0 votes
1 answer

Is there a way to update a distribution group using a CSV file as the source through the web portal? This script is an example of how I would do in manually ... import-csv C:\Distro1.csv | foreach {add-distributiongroupmember -id Distro1-member $_.Name }

asked Dec 11, 2012 by mdeflice (350 points)
3,589 questions
3,278 answers
8,303 comments
548,105 users