How to Report Membership of Microsoft 365 Compliance Role Groups

The Looming End for the Security and Compliance Center

Updated 18 June 2023

A reader asked how to create a report of the membership of Microsoft 365 role groups. Although this sounds like a straightforward question, the answer is complex. Here’s why.

Originally, compliance functionality was workload-based. Exchange Online had its own features as did SharePoint Online. In 2016, Microsoft introduced the Office 365 Security and Compliance Center (SCC) to bring together functionality which applied across all workloads. Permissions for the SCC follow the Exchange Online Role-Based Access Control (RBAC) model. Users receive permissions to perform actions through membership of role groups. If your account is a member of the right role group, you can perform a compliance action, like running a content search or managing an eDiscovery case. If it’s not, you won’t see the options to perform those actions displayed in the SCC.

Here’s where the situation becomes complicated. We are in the middle of a transition from the SCC to the Microsoft 365 compliance center, which Microsoft launched in 2018. Three years and a lot of confusion later, an April 15 blog post warns that Microsoft will soon start to redirect users automatically from the SCC to the Microsoft 365 compliance center. Message center notification MC256030 posted on May 12 confirms that a new permissions management page in the Microsoft 365 compliance center will make role management easier (Microsoft 365 roadmap item 72239).

Update: The Microsoft 365 compliance center is now the Microsoft Purview compliance portal. I’ve also updated the PowerShell code in this post to use the Microsoft Graph PowerShell SDK.

New Permissions Management Page

The new permissions page in the Microsoft 365 compliance center
Figure 1: The new permissions page in the Microsoft 365 compliance center

The new permissions management page (Figure 1) allows management for both Azure AD roles and compliance center roles (more correctly, role groups). The differences between the two are:

  • Azure AD roles allow performance of directory-related tasks. The full set of Azure AD roles is visible by running the Get-AzureADDirectoryRole cmdlet or through the Roles and administrators section of the Azure AD admin center. Azure AD roles support advanced role management functionality like Privileged Identity Management to assign roles to users for limited periods.
  • Compliance center role groups allow performance of compliance tasks like eDiscovery. Each role group contains one or more roles, which is how accounts gain permission. The 43 compliance role groups (my tenant has 44 because I created a specific role group to handle certain processing) have no other function except to control access to different compliance features. The SCC was the previous location to manage these roles. To update the set of users who receive permissions through membership of a compliance role group, an account must be a global administrator or assigned the Role Management role (a role assigned only to the Organization Management role group).

The permission management page shows Azure AD roles used to performance compliance tasks. Currently, the page lists nine Azure AD roles like compliance administrator and compliance data administrator. Other Azure AD roles like Teams Administrator don’t appear because they are not associated with compliance management.

Reporting Who Holds Compliance Roles

Returning to the original question of how to generate a report about the holders of different compliance roles, the answer depends on if you want to report the membership of compliance role groups or Azure AD roles. Given that more functionality is governed by the latter type at present, the following code is a solution.

The steps to create the report are:

  • Uses the Connect-IPPSSession cmdlet (in the Exchange Online management module) to connect to the Compliance endpoint. You’ll need to use a tenant administrator account or one holding the compliance Organization Management role.
  • Fetches the list of known role groups.
  • Parses the set of members for each role group by converting the odd values used to represent members Exchange Hosted Organizations/ into mailbox display names (the last part of the name is the Azure AD object identifier for the account).
  • Writes details out into a PowerShell list and displays the results using the Out-GridView cmdlet.

Here’s the code:

[array]$RoleGroups = Get-RoleGroup
$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($RoleGroup in $RoleGroups) {
    $Members = $RoleGroup.Members
    $MemberNames = [System.Collections.Generic.List[Object]]::new()
    ForEach ($Member in $Members) {
       $MemberName = (Get-ExoMailbox -Identity $Member.SubString(($Member.IndexOf("")+16),36) -Erroraction SilentlyContinue).DisplayName 
    If ($RoleGroup.WhenChanged -eq "Wednesday 1 January 2020 00:00:00") {
       $RoleGroupChanged = "Never"
    } Else {
       $RoleGroupChanged = Get-Date($RoleGroup.WhenChanged) -format g }

    $MemberNames = $MemberNames -join ", "
    $ReportLine = [PSCustomObject][Ordered]@{  
       "Role Group"           = $RoleGroup.DisplayName 
       "Members"              = $MemberNames
       "Last Updated"         = $RoleGroupChanged   }
} #End ForEach $RoleGroup
$Report | Sort-Object "Role Group" | Out-GridView

Figure 2 shows what the report looks like. A simple Export-CSV command will write the details out to a CSV file if you want to manipulate the data in Excel.

Reporting membership of compliance role groups
Figure 2: Reporting membership of compliance role groups

The same approach works to create a report for the Azure AD roles. In this case, you use the Get-MgDirectoryRole cmdlet to find the set of roles and Get-MgDirectoryRoleMember cmdlet to process each role (here’s an example of using these cmdlets to report on Microsoft 365 admin accounts which aren’t protected by multi-factor authentication).

Connect-MgGraph -Scopes Directory.Read.All
[array]$RoleGroups = Get-MgDirectoryRole | Sort-Object DisplayName

$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($RoleGroup in $RoleGroups) {
  [array]$Members = Get-MgDirectoryRoleMember -DirectoryRoleId $RoleGroup.Id
  $MemberNames = $Members.additionalProperties.displayName -join (", ")
  $ReportLine = [PSCustomObject][Ordered]@{  
       "Role Group"          = $RoleGroup.DisplayName 
       "Members"             = $MemberNames
       "Description"         = $RoleGroup.Description  }
} #End ForEach $RoleGroup
$Report | Out-GridView

Simple questions often have complex answers. In this case, it’s a matter of deciding what kind of role holders you want to report. Once you know that, the PowerShell to generate the report is relatively straightforward.

Learn lots more about how different parts of Office 365 work by subscribing to the Office 365 for IT Pros eBook. We go where other writing teams don’t, and we keep our book refreshed with monthly updates.

22 Replies to “How to Report Membership of Microsoft 365 Compliance Role Groups”

  1. the first script does not work fine , in the “$MemberName = (Get-ExoMailbox -Identity $Member.SubString(($Member.IndexOf(“”)+16),36) -Erroraction SilentlyContinue).DisplayName ” can’t make the subsrick

    1. obviously something doesn’t work as well as it did in my environment when it’s in your environment, so it’s an opportunity to debug. Did you check what the $Member variable contained?

  2. Hey Tony,

    Stupid question, how can i connect both Connect-IPPSession and Connect-ExchangeOnline Cmdlets? Are you saying if run your script it will connect both of them at the same time? I have not had much luck getting Connect-IppSession to work in conjunction with any other modules.


    1. Run Connect-ExchangeOnline to connect to the Exchange Online management endpoint, and then run Connect-IPPSsession to connect to the compliance endpoint. You’ll then be able to use the Exchange Online and compliance cmdlets.

      1. Hey Tony, I will give that a try. But i swear i did that at least 3xs. Maybe just the wrong order.


  3. It ran successfully. But it doesnt report for example my admin user account which is part of the Compliance Administrator group (In the security and compliance center). I also tried using Get-ExoRecipient which seemed to work better but didnt quite get the job done.

      1. Hey Tony,

        A fair point. I was able to work around it by using Get-Recipient. That seemed to work better. It sometimes shows system.object[0] but when checking that seems only to occur for blank spaces in the repport line (For example, username1, , username2) shows the system object value.

        but the data is correct.


  4. For some reason the output does not seem to match the membership displayed in the Compliance center.

  5. Hi, Tony, and thanks for the code.

    It worked fine a couple of times, but now I’m facing an exception for the “SubString” method, at the command:

    $MemberName = (Get-ExoMailbox -Identity $Member.SubString(($Member.IndexOf(“”)+16),36) -Erroraction SilentlyContinue).DisplayName

    Error is:

    Exception calling “Substring” with “2” argument(s): “startIndex cannot be larger than length of string.
    Parameter name: startIndex”

    I tried to change the values for the command $Member.IndexOf…. but I can’t get through.
    I also changed the command into this below, not having the error but with useless results (empty Roles column and “SystemObject[]” in the Members column)

    $MemberName = (Get-ExoMailbox -Identity $Member.SubString(0,($Member.lenght))).DisplayName

    What can I do to solve this?

    Thank you in advance.

    1. It’s PowerShell, so you will have to debug it based on what results you see from your tenant. It looks as if the $Member variable is not populated with a value. I’d start by checking there.

  6. @Tony Redmond – I’m getting this to run, but a few Role Groups members show a “,” at the beginning and don’t list all of the members I can see in the Admin Center. Any idea why this is?

    1. I’ve had a look at the code (and revised the Azure AD code to use the Microsoft Graph PowerShell SDK) and it all works as expected. I don’t see what you reported.

  7. I am not sure if this will help anyone but the first script which uses IPPSSession, kept tossing back blanks and took forever to run.due to Get-EXOMailbox. When I was debugging this (as Tony mentioned several times) this was the error I was getting due to the Get-EXOMailbox cmdlet running against the GUID returned as a Member of the Members property from the RoleGroup.

    “Get-EXOMailbox : Error while querying REST service. HttpStatusCode=500 ErrorMessage={“error”:{“code”:”InternalServerError”,”message”:”The call to Microsoft Exchange
    Active Directory Topology service on server ‘TopologyClientTcpEndpoint (localhost)’ returned an error. Error details Active Directory server is not available. Error
    message: Active directory response: The LDAP server is unavailable..”,”innererror”:{“message”:”The call to Microsoft Exchange Active Directory Topology service on server
    ‘TopologyClientTcpEndpoint (localhost)’ returned an error. Error details Active Directory server is not available. Error message: Active directory response: The LDAP
    server is unavailable..”,”type”:”Microsoft.Exchange.Data.Directory.ADTransientException”}}}}”

    I changed Get-EXOMailbox to Get-Mailbox and it worked ok, but I want to move this to MS Graph as well and you should too. Thank you Tony for your point in the right direction!

    I did make another change…I do not know if this is a tenant setting, but I have been with Office 365 since it was called BPOS back in 2010 so my tenant has seen some “stuff” LOL

    I changed…
    If ($RoleGroup.WhenChanged -eq “Wednesday 1 January 2020 00:00:00”)
    If ($RoleGroup.WhenChanged.ToUniversalTime() -eq “Wednesday 1 January 2020 00:00:00”)

    My WhenChanged times were local to my time zone and that date Tony is using is Universal Tme (UTC/GMT)

    1. I think there’s been some problems with Get-ExoMailbox in the last few days. I’ve seen that error too… I’ve told Microsoft. They are “investigating.” It would be helpful if you could file a service incident too.

      1. I will do that now.

        I think this has to due with MS pushing hard on a move to the Microsoft Graph PowerShell SDK. You are kind of warned or pointed to it when you attempt to use Connect-AzureAD

        “WARNING: Install the latest PowerShell module, the Microsoft Graph PowerShell SDK, for new features and improvements!

        And I have not seen this message appear until today when Connect-IPPSSession is invoked

        We have made updates to move the SCC admin experience to REST-based APIs. In doing so, we will be deprecating the legacy Remote PowerShell (RPS) protocol starting July 15,

        Benefits of REST-based cmdlets: improved security, WinRM no longer required for client-server communication, improved error handling.

        The REST API has the same cmdlets available and feature parity with RPS(V1) cmdlets, so existing scripts and processes don’t need to be updated. Simply using the new modul
        e will ensure REST is used rather than RPS.

        For more information, go to

        May be old news for the rest of you, but with this just showing up out of the blue for me, I tend to think maybe the “new” guy got to release the latest version to production (just kidding guys!).

      2. Unfortunately, there is no Graph API for managing Exchange mailboxes, so pushing people to use the SDK wouldn’t do much in this instance. Microsoft is pushing people to use the SDK – but only where it makes sense (like Azure AD).

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.