Learn how to assign Microsoft Graph application permissions to a Managed Identity or Service Principal in Microsoft Entra ID using Microsoft Graph PowerShell.
# Validated on Microsoft.Graph PowerShell SDK v2.29.1
$ErrorActionPreference = 'stop'
$requiredScopes = @('Application.Read.All', 'AppRoleAssignment.ReadWrite.All')
$ctx = Get-MgContext
if (-not $ctx -or ($requiredScopes | Where-Object { $ctx.Scopes -notcontains $_ })) {
Connect-MgGraph -Scopes $requiredScopes -NoWelcome
}
function Grant-AppRolesToServicePrincipal {
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'PName_RName')]
param(
[Parameter(Mandatory, ParameterSetName = 'PName_RName')]
[Parameter(Mandatory, ParameterSetName = 'PName_RApp')]
[string]$PrincipalDisplayName,
[Parameter(Mandatory, ParameterSetName = 'PId_RName')]
[Parameter(Mandatory, ParameterSetName = 'PId_RApp')]
[string]$PrincipalObjectId,
[Parameter(Mandatory, ParameterSetName = 'PName_RApp')]
[Parameter(Mandatory, ParameterSetName = 'PId_RApp')]
[string]$ResourceAppId,
[Parameter(Mandatory, ParameterSetName = 'PName_RName')]
[Parameter(Mandatory, ParameterSetName = 'PId_RName')]
[string]$ResourceDisplayName,
[Parameter(Mandatory)]
[string[]]$RoleValues
)
if ($PSCmdlet.ParameterSetName -like 'PId_*') {
$principal = Get-MgServicePrincipal -ServicePrincipalId $PrincipalObjectId -Select 'id,displayName'
if (-not $principal) { throw "Service principal with objectId '$PrincipalObjectId' not found." }
}
else {
$escaped = $PrincipalDisplayName.Replace("'", "''")
$candidates = Get-MgServicePrincipal -Filter "startswith(displayName,'$escaped')" -All -Select 'id,displayName'
$exact = $candidates | Where-Object { $_.DisplayName -eq $PrincipalDisplayName }
if ($exact) { $candidates = $exact }
$foundMatches = @($candidates)
if ($foundMatches.Count -eq 0) {
throw "No service principal matched '$PrincipalDisplayName'. Use -PrincipalObjectId for certainty."
}
if ($foundMatches.Count -gt 1) {
throw "Multiple service principals matched '$PrincipalDisplayName': $($foundMatches.DisplayName -join ', '). Use -PrincipalObjectId."
}
$principal = $foundMatches[0]
}
if ([string]::IsNullOrWhiteSpace($principal.Id)) { throw "Resolved principal has no Id." }
if ($PSCmdlet.ParameterSetName -like '*_RApp') {
$resourceSp = Get-MgServicePrincipal -Filter "appId eq '$ResourceAppId'" -Select 'id,displayName,appId,appRoles'
if (-not $resourceSp) { throw "Resource service principal with appId '$ResourceAppId' not found." }
}
else {
$escapedRes = $ResourceDisplayName.Replace("'", "''")
$resources = Get-MgServicePrincipal -Filter "startswith(displayName,'$escapedRes')" -All -Select 'id,displayName,appId,appRoles'
$exactRes = $resources | Where-Object { $_.DisplayName -eq $ResourceDisplayName }
if ($exactRes) { $resources = $exactRes }
$resMatches = @($resources)
if ($resMatches.Count -eq 0) {
throw "Resource service principal '$ResourceDisplayName' not found. Prefer -ResourceAppId."
}
if ($resMatches.Count -gt 1) {
throw "Multiple resources named '$ResourceDisplayName': $($resMatches.DisplayName -join ', '). Use -ResourceAppId."
}
$resourceSp = $resMatches[0]
}
if ([string]::IsNullOrWhiteSpace($resourceSp.Id)) { throw "Resolved resource SP has no Id." }
$existing = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $principal.Id -All |
Where-Object { $_.ResourceId -eq $resourceSp.Id }
$results = @()
foreach ($val in $RoleValues) {
$appRole = $resourceSp.AppRoles | Where-Object {
$_.IsEnabled -and
$_.Value -eq $val -and
($_.AllowedMemberTypes -contains 'Application')
} | Select-Object -First 1
if (-not $appRole) {
Write-Warning "Role '$val' not found on '$($resourceSp.DisplayName)' (or not an Application role)."
$results += [pscustomobject]@{ Principal = $principal.DisplayName; Resource = $resourceSp.DisplayName; RoleValue = $val; Status = 'MissingRole' }
continue
}
if ($existing | Where-Object { $_.AppRoleId -eq $appRole.Id }) {
Write-Host "Already assigned: $val to '$($principal.DisplayName)' on '$($resourceSp.DisplayName)'." -ForegroundColor Yellow
$results += [pscustomobject]@{ Principal = $principal.DisplayName; Resource = $resourceSp.DisplayName; RoleValue = $val; Status = 'AlreadyAssigned' }
continue
}
if ($PSCmdlet.ShouldProcess($principal.DisplayName, "Grant $val on $($resourceSp.DisplayName)")) {
$appRoleAssignmentParams = @{
ServicePrincipalId = $principal.Id
BodyParameter = @{
principalId = $principal.Id
resourceId = $resourceSp.Id
appRoleId = $appRole.Id
}
}
New-MgServicePrincipalAppRoleAssignment @appRoleAssignmentParams | Out-Null
Write-Host "Assigned $val to '$($principal.DisplayName)' on '$($resourceSp.DisplayName)'." -ForegroundColor Green
$results += [pscustomobject]@{ Principal = $principal.DisplayName; Resource = $resourceSp.DisplayName; RoleValue = $val; Status = 'Assigned' }
}
}
return $results
}
<#
Usage:
$params = @{
PrincipalDisplayName = 'AAA-001'
ResourceDisplayName = 'Microsoft Graph'
RoleValues = @('AuditLog.Read.All', 'User.Read.All')
}
Grant-AppRolesToServicePrincipal @params
OR
$params = @{
PrincipalObjectId = '1654793e-8817-4050-b7c0-1f14c331d8d7'
ResourceAppId = '00000003-0000-0000-c000-000000000000'
RoleValues = @('AuditLog.Read.All', 'User.Read.All')
}
Grant-AppRolesToServicePrincipal @params
#>