Modernize / Data / Deploy / Provision / Configure / Report / Security / AI / Microsoft Copilot

Check Dataverse Security Role Direct vs Group Assignments

Identify which users have security roles assigned directly vs via Dataverse Group Teams - useful for compliance reporting

Summary

As a Power Platform Admin, you may need to prove to your Security Team that all Dataverse security roles are assigned via Group Teams only, and that no direct role assignments exist. This script queries the Dataverse Web API and outputs two lists: users with direct role assignments, and users without (meaning they receive roles exclusively via Group Teams).

System and service accounts (identified by a leading # in their name) and Microsoft-internal accounts are automatically filtered out, so only real tenant users are shown.

Example Screenshot

powershell
# ============================================================
# Dataverse Security Role Assignment Checker
# ============================================================

# Please update before running:
$orgUrl = "https://YOURORG.crm4.dynamics.com"
$maxUsers = 500

Write-Host "Getting Access Token..." -ForegroundColor Cyan
$tokenObj = Get-AzAccessToken -ResourceUrl $orgUrl
$token = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($tokenObj.Token)
)

if (-not $token) {
    Write-Error "Could not retrieve token. Please run 'Connect-AzAccount' first."
    exit
}

$headers = @{
    Authorization      = "Bearer $token"
    "OData-MaxVersion" = "4.0"
    "OData-Version"    = "4.0"
    Accept             = "application/json"
}

Write-Host "Loading users..." -ForegroundColor Cyan

$usersUrl = "$orgUrl/api/data/v9.2/systemusers?`$select=fullname,domainname,isdisabled&`$filter=isdisabled eq false and domainname ne ''&`$expand=systemuserroles_association(`$select=name)&`$top=$maxUsers"

try {
    $response = Invoke-RestMethod -Uri $usersUrl -Headers $headers -Method Get
} catch {
    Write-Error "API call failed: $_"
    exit
}

$users = $response.value | Where-Object { 
    $_.fullname -notlike "#*" -and 
    $_.domainname -notlike "*@microsoft.com" 
}

Write-Host "$($users.Count) users loaded." -ForegroundColor Cyan

$directAssignment = @()
$noDirectAssignment = @()

foreach ($user in $users) {
    $roles = $user.systemuserroles_association
    $roleNames = if ($roles -and $roles.Count -gt 0) {
        ($roles | ForEach-Object { $_.name }) -join ", "
    } else { "" }

    $obj = [PSCustomObject]@{
        Name     = $user.fullname
        Username = $user.domainname
        Roles    = $roleNames
    }

    if ($roles -and $roles.Count -gt 0) {
        $directAssignment += $obj
    } else {
        $noDirectAssignment += $obj
    }
}

Write-Host ""
Write-Host "================================================" -ForegroundColor Red
Write-Host " USERS WITH DIRECT ROLE ASSIGNMENT ($($directAssignment.Count))" -ForegroundColor Red
Write-Host "================================================" -ForegroundColor Red
if ($directAssignment.Count -gt 0) {
    $directAssignment | Format-Table
} else {
    Write-Host "No users with direct assignment found." -ForegroundColor Green
}

Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " USERS WITHOUT DIRECT ASSIGNMENT - VIA GROUP ONLY ($($noDirectAssignment.Count))" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
$noDirectAssignment | Format-Table

Prerequisites

  • Az.Accounts PowerShell module (Install-Module -Name Az.Accounts -Scope CurrentUser)
  • PowerShell 5.1 or newer
  • Dataverse role: System Administrator or System Customizer
  • Run Connect-AzAccount before executing the script

Contributors

Author(s)
Michael Roth

Disclaimer

THESE SAMPLES ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

Tool documentation & references

Keep exploring