0 votes

All,

Looking for some advice / guidance on something I've been working on. We have a pshell script in place currently that reads from a flat file we pull out of our HR system that lists users Department #, Manager, Employee Type, etc. The script runs every morning and updates those attributes in AD. Sweet!

Next, i've created a handful of "Department xxx" groups for each of our departments. I would like to leverage an adaxes scheduled task to check each of our users daily to see if their department number changed, and if so add them to the new department group. Again not hard. (If department field equals "7510", add use to "Department 7510 Group).

The issue....
1. How can throw an approval workflow so someone on the IAM team can approve or deny each change before adaxes places a user in these groups?
2. On approval, I would like to then have the script remove the user from the "old" department group.

Thoughts? Any help is appreciated! Thanks!

by (490 points)
0

Support,

Would you happen to have a script or some feature built into Adaxes that can help me with this that i'm not aware of? Thanks!

0

Hello Ben,

We will make you a sample, however we need to clarify a couple of things to make the script suited to your requirements.

  1. On approval, I would like to then have the script remove the user from the "old" department group.

If approval is not granted, what should the script do? Remove the user from those groups anyway or not?

Also, should there be an additional approval for removing the user from the groups, or this part should be completed without any approval?

0

Support, thanks for writing back. My apologizes for not seeing your reply!!

In response to your approval workflow question(s): I have yet to dabble into approval workflows within Adaxes so i'm a little gray on what that would look like.

If we create an approval workflow for this would myself (or others I grant the ability to) be able to "Select All" so the script makes those changes in bulk or do you have to approve everything on a one-by-one basis? If it's a one-by-one basis I may suggest we forget the approval workflow and build this without the approval.

So to answer your questions:

If approval is NOT granted, what should the script do? Remove the user from those groups anyway or not?
- If NOT granted, the script should NOT do anything with the the users account, the original group(s) that were on the account before this script ran should remain.

Also, should there be an additional approval for removing the user from the groups, or this part should be completed without any approval?
- No additional workflow needs to be created when removing the users from the group. If we "Approve" it, go ahead and add the user to the new group and remove the old group(s).

Does that help? If not let me know, I checked "Notify me" this time so I'll know when you respond.

Thanks!

0

Support, for what it's worth our groups all start with "ORG_7510_"...."ORG_2070_" etc.

I wrote up a Scheduled task that states "If the 'Department Number' property equals 7510" then Add the user to the 'ORG_7510_etc' group. I'm just missing something to loop back and remove them from all other groups that start with ORG_...

Thanks again!!

0

Just wondering if their is any progress on this? Thanks in advance!

1 Answer

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

Hello Ben,

Sorry for the delayed reply.

Have a look at the following script in our repository: http://www.adaxes.com/script-repository ... t-s403.htm.

0

No need for apologizes, I was asking a lot.

With that being said I'm a little confused on how I go about utilizing this script. How does this $groupNameTemplate parameter work? Like I mentioned before, our "Department" group names start with ORG_DEPARTMENTNUMBER.

I'm going to cut to the chase, How would I go about modifying the GroupNameTemplate parameter to work with the ORG_XXXX groups I have.

Help, thanks thanks!

0

If i understand you right it should be this:-

$groupNameTemplate = "Department_{0}_" # TODO: modify me

To this:-

$groupNameTemplate = "ORG_{0}" # Modified!
0

Ah, great. We were close to getting that to work. I ran into another snag on this now...and will blame myself for not being clear on this.

If it was the case (like I asked), that our department groups are "ORG_7510" this would work. Is is the case that they are actually "ORG_7510_DEPARTMENTNAME1" or "ORG_7510_DEPARTMENTNAME2" etc...

Is there a way to make this all work without a ton of re-work? We tried updating the groupNameTemplate variable but didn't have any luck.

ORG_{0}_*
ORG_{0000}_*

Thanks!

0

Hello Ben,

We don't quite get it... Do you mean that each department can have more than 1 group? Is that correct? Should the action be sent for approval to all the groups that belong to a department?

0

Well I may of led you guys to think our Department group names are "ORG_7510", "ORG_0871" etc when it's actually "ORG_7510_INFORMATION TECHNOLOGY" or "ORG_0817_FINANCE" for example.

From the testing we tried, It appears that the script is attempting is recognizing that user X's department number changed to 7510, and is then trying to place the user in "ORG_7510" group when in fact the group name is "ORG_7510_INFORMATION_TECHNOLOGY" and errors out. We receive an error that says group ORG_7510" cannot be found...which is true.

Is there a way to wildcard this out so it places a user in group "ORG_7510*"?

0

Hello Ben,

You will need to update the following line in the script we referenced in Automation of "Department" AD Groups:

$groupPath = SearchGroup "(&(objectCategory=group)(name=$groupNameToSearch))" $NULL

Replace with:

$groupPath = SearchGroup "(&(objectCategory=group)(name=$groupNameToSearch*))" $NULL
0

OK, here is where we are at with this. We updated the code per your recommendation but are still receiving an error when running it.

Our test user in this particular case is a member of two Department (ORG_) groups currently. One incorrect department group, and the correct group they should be in. This script should take a pass at the account, check the department attribute, update the account with the corresponding department group, then remove any other "ORG_" groups on the account....next account.

Here is a screenshot of the user in AD belonging to the two groups:


The user's "DepartmentNumber" attribute currently is 7510

Running the code I'm attaching at the bottom, this is the error we receive


This is the end result we would like to see in this particular example

#  Update group memberships of user based on department
#  The script adds a user to an AD group that is associated with the user's department and removes from groups associated with other departments.
#  http://www.adaxes.com/script-repository/updategroupmembership-base-on-department-s403.htm

<#
Parameters:

    $departmentProperty - specifies the LDAP display name of the property that stores the name of the user's department;     = DepartmentNumber
    $groupNameTemplate - specifies a template for names of groups that represent various departments.                        = ORG_{0}?  ORG_{0}_*?
    The {0} placeholder in the template will be replaced with the value of the property specified by $departmentProperty.
#>

$departmentProperty = "DepartmentNumber" 
$groupNameTemplate = "ORG_{0}"

function SearchGroup ($filter, $list)
{
    # Find groups
    $searcher = $Context.BindToObject("Adaxes://RootDSE")
    $searcher.SearchFilter = $filter
    $searcher.PageSize = 500                                              # page size unlimited?? 5000??
    $searcher.SearchScope = "ADS_SCOPE_SUBTREE"
    $searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
    $searcher.SetPropertiesToLoad(@("name"))
    $searcher.VirtualRoot = $True

    try
    {
        $searchResultIterator = $searcher.ExecuteSearch()
        $searchResults = $searchResultIterator.FetchAll()

        if ($list -ne $NULL)
        {
            foreach ($searchResult in $searchResults)
            {
                $list.Add($searchResult.AdsPath, $searchResult.Properties["name"].Value.ToString().ToLower())
            }
        }
        elseif ($searchResults.Length -gt 1)
        {
            $Context.LogMessage("Found more than one group for department '$department'", "Warning")
            return $NULL
        }
        elseif ($searchResults.Length -eq 0)
        {
            $Context.LogMessage("There is no group for department '$department'", "Warning")
            return $NULL
        }
        else
        {
            return $searchResults[0].AdsPath
        }
    }
    finally
    {
        # Release resources
        $searchResultIterator.Dispose()
    }
}

function UpdateGroupMembership ($groupPath, $groupName, $operation, $groupInfo)
{
    if ($groupPath -eq $NULL)
    {
        return
    }

    $group = $Context.BindToObjectEx($groupPath, $True)
    switch ($operation)
    {
        "Add"
        {
            try
            {
                $group.Add($Context.TargetObject.AdsPath)
                $groupInfo.Add($groupPath, $groupName)
            }
            catch
            {
                $operationInfo = $group.GetLastOperationInfo()
                if (-not([System.String]::IsNullOrEmpty($operationInfo.ExecutionLog.FirstErrorEntry)))
                {
                    $Context.LogMessage("An error occurred when adding the user to group '$groupName'. Error: " + $_.Exception.Message, "Warning")
                }
            }
        }

        "Remove"
        {
            try
            {
                $group.Remove($Context.TargetObject.AdsPath)
            }
            catch
            {
                $operationInfo = $group.GetLastOperationInfo()
                if (-not([System.String]::IsNullOrEmpty($operationInfo.ExecutionLog.FirstErrorEntry)))
                {
                    $Context.LogMessage("An error occurred when removing the user from group '$groupName'. Error: " + $_.Exception.Message, "Warning")
                }
            }
        }
    }
}

# Get the user's department
try
{
    $department = $Context.TargetObject.Get($departmentProperty)
}
catch
{
    $department = $NULL
}

# Get names and paths of the groups the user is already a member of
try
{
    $groupGuidsBytes = $Context.TargetObject.GetEx("adm-DirectMemberOfGuid")
}
catch
{
    $groupGuidsBytes = @()
}

$groupInfo = @{}
if ($groupGuidsBytes.Length -ne 0)
{
    # Build filter
    $filter = New-Object "System.Text.StringBuilder"
    $searchValue = [System.String]::Format($groupNameTemplate, "*")

    [void]$filter.Append("(&(objectCategory=group)(name=$searchValue)(|")
    foreach ($guidBytes in $groupGuidsBytes)
    {
        $filterPart = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("objectGuid", $guidBytes)
        [void]$filter.Append($filterPart)
    }
    [void]$filter.Append("))")

    SearchGroup $filter.ToString() $groupInfo
}

if ($department -eq $NULL)
{
    # Remove from all groups that belong to various departments
    foreach ($path in $groupInfo.Keys)
    {
        UpdateGroupMembership $path $groupInfo[$path] "Remove"
    }
    return # Exit the script
}
else
{
    $groupNameToSearch = [System.String]::Format($groupNameTemplate, $department).ToLower()
    if (-not($groupInfo.ContainsValue($groupNameToSearch)))
    {
        # Add to group
        $groupPath = SearchGroup "(&(objectCategory=group)(name=$groupNameToSearch*))" $NULL
        UpdateGroupMembership $groupPath $groupNameToSearch "Add" $groupInfo
    }
}

# Check whether it is necessary to remove the user from groups for other departments
if ($groupInfo.ContainsValue($groupNameToSearch))
{
    foreach ($path in $groupInfo.Keys)
    {
        $groupName = $groupInfo[$path]
        if ($groupName -eq $groupNameToSearch)
        {
            continue
        }

        UpdateGroupMembership $path $groupInfo[$path] "Remove"
    }
}

If you have any other questions...let us know. And as always, thanks again!

0

Hello Ben,

Thank you for the details. Find the updated script below.

$departmentProperty = "department" # TODO: modify me
$groupNameTemplate = "ORG_{0}" # TODO: modify me

function SearchGroup ($filter, $list)
{
    # Find groups
    $searcher = $Context.BindToObject("Adaxes://RootDSE")
    $searcher.SearchFilter = $filter
    $searcher.PageSize = 500
    $searcher.SearchScope = "ADS_SCOPE_SUBTREE"
    $searcher.ReferralChasing = "ADS_CHASE_REFERRALS_NEVER"
    $searcher.SetPropertiesToLoad(@("objectGUID", "name"))
    $searcher.VirtualRoot = $True

    try
    {
        $searchResultIterator = $searcher.ExecuteSearch()
        $searchResults = $searchResultIterator.FetchAll()

        if ($list -ne $NULL)
        {
            foreach ($searchResult in $searchResults)
            {
                $list.Add([Guid]$searchResult.Properties["objectGUID"].Value, $searchResult.Properties["name"].Value)
            }
        }
        elseif ($searchResults.Length -gt 1)
        {
            $Context.LogMessage("Found more than one group for department '$department'", "Warning")
            return $NULL
        }
        elseif ($searchResults.Length -eq 0)
        {
            $Context.LogMessage("There is no group for department '$department'", "Warning")
            return $NULL
        }
        else
        {
            return ,$searchResults
        }
    }
    finally
    {
        # Release resources
        $searchResultIterator.Dispose()
    }
}

function UpdateGroupMembership ($guid, $groupName, $operation, $groupInfo)
{
    if ($guid -eq $NULL)
    {
        return
    }

    $groupPath = "Adaxes://<GUID=$guid>"
    $group = $Context.BindToObjectEx($groupPath, $True)
    switch ($operation)
    {
        "Add"
        {
            try
            {
                $group.Add($Context.TargetObject.AdsPath)
                $groupInfo.Add($guid, $groupName)
            }
            catch
            {
                $operationInfo = $group.GetLastOperationInfo()
                if (-not([System.String]::IsNullOrEmpty($operationInfo.ExecutionLog.FirstErrorEntry)))
                {
                    $Context.LogMessage("An error occurred when adding the user to group '$groupName'. Error: " + $_.Exception.Message, "Warning")
                }
            }
        }

        "Remove"
        {
            try
            {
                $group.Remove($Context.TargetObject.AdsPath)
            }
            catch
            {
                $operationInfo = $group.GetLastOperationInfo()
                if (-not([System.String]::IsNullOrEmpty($operationInfo.ExecutionLog.FirstErrorEntry)))
                {
                    $Context.LogMessage("An error occurred when removing the user from group '$groupName'. Error: " + $_.Exception.Message, "Warning")
                }
            }
        }
    }
}

# Get the user's department
try
{
    $department = $Context.TargetObject.Get($departmentProperty)
}
catch
{
    $department = $NULL
}

# Get names and paths of the groups the user is already a member of
try
{
    $groupGuidsBytes = $Context.TargetObject.GetEx("adm-DirectMemberOfGuid")
}
catch
{
    $groupGuidsBytes = @()
}

$groupInfo = @{}
if ($groupGuidsBytes.Length -ne 0)
{
    # Build filter
    $filter = New-Object "System.Text.StringBuilder"
    $searchValue = [System.String]::Format($groupNameTemplate, "*")

    [void]$filter.Append("(&(objectCategory=group)(name=$searchValue)(|")
    foreach ($guidBytes in $groupGuidsBytes)
    {
        $filterPart = [Softerra.Adaxes.Ldap.FilterBuilder]::Create("objectGuid", $guidBytes)
        [void]$filter.Append($filterPart)
    }
    [void]$filter.Append("))")

    SearchGroup $filter.ToString() $groupInfo
}

if ($department -eq $NULL)
{
    # Remove the user from all groups that belong to various departments
    foreach ($guid in $groupInfo.Keys)
    {
        UpdateGroupMembership $guid $groupInfo[$guid] "Remove"
    }
    return # Exit the script
}
else
{
    $groupNameToSearch = [System.String]::Format($groupNameTemplate, $department).ToLower()
    $searchResults = SearchGroup "(&(objectCategory=group)(name=$groupNameToSearch*))" $NULL
    if ($searchResults.Length -eq $NULL)
    {
        return
    }

    $newGroupGuid = [Guid]$searchResults[0].Properties["objectGuid"].Value
    if (-not($groupInfo.ContainsKey($newGroupGuid)))
    {
        # Add to group
        $groupName = $searchResults[0].Properties["name"].Value
        UpdateGroupMembership $newGroupGuid $groupName "Add" $groupInfo
    }
}

# Check whether it is necessary to remove the user from groups for other departments
if ($groupInfo.ContainsKey($newGroupGuid))
{
    foreach ($guid in $groupInfo.Keys)
    {
        if ($guid -eq $newGroupGuid)
        {
            continue
        }

        $groupName = $groupInfo[$guid]
        UpdateGroupMembership $guid $groupName "Remove"
    }
}
0

Thanks for the help, this did the trick This is a great step forward for us! Now we can start nesting some access under the department groups we have.

Related questions

0 votes
1 answer

As part of offboarding a user I need to generate a report of all AD groups, Entra groups and all Azure / M365 roles and licenses the user has before they ... about keeping a record of the leavers configured profile to simplify cloning them onto new starters.

asked Jun 24 by dhardyuk (20 points)
0 votes
1 answer

I created a group Business Rule that triggers "After adding or removing a member from a group". On its Activity Scope I added a test group, and set it for "The group ... does not trigger. What should I do to make the BR detect this (admittedly rare) case?

asked Mar 16, 2023 by alex.vanderwoude (60 points)
0 votes
1 answer

I'd like to create a a custom report to show any approval requests (Approved, Pending, and Rejected) for membership in certain AD groups within our domain. These groups grant users ... " (Just In Time) in the name of the group. Is something like this possible?

asked Mar 30, 2020 by sirslimjim (480 points)
0 votes
1 answer

Good Morning, I've been working through some of my processes and I'm not looking to make sure the deletion of Home directories (both remote and standard) as well as ... for user deletion. If there are any questions or clarification needed, please let me know.

asked Oct 16, 2015 by jtop (700 points)
0 votes
1 answer

We have almost 200 departments, and I need to add all of them to the drop down for the Department field (via Property Patterns). Is there a way to add all the 200 department names to the drop down list in a single shot instead of adding one by one?

asked Apr 7, 2011 by jwilson1010 (20 points)
3,589 questions
3,278 answers
8,303 comments
548,115 users