Syncing Security Groups with team membership
Published Sep 04 2018 07:36 PM 90.4K Views
Microsoft

In this post, I present a PowerShell script to synchronize the membership between security groups and Office 365 groups.

 

Security groups in Azure Active Directory (AAD) have long been a useful way to manage sets of users in the enterprise -- even going back to on-premises Active Directory and before that, Windows NT global groups. The Office 365 Groups service is the more modern way to address this need, used by Microsoft Teams, Planner, Outlook Groups, Yammer, Power BI, and more. Of course, they're not connected (yet), which is unfortunate but not atypical given the evolution of platforms and products and sometimes divergent scenarios.

 

Many companies use AAD security groups extensively, for good reason, and they have a lot of intellectual capital vested in the creation, curation, and management of those security groups. At Microsoft we've used security groups to manage access to internal support resources, bug databases, and source code systems. These companies logically want to leverage their security groups investment for Microsoft Teams and other Office 365 Groups-based services, but they can't right now. If you add a security group to a team membership list, Teams will do a one-time expansion of the security group (same for a distribution list), but any subsequent changes are not reflected in the team, and vice versa.

 

Obviously, a great solution would be to base the team membership directly on a security group, so that any changes to the security group are reflected in the team in Microsoft Teams, and vice versa. This would be similar to how Teams leverages the Office 365 Groups service. The engineering team is aware of this request and it is marked as on the backlog. You can provide additional input on the use case and priority via the User Voice feedback system, item 1861385. Similar user feedback has also been provided to the Office 365 Groups team, and you can read and vote on their feedback system too, item 33942997.

 

But while we wait for those engineering teams to get to this work (and deal with a thousand other demands on their time), let's take a look at a short-term solution that will unblock companies looking to synchronize security group membership with team membership.

 

The premise is straightforward: create a PowerShell script that will run periodically, maybe every 12 hours or 24 hours, which synchronizes one or more pairs of security group/Office 365 group. Now, the PowerShell interfaces are a little different for each type of group (see note above re: platform evolution and divergent scenarios), but with a little hacking and slashing, I got it to work reasonably well.

 

BIG WARNING: I was a physics major in college who fell backwards into software product management. I'm not a developer, and only sort of an "engineer" (in Canada, they probably wouldn't let me wear the pinky ring). My coding process involves a lot of trial-and-error mixed with Stack Overflow research. This code should not be considered production-ready. Rather, look at it as an illustrated proof-of-concept that actual real developers can use to build actual real code.

 

The source code is on GitHub, naturally: https://github.com/danspot/Danspot-Scripts-and-Samples-Emporium 

 

Here's roughly what the script does:

  1. Get the security group ID and Office 365 group ID; in the script, this is done via lookup based on user input, but in a real app, this should probably be read in from a configuration file
  2. Scan the membership of the security group
  3. Make sure all those users are also in the Office 365 group
  4. Remove anybody in the Office 365 group who is not in the security group

 

In this diagram, you can see how one user ("Rajesh") is in the AAD security group but not the Office 365 group, so that user should be added to the latter. And another user, "Stewart" is in the Office 365 group but not the security group, so that user should be removed. Bye Stewart!

 

group-sync-diagram.png

Here's the key part of the code that scans the security group and adds missing members (in a brute force way) to the Office 365 group:

 

 

# loop through all Security Group members and add them to a list
# might be more efficient (from a service API perspective) to have an inner foreach
# loop that verifies the user is not in the O365 Group
Write-Output "Loading list of Security Group members"
$securityGroupMembersToAdd = New-Object System.Collections.ArrayList
foreach ($securityGroupMember in $securityGroupMembers)
{
$memberType = $securityGroupMember.GroupMemberType
if ($memberType -eq 'User') {
$memberEmail = $securityGroupMember.EmailAddress
$securityGroupMembersToAdd.Add($memberEmail)
}
}

# add all the Security Group members to the O365 Group
# this is not super efficient - might be better to remove any existing members first
# this might need to be broken into multiple calls depending on API limitations
Write-Output "Adding Security Group members to O365 Group"
Add-UnifiedGroupLinks -Identity $O365GroupID -LinkType Members -Links $securityGroupMembersToAdd

 

And here's the part of the code that removes users who are in the Office 365 group but not the security group. Probably the trickiest part of the script was finding and aligning the user ID between the two different groups schemas.

 

 

# loop through the O365 Group and remove anybody who is not in the security group
Write-Output "Looking for O365 Group members who are not in Security Group"
$O365GroupMembersToRemove = New-Object System.Collections.ArrayList
foreach ($O365GroupMember in $O365GroupMembers) {
        $userFound = 0
        foreach ($emailAddress in $O365GroupMember.EmailAddresses) {
# trim the protocol ("SMTP:")
                $emailAddress = $emailAddress.substring($emailAddress.indexOf(":")+1,$emailAddress.length-$emailAddress.indexOf(":")-1)
                if ($securityGroupMembersToAdd.Contains($emailAddress)) { $userFound = 1 }
        }
        if ($userFound -eq 0) { $O365GroupMembersToRemove.Add($O365GroupMember) }
}


if ($O365GroupMembersToRemove.Count -eq 0) {
        Write-Output "   ...none found"
} else {
# remove members
        Write-Output " ... removing $O365GroupMembersToRemove"
                foreach ($memberToRemove in $O365GroupMembersToRemove) {
                Remove-UnifiedGroupLinks -Identity $O365GroupID -LinkType Members -Links $memberToRemove.name
        }
}

 

Important notes:

  1. This script should probably be run periodically, perhaps every 6 hours or every 24 hours, maybe on an admin’s desktop, or better yet, using Azure Automation.
  2. Either the security group or the Office 365 group should probably be designated as the "primary" and any changes to that would be reflected on the other, "replica" entity, and not vice-versa. For example, if the security group was the primary, but a user changed the team membership in Microsoft Teams (the replica), that change on the Teams side should be overwritten. Given most people interested in this solution probably have a lot of time and effort already invested in security groups, it’s likely that you'll want to make the security group the primary in this model.
  3. There are sometimes odd ways that emails are handled in the directory, so you may need to test and tweak the script to handle email addresses for your domain(s), especially if you have multiple email domains or users with secondary email addresses.
  4. This script probably requires more hardening against other situations like nested security groups, Unicode email addresses, resource and room accounts, etc.
  5. This script may not scale very well as currently written, although in practice that may not be a real problem. There may be limits to the number of users that can be added in one operation (so batching may be required). There are some foreach loops and brute-force adding of members, which probably isn't super efficient.
  6. It's probably a good idea to not do the cleanup to remove stray team members (in the Office 365 group) who are not in the security group. Rather, log that information and have a real human go and double check and remove if necessary. You wouldn't want a coding or configuration error to accidentally nuke every member of a team.
  7. In general, I think it's a good idea to create an audit log so all actions taken by the script are output to a log file, which a human can review. That file can then be stored somewhere in case of a bug or error, to make it easier to fix things.
  8. The script right now asks for your credentials (twice, since there are two different APIs being used). There are probably some PowerShell best practices for storing credentials in non-interactive mode, or somehow leveraging the OS credentials. Hard-coding credentials into the script is a shortcut but seems like a bad idea.
  9. As noted earlier, to use this in production, you'll probably want to make the script run from a configuration file containing a list of pairs of security group ID plus Office 365 group ID. You can get those IDs using some of the same API calls in the sample script (like building a separate script just for that), or via Graph Explorer for Office 365 or Graph Explorer for Azure AD.

 

And there you go! Use the comments to let me know how it works, suggest improvements, link to your own solutions, and more. Just remember not to add Stewart to any of your teams.

 

About the author: Dan Stevenson was one of the early creators of Microsoft Teams. He led the product management team behind key features like teams and channels, guest access, Teams for education, and the free version of Teams. He recently moved with his family to Taipei, Taiwan, where he leads Teams customer engineering for the Asia Pacific region.

28 Comments
Steel Contributor

Hi @Dan Stevenson

 

Thank you for this informative post! We often get the question in the other direction, how can we use our (self-service managed) Team or Office 365 group as a security principal. Apparently you can embed an Office 365 group in an AAD security group. Do you see any issues with that apporach?

 

 

Thanks,

 

Bart

Brass Contributor

Is an AD Security Group-to-Office 365 Group Sync on the roadmap, and if yes, when? 

Thanks

Douglas

Copper Contributor
Copper Contributor

Will  AD Security Group-to-Office 365 Group Sync feature on the roadmap support Nested Security Groups?

 

Group Driven Membership 

https://www.microsoft.com/en-us/microsoft-365/roadmap?featureid=50002

 

Copper Contributor
Copper Contributor

Since the ability to link to an external security group still seems to be "in the plans" now, I'm wondering if the mechanism that tells Teams to suppress the membership interfaces for dynamic groups (link belows) could be triggered when the programmatic syncing that you've described is used for selected Teams.

https://docs.microsoft.com/en-us/microsoftteams/dynamic-memberships

Martin

The roadmap item 50002 is gone. 2 years since it was placed on the "roadmap" - did this get released? 

 

Release roadmap items within 6 months or don't place them on the roadmap. Uservoice is the wish list - the roadmap should be the contract with your users.

 
 
Brass Contributor

Yes @Joe Gasper I noticed that too. Is there any update on this? 

Copper Contributor

Did they just delete 50002 and replaced it with a new roadmap item: https://www.microsoft.com/da-dk/microsoft-365/roadmap?filters=&searchterms=83113 ?

Copper Contributor

The latest estimate is December 2022.  Argh!!  What a joke.  Years later and Powershell hacks must still be used.  Microsoft is likely trying to kill off on premise AD.

Brass Contributor

They do have this tool for anyone interested, takes a bit of configuring and work to setup but it does work

 

https://github.com/microsoftgraph/group-membership-management

Copper Contributor

@wcsand - Hi that was a fast response!  Do you know how admins are handling this for large organizations?  There must be a recipe somewhere.  So if I have:

 

GroupA in AD I usually create an O365 group called "GroupA Team"

But how does one do this for dozens of groups?  I've run the sync-sg-o365 script successfully as a test.  But it handles only one group at a time and requires signon to AD and Azure for each run.  It seems like everyone here is on their own for a solution.  But hard to believe someone out there hasn't modified the script to run against multiple groups.

Brass Contributor

Hey no problemo

 

The answer is the Github link I posted. You basically use the Azure app it creates to generate group relationships, and it handles everything in the background. There are some costs associated with the infrastructure in Azure it requires, but the app itself is free. It does take some time to get setup though but once it's going it's pretty maintenance free. MS uses it themselves apparently

 

You could create your own powershell that syncs the groups using a stored secure credential on one of your AD servers, but like the script would be more in depth than the one on this page!

 

I think, if it's for a large org and you need a lot of these relationships created I would check out the github link I posted there... for now that would be the best option

Copper Contributor

OK, it looks like a bit of work to setup but I'll give it a go.  We have E3 licensing so have access to Azure Premium features.  Until now I didn't know this utility existed - ask and ye shall receive I suppose, lol.

Much appreciated!

Brass Contributor

Good luck man!

Copper Contributor

Prerequisites .... Visual Studio, lol!  Nope that's out of my wheelhouse so it's Powershell to the rescue.  Thanks anyways!

Brass Contributor

Yep TBH I actually gave up on that tool myself. It is such a trainwreck to setup and the instructions are terrible.

 

Powershell it is

Copper Contributor

What about a on-premise scheduled ps-script which writes all group-names of all group-membership of a user in an ad-attribute (for example extensionattribute1)?
This extensionattribute1 could now used in azure ad by a dynamic o365/security group with dynamic memberships (operator = contains).

So you would have an dynamic o365/security group which bases on a on-prem-ad-security group.

 

Copper Contributor

Hi Johannes, I basically gave up on it and manually go in and update groups once a month.  I'd be up for a Powershell script if a demo script existed.  I've tried some of them and even though I've followed them to the letter they inevitably fail.  It's incomprehensible that such a basic feature as syncing groups requires Visual Studio.  They say that the syncing of groups feature in 365 is to be released this year but I'm not holding my breath on that.

can you update the links for the feedback items?  user voice was decommissioned...

 

item 1861385 & item 33942997

 

I'm wondering what's the status.   Were those items implemented? :thinking_face:

Microsoft

Can you please provide an update on this feature request? It is a pain to maintain the membership list separately in Teams and IdWeb

Brass Contributor

I had a request open w Microsoft regarding access to the private preview of a feature (applying roles to AD synced groups).

This was the response:

"Thanks for the time, the private preview is currently not available for customers. According to the information that is handled, it will be available in the coming months of 2022 at the end of 3 and 4 quarter. This is the information sent by the Product Group engineers, who are an internal department that handles requests like this."

 

So, it's coming... hopefully

Copper Contributor

Almost 2023 and no productive Solution.

Thank you MS for building unfinished Products ;:)

I found this 2 interesting links:
https://techcommunity.microsoft.com/t5/microsoft-entra-azure-ad-blog/create-quot-nested-quot-groups-...

 

And a Power Automate template which keeps user between Security Groups and O365 Groups in Sync:
https://powerautomate.microsoft.com/en-us/templates/details/0b51be005f85488c8da4c23b929f771c/synchro...

 

I will have to test this, because i want to have guests also in a Office365 Group. = Internal Users + guests.

Copper Contributor

@pg2020 Thank you. That Power Automate template appears perfectly suited for the sync.  But have you (or anyone here) found a way to run it for rows in a spreadsheet having the group mappings?  EDIT: I got it working.  Created a List Rows in Excel flow, and in the Apply to Each step, manually recreate every step of Microsoft's flow, then run it periodically. Note there was a typo and logic flaw in the template, so be careful.  Thanks again, pg2020  

Brass Contributor

Best I can tell, as of 2024, this still hasn't been implemented. We need to be able to use AD groups within Teams and have them stay in sync automatically!

Copper Contributor

Hi MgSam,

I should have posted an update to this.  Group writeback was introduced and we've been using that since last year.  We use this and manage the groups in Azure to have them writeback to on prem AD.  Works great and now Teams are in sync with on prem groups.  But yes the master copy is in Azure so it doesn't work the other way.  Despite this I'm happy with the result.  You just have to be careful with things like file share permissions, group distribution lists if you use/used Exchange hybrid, etc. as these need to be migrated.  We decommissioned our Exchange server but continue in hybrid mode so you use Powershell to manage things.  A couple weeks getting the scripting to work but worth the time savings.

Copper Contributor

When we need the membership of a Team to be updated based on a synced OnPrem AD group, we change the membership type from assigned to dynamic and use a dynamic rule like this one:

 

user.memberof -any (group.objectid in ['a2758e4c-1650-463d-be36-126ccb6cf572'])

 

If you want the membership to be the union of two or more groups, you can add them to the list separated by commas.

 

Copper Contributor

Yes that looks like a good solution too.   Since Microsoft didn't provide any easy solutions for such a long time it's not surprising there are so many improvised ones.  Whatever works.  Out of the gate you'd think if you're providing businesses with a cloud identity system like Azure they would have solved it from the beginning (Azure first came out in 2010; group writeback in 2022).  Intune is the same.  You have a 20 year old solution in group policy that works so well but they don't replicate that to Intune.  Then IT admins use 100 different custom Powershell scripts to do the same thing.

Version history
Last update:
‎Jan 26 2021 01:39 PM
Updated by: