I still haven't had the time to implement this in my test domain but there may be a viable solution to RBAC based on AD groups through one of the two options below:
This is all based on https://community.powerbi.com/t5/Community-Blog/Power-BI-Group-management-using-Active-Directory-roles-and/ba-p/308092
This is all based on https://techcommunity.microsoft.com/t5/Microsoft-Teams-Blog/Syncing-Security-Groups-with-team-membership/ba-p/241959 author is Dan Stevenson
This is all based on https://community.powerbi.com/t5/Community-Blog/Power-BI-Group-management-using-Active-Directory-roles-and/ba-p/308092
Set-ExecutionPolicy Unrestricted -Scope
CurrentUser -Force
# Get security credential based on a user name and password
$User_Credential = Get-Credential
# Get Exchange cmdlets
$Session = New-PSSession -ConfigurationName
Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/
-Credential $User_Credential
-Authentication Basic
-AllowRedirection
Import-PSSession $Session -Allowclobber
# Store Role and Group names; change as required
$AD_Role_Name = "AD Role name
here"
$O365_Group_Name = "O365 Group
name here"
# Get AD role membership
$Active_Directory_Role = get-group -Identity $AD_Role_Name
| Select-Object
-Property Members
| foreach { $_.Members } | Sort-Object
# Get O365 Group membership
$Office_365_Group = get-group -Identity $O365_Group_Name
| Select-Object
-Property Members
| foreach { $_.Members } | Sort-Object
# Find and store new AD role users
$Add_O365_Group = Compare-Object $Active_Directory_Role $Office_365_Group
-PassThru | Left_Side
# Add new AD role users
$Add_O365_Group | % {Add-UnifiedGroupLinks -Identity
$O365_Group_Name -LinkType
Members -links
"$_"}
# Debug print users
Write-Host "Added
the following users: " $Add_O365_Group
-foregroundcolor black
-backgroundColor Green
# Find and store old AD role users; switch filter position of
AD_Role and O365_Group
$Remove_O365_Group = Compare-Object $Office_365_Group $Active_Directory_Role
-PassThru | Left_Side
# Remove old AD role users
$Remove_O365_Group | % {Remove-UnifiedGroupLinks -Identity
$O365_Group_Name -LinkType
Members -links
"$_"}
# Debug print users
Write-Host "Removed
the following users: " $Remove_O365_Group
-foregroundcolor black
-backgroundColor Yellow
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:
- 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.
- 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.
- 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.
- This
script probably requires more hardening against other situations like
nested security groups, Unicode email addresses, resource and room
accounts, etc.
- 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.
- 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.
- 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.
- 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.
- 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.
Comments
Post a Comment