In a recent blogpost, Protecting Microsoft 365 from on-premises attacks, it is recommended to not federate (which I have been recommending for years) and not to synchronize accounts with privileged access to Azure AD and Microsoft 365. This post contains a few script to help you determine synchronized accounts with privileged access to Azure AD.
As usual, I skip the whole “Get yourself an access token”-part of the script, for simplicity. In short words:
- Go to Graph Explorer
- Sign in with your admin account
- Click Select Permissions:

- Make sure the following consents are in place:
- Group.Read.All
- User.Read.All
- Directory.AccessAsUser.All
- PrivilegedAccess.Read.AzureAD
- RoleManagement.Read.Directory
- Copy the access token like this, into the $accessToken variable in the scripts

There are ways to automate all of this, of course.
First script – For those without Azure AD Privileged Identity Management:
The script uses the /v1.0/directoryRoles endpoint for getting directory role assignments. This is valid for all tenants, but does not return eligible assignments. Hence, only use this if you do not use PIM:
$accessToken = "eyJ0eXAiOiJKV1Q---- snip ---dtEiPuNZ8bA"
$Script:Headers = @{Authorization = "Bearer $accessToken"}
# Function that returns all role members using the directoryRoles MS Graph endpoint
function Get-AADDirectoryRoleMember
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
Position=0)]
[String] $RoleTemplateId
)
Process
{
try {
Write-Debug "Getting directory role id for role template id $RoleTemplateId"
$result = Invoke-RestMethod "https://graph.microsoft.com/v1.0/directoryRoles?`$filter=roleTemplateId eq '$RoleTemplateId'" -Headers $Script:Headers
if(($result.value|Measure-Object).Count -eq 0) {
return;
}
$directoryRoleId = $result.value[0].id
$directoryRoleDisplayName = $result.value[0].displayName
Write-Debug "Found role $directoryRoleDisplayName"
# Get members and count them
Write-Debug "Counting members"
$result = Invoke-RestMethod "https://graph.microsoft.com/v1.0/directoryRoles/$directoryRoleId/members?`$select=id" -Headers $Script:Headers
Write-Debug "Getting users assigned to role, with some details"
$result.value | ForEach-Object {
$assignment = $_
$uri = "https://graph.microsoft.com/beta/directoryObjects/$($assignment.id)"
$_r = Invoke-RestMethod $uri -Headers $Script:Headers
if($_r.'@odata.type' -eq "#microsoft.graph.group") {
$uri = "https://graph.microsoft.com/beta/groups/$($assignment.id)/transitiveMembers?`$top=999"
$_members = $null
$_members = Invoke-RestMethod $uri -Headers $Script:Headers
$_members.value | ForEach-Object {
[PSCustomObject] @{
objectId = $_.id
userType = $_.userType
upn = $_.userPrincipalName
roleTemplateId = $RoleTemplateId
directoryRoleDisplayName = $directoryRoleDisplayName
synched = $_.onPremisesDistinguishedName -or $_.onPremisesSyncEnabled
}
}
} else{
[PSCustomObject] @{
objectId = $_r.id
userType = $_r.userType
upn = $_r.userPrincipalName
roleTemplateId = $RoleTemplateId
directoryRoleDisplayName = $directoryRoleDisplayName
synched = $_.onPremisesDistinguishedName -or $_.onPremisesSyncEnabled
}
}
}
} catch {
throw "Error occured processing role '$RoleTemplateId': $_"
}
}
}
$RoleTemplateIds =
"9B895D92-2CD3-44C7-9D02-A6AC2D5EA5C3",# ApplicationAdministrator
"CF1C38E5-3621-4004-A7CB-879624DCED7C",# ApplicationDeveloper
"c4e39bd9-1100-46d3-8c65-fb160da0071f",# AuthenticationAdministrator
"7495fdc4-34c4-4d15-a289-98788ce399fd",# AzureInformationProtectionAdministrator
"aaf43236-0c0d-4d5f-883a-6955382ac081",# B2CIEFKeysetAdministrator
"3edaf663-341e-4475-9f94-5c398ef6c070",# B2CIEFPolicyAdministrator
"158c047a-c907-4556-b7ef-446551a6b5f7",# CloudApplicationAdministrator
"7698a772-787b-4ac8-901f-60d6b08affd2",# CloudDeviceAdministrator
"62e90394-69f5-4237-9190-012177145e10",# CompanyAdministrator
"17315797-102d-40b4-93e0-432062caca18",# ComplianceAdministrator
"e6d1a23a-da11-4be4-9570-befc86d067a7",# ComplianceDataAdministrator
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",# ConditionalAccessAdministrator
"5c4f9dcd-47dc-4cf7-8c9a-9e4207cbfc91",# CustomerLockBoxAccessApprover
"38a96431-2bdf-4b4c-8b6e-5d3d8abac1a4",# DesktopAnalyticsAdministrator
"9f06204d-73c1-4d4c-880a-6edb90606fd8",# DeviceAdministrators
"9360feb5-f418-4baa-8175-e2a00bac4301",# DirectoryWriters
"29232cdf-9323-42fd-ade2-1d097af3e4de",# ExchangeServiceAdministrator
"6e591065-9bad-43ed-90f3-e9424366d2f0",# ExternalIdUserflowAdministrator
"0f971eea-41eb-4569-a71e-57bb8a3eff1e",# ExternalIdUserFlowAttributeAdministrator
"be2f45a1-457d-42af-a067-6ec1fa63bc45",# ExternalIdentityProviderAdministrator
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",# GlobalReader
"fdd7a751-b60b-444a-984c-02652fe8fa1c",# GroupsAdministrator
"729827e3-9c14-49f7-bb1b-9608f156bbb8",# HelpdeskAdministrator
"8ac3fc64-6eca-42ea-9e69-59f4c7b60eb2",# HybridIdentityAdministrator
"3a2c62db-5318-420d-8d74-23affee5d9d5",# IntuneServiceAdministrator
"4d6ac14f-3453-41d0-bef9-a3e0c569773a",# LicenseAdministrator
"75941009-915a-4869-abe7-691bff18279e",# LyncServiceAdministrator
"ac16e43d-7b2d-40e0-ac05-243ff356ab5b",# MessageCenterPrivacyReader
"790c1fb9-7f7d-4f88-86a1-ef1f95c05c1b",# MessageCenterReader
"d37c8bed-0711-4417-ba38-b4abe66ce4c2",# NetworkAdministrator
"2b745bdf-0803-4d80-aa65-822c4493daac",# OfficeAppsAdministrator
"966707d0-3269-4727-9be2-8c3a10f19b9d",# PasswordAdministrator
"11648597-926c-4cf3-9c36-bcebb0ba8dcc",# PowerPlatformAdministrator
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",# PrivilegedAuthenticationAdministrator
"e8611ab8-c189-46e8-94e1-60213ab1f814",# PrivilegedRoleAdministrator
"4a5d8f65-41da-4de4-8968-e035b65339cf",# ReportsReader
"0964bb5e-9bdb-4d7b-ac29-58e794862a40",# SearchAdministrator
"8835291a-918c-4fd7-a9ce-faa49f0cf7d9",# SearchEditor
"194ae4cb-b126-40b2-bd5b-6091b380977d",# SecurityAdministrator
"5f2222b1-57c3-48ba-8ad5-d4759f1fde6f",# SecurityOperator
"5d6b6bb7-de71-4623-b4af-96380a352509",# SecurityReader
"f023fd81-a637-4b56-95fd-791ac0226033",# ServiceSupportAdministrator
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",# SharePointServiceAdministrator
"baf37b3a-610e-45da-9e62-d9d1e5e8914b",# TeamsCommunicationsAdministrator
"f70938a0-fc10-4177-9e90-2178f8765737",# TeamsCommunicationsSupportEngineer
"fcf91098-03e3-41a9-b5ba-6f0ec8188a12",# TeamsCommunicationsSupportSpecialist
"3d762c5a-1b6c-493f-843e-55a3b42923d4",# TeamsDevicesAdministrator
"69091246-20e8-4a56-aa4d-066075b2a7a8",# TeamsServiceAdministrator
"fe930be7-5e62-47db-91af-98c3a49a38b1" # UserAccountAdministrator
$RoleTemplateIds |
Get-AADDirectoryRoleMember |
# Where-Object synched |
Out-GridView
The output contains a column / property telling you whether the account is synched or not. Follow up anyone that is synched, to make sure they use cloud managed accounts instead!

Second script – For those with Azure AD Privileged Identity Management:
The script uses the newer /beta/privilegedAccess/aadroles/roleAssignments endpoint, which only works for Azure AD Premium P2 enabled tenants, and works for eligible assignments as well as active assignments.
$accessToken = "eyJ0eXAiOiJ---snip---EiPuNZ8bA"
$Script:Headers = @{Authorization = "Bearer $accessToken"}
function Get-AADPIMRoleMember
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
Position=0)]
[String] $RoleTemplateId
)
Process
{
try {
# Get TenantID from payload
$jwtPayload = $Script:Headers.Authorization -replace "Bearer " -split "\." | Select-Object -Index 1
if($jwtPayload.Length % 4 -ne 0) {
$jwtPayload = $jwtPayload.PadRight($jwtPayload.Length + 4 - $jwtPayload.Length % 4, "=")
}
$Script:TenantId = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($jwtPayload)) | ConvertFrom-Json | Select-Object -ExpandProperty tid
Write-Debug "Getting directory role id for role template id $RoleTemplateId"
$result = Invoke-RestMethod "https://graph.microsoft.com/v1.0/directoryRoles?`$filter=roleTemplateId eq '$RoleTemplateId'" -Headers $Script:Headers
if(($result.value|Measure-Object).Count -eq 0) {
return;
}
# $directoryRoleId = $result.value[0].id
$directoryRoleDisplayName = $result.value[0].displayName
Write-Debug "Found role $directoryRoleDisplayName"
Write-Debug "Getting PIM role for role template id $RoleTemplateId"
$uri = "https://graph.microsoft.com/beta/privilegedAccess/aadroles/roleAssignments?`$filter=resourceId+eq+'$Script:TenantID' and roleDefinitionId+eq+'$RoleTemplateId'&`$expand=roleDefinition(`$expand=resource)" # $expand=linkedEligibleRoleAssignment,subject,scopedResource,roleDefinition($expand=resource)
$result = Invoke-RestMethod $uri -Headers $Script:Headers
Write-Debug "Getting users assigned to role, with some details"
$result.value | ForEach-Object {
$assignment = $_
$uri = "https://graph.microsoft.com/beta/directoryObjects/$($assignment.subjectId)"
$_r = Invoke-RestMethod $uri -Headers $Script:Headers
if($_r.'@odata.type' -eq "#microsoft.graph.group") {
$uri = "https://graph.microsoft.com/beta/groups/$($assignment.subjectId)/transitiveMembers?`$top=999"
$_members = $null
$_members = Invoke-RestMethod $uri -Headers $Script:Headers
$_members.value | ForEach-Object {
[PSCustomObject] @{
objectId = $_.id
userType = $_.userType
upn = $_.userPrincipalName
roleTemplateId = $RoleTemplateId
directoryRoleDisplayName = $directoryRoleDisplayName
synched = $_.onPremisesDistinguishedName -or $_.onPremisesSyncEnabled
}
}
} else{
[PSCustomObject] @{
objectId = $_r.id
userType = $_r.userType
upn = $_r.userPrincipalName
roleTemplateId = $RoleTemplateId
directoryRoleDisplayName = $directoryRoleDisplayName
synched = $_.onPremisesDistinguishedName -or $_.onPremisesSyncEnabled
}
}
}
} catch {
throw "Error occured processing role '$RoleTemplateId': $_"
}
}
}
$RoleTemplateIds =
"9B895D92-2CD3-44C7-9D02-A6AC2D5EA5C3",# ApplicationAdministrator
"CF1C38E5-3621-4004-A7CB-879624DCED7C",# ApplicationDeveloper
"c4e39bd9-1100-46d3-8c65-fb160da0071f",# AuthenticationAdministrator
"7495fdc4-34c4-4d15-a289-98788ce399fd",# AzureInformationProtectionAdministrator
"aaf43236-0c0d-4d5f-883a-6955382ac081",# B2CIEFKeysetAdministrator
"3edaf663-341e-4475-9f94-5c398ef6c070",# B2CIEFPolicyAdministrator
"158c047a-c907-4556-b7ef-446551a6b5f7",# CloudApplicationAdministrator
"7698a772-787b-4ac8-901f-60d6b08affd2",# CloudDeviceAdministrator
"62e90394-69f5-4237-9190-012177145e10",# CompanyAdministrator
"17315797-102d-40b4-93e0-432062caca18",# ComplianceAdministrator
"e6d1a23a-da11-4be4-9570-befc86d067a7",# ComplianceDataAdministrator
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",# ConditionalAccessAdministrator
"5c4f9dcd-47dc-4cf7-8c9a-9e4207cbfc91",# CustomerLockBoxAccessApprover
"38a96431-2bdf-4b4c-8b6e-5d3d8abac1a4",# DesktopAnalyticsAdministrator
"9f06204d-73c1-4d4c-880a-6edb90606fd8",# DeviceAdministrators
"9360feb5-f418-4baa-8175-e2a00bac4301",# DirectoryWriters
"29232cdf-9323-42fd-ade2-1d097af3e4de",# ExchangeServiceAdministrator
"6e591065-9bad-43ed-90f3-e9424366d2f0",# ExternalIdUserflowAdministrator
"0f971eea-41eb-4569-a71e-57bb8a3eff1e",# ExternalIdUserFlowAttributeAdministrator
"be2f45a1-457d-42af-a067-6ec1fa63bc45",# ExternalIdentityProviderAdministrator
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",# GlobalReader
"fdd7a751-b60b-444a-984c-02652fe8fa1c",# GroupsAdministrator
"729827e3-9c14-49f7-bb1b-9608f156bbb8",# HelpdeskAdministrator
"8ac3fc64-6eca-42ea-9e69-59f4c7b60eb2",# HybridIdentityAdministrator
"3a2c62db-5318-420d-8d74-23affee5d9d5",# IntuneServiceAdministrator
"4d6ac14f-3453-41d0-bef9-a3e0c569773a",# LicenseAdministrator
"75941009-915a-4869-abe7-691bff18279e",# LyncServiceAdministrator
"ac16e43d-7b2d-40e0-ac05-243ff356ab5b",# MessageCenterPrivacyReader
"790c1fb9-7f7d-4f88-86a1-ef1f95c05c1b",# MessageCenterReader
"d37c8bed-0711-4417-ba38-b4abe66ce4c2",# NetworkAdministrator
"2b745bdf-0803-4d80-aa65-822c4493daac",# OfficeAppsAdministrator
"966707d0-3269-4727-9be2-8c3a10f19b9d",# PasswordAdministrator
"11648597-926c-4cf3-9c36-bcebb0ba8dcc",# PowerPlatformAdministrator
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",# PrivilegedAuthenticationAdministrator
"e8611ab8-c189-46e8-94e1-60213ab1f814",# PrivilegedRoleAdministrator
"4a5d8f65-41da-4de4-8968-e035b65339cf",# ReportsReader
"0964bb5e-9bdb-4d7b-ac29-58e794862a40",# SearchAdministrator
"8835291a-918c-4fd7-a9ce-faa49f0cf7d9",# SearchEditor
"194ae4cb-b126-40b2-bd5b-6091b380977d",# SecurityAdministrator
"5f2222b1-57c3-48ba-8ad5-d4759f1fde6f",# SecurityOperator
"5d6b6bb7-de71-4623-b4af-96380a352509",# SecurityReader
"f023fd81-a637-4b56-95fd-791ac0226033",# ServiceSupportAdministrator
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",# SharePointServiceAdministrator
"baf37b3a-610e-45da-9e62-d9d1e5e8914b",# TeamsCommunicationsAdministrator
"f70938a0-fc10-4177-9e90-2178f8765737",# TeamsCommunicationsSupportEngineer
"fcf91098-03e3-41a9-b5ba-6f0ec8188a12",# TeamsCommunicationsSupportSpecialist
"3d762c5a-1b6c-493f-843e-55a3b42923d4",# TeamsDevicesAdministrator
"69091246-20e8-4a56-aa4d-066075b2a7a8",# TeamsServiceAdministrator
"fe930be7-5e62-47db-91af-98c3a49a38b1" # UserAccountAdministrator
$RoleTemplateIds |
Get-AADPIMRoleMember |
# Where-Object synched |
Out-GridView
The output contains a column / property telling you whether the account is synched or not. Follow up anyone that is synched, to make sure they use cloud managed accounts instead!
Why does MFA not help in this case?
It definitely helps, but there are several attack vectors present:
– For federation, with the SolarWinds attacks, one has seen attackers forging tokens for accessing Azure AD. These forged tokens can contain the “MFA claim”, meaning that the token is accepted as MFA because Azure AD assumes the federation server has already conducted MFA.
– For synced accounts without federation, it is a lot better, but the attacker might still “sneak in” coming from trusted sites, trusted compliant computers etc. exempted from MFA through CA etc.
That makes a lot of sense. Thank you!