When using Azure Active Directory for adding role-based access control to your web applications and APIs, it is highly recommended to use application roles. This allows you to define custom application roles and these can be assigned to users and applications. A clean way to secure your applications!
The challenge
Preferably, role assignments are automated through privileged release pipelines. The two PowerShell commands that can be used to automate this – New-AzureADServiceAppRoleAssignment and New-AzureADUserAppRoleAssignment – are not easy to use, as it is not always clear what the exact Ids are that you have to provide.
The solution
That’s why I prefer to automate this based on display names and let a script take care of fetching the required Ids. Lately, I have developed such a script to assign Azure AD application roles to users and applications. Hereby, I share it with the community. The script can be found in this gist.
Config file
The script is driven by a simple config file, that contains a JSON array of role assignments:
- description: free text field that describes the role assignment
- client_type: “user” or “application”
- client_principal_name: the users’ UPN (someone@example.com) or the display name of the service principal (enterprise application)
- server_app_registration_name: the display name of the app registration to which you want to grant the client access
- role_name the display name of the application role you want to assign to the configured client
This is how you configure access for a user (UPN):
{ | |
"description": "Grant Toon administrator access on application Z.", | |
"client_type" : "user", | |
"client_principal_name": "toon@yourazurecoach.com", | |
"server_app_registration_name": "app-registration-z-prod", | |
"role_name": "administrator" | |
} |
This is how you configure access for an application (service principal):
{ | |
"description": "Grant service principal X reader access on application Z.", | |
"client_type" : "application", | |
"client_principal_name": "service-principal-x-prod", | |
"server_app_registration_name": "app-registration-z-prod", | |
"role_name": "reader" | |
}, |
Script
You can use the script like this:
- Download the script and the config file.
- Update the config files to your needs
- Trigger the script via PowerShell
.\aad-apply-role-assignments.ps1 -TenantId xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -ConfigFilePath ".\aad-role-assignments.json"
If you are interested, this is how the script looks like:
param ( | |
[string] $TenantId, | |
[string] $ConfigFilePath | |
) | |
$ErrorActionPreference = "Stop" | |
Write-Host Start Azure AD role assignment script | |
Write-Host "-Tenant Id:" $TenantId -ForegroundColor Gray | |
Write-Host "-Config File Path:" $ConfigFilePath -ForegroundColor Gray | |
Write-Host Installing and importing AzureAD Module | |
if (Get-Module -ListAvailable -Name AzureAD) { | |
Import-Module -Name "AzureAD" | |
} | |
else { | |
Install-Module -Name "AzureAD" -Force | |
} | |
Write-Host Connecting to Azure AD Tenant within current security context | |
$azure_context = Get-AzContext | |
$account_id = $azure_context.Account.Id | |
Write-Host "-Account Id:" $azure_context.Account.Id -ForegroundColor Gray | |
Connect-AzureAD -TenantId $TenantId -AccountId $account_id | |
Write-Host Loading role assignments from config file | |
$role_assignments = (Get-Content $ConfigFilePath -Raw) | ConvertFrom-Json | |
Write-Host Looping each configured role assignment | |
foreach($role_assignment in $role_assignments) | |
{ | |
Write-Host Applying role assigment... started -ForegroundColor Green | |
Write-Host "-Description:" $role_assignment.description -ForegroundColor Gray | |
Write-Host "-Client principal Name:" $role_assignment.client_principal_name -ForegroundColor Gray | |
Write-Host "-Server App Registration Name:" $role_assignment.server_app_registration_name -ForegroundColor Gray | |
Write-Host "-Role Name:" $role_assignment.role_name -ForegroundColor Gray | |
Write-Host Getting the server application registration | |
$aad_filter = "DisplayName eq '" + $role_assignment.server_app_registration_name + "'" | |
$server_application_registration = Get-AzureADApplication -Filter $aad_filter | |
if (!$server_application_registration) { throw "Cannot find configured server application registration with name '" + $role_assignment.server_app_registration_name + "'" } | |
Write-Host Getting the server service principal id | |
$aad_filter = "AppId eq '" + $server_application_registration.AppId + "'" | |
$server_service_principal = Get-AzureADServicePrincipal -Filter $aad_filter | |
$server_service_principal_id = $server_service_principal.ObjectId | |
Write-Host "-Server service principal Id: " $server_service_principal_id -ForegroundColor Gray | |
Write-Host Getting the Id for the configured application role | |
$role_id = ($server_application_registration.AppRoles | Where-Object DisplayName -eq $role_assignment.role_name).Id | |
if (!$role_id) { throw "Cannot find configured application role with name '" + $role_assignment.role_name + "'" } | |
Write-Host "-Role Id: " $role_id -ForegroundColor Gray | |
if(($role_assignment.client_type -ne "application") -and ($role_assignment.client_type -ne "user")) { throw "Incorrect client_type '" + $role_assignment.client_type + "' provided." } | |
switch ($role_assignment.client_type) | |
{ | |
"application" | |
{ | |
Write-Host Getting the configured client service principal | |
$aad_filter = "DisplayName eq '" + $role_assignment.client_principal_name + "'" | |
$client_service_principal = (Get-AzureADServicePrincipal -Filter $aad_filter) | |
if (!$client_service_principal) { throw "Cannot find configured client service principal with name '" + $role_assignment.client_principal_name + "'" } | |
$client_service_principal_id = $client_service_principal.ObjectId | |
$client_service_principal_name = $client_service_principal.DisplayName | |
Write-Host "-Client service principal Id:" $client_service_principal_id -ForegroundColor Gray | |
Write-Host Assigning the Azure Ad role to the configured service principal | |
try | |
{ | |
New-AzureADServiceAppRoleAssignment -Id $role_id -ResourceId $server_service_principal_id -ObjectId $client_service_principal_id -PrincipalId $client_service_principal_id | |
} | |
catch | |
{ | |
if( $_.Exception.Message -like '*Permission being assigned already exists on the object*') | |
{ | |
Write-Host Permission already exists | |
} | |
else | |
{ | |
Write-Error $_.Exception.Message | |
} | |
} | |
} | |
"user" | |
{ | |
Write-Host Getting the configured client user | |
$user = Get-AzureADUser -searchstring $role_assignment.client_principal_name | |
if (!$user) { throw "Cannot find configured client users with name '" + $role_assignment.client_principal_name + "'" } | |
$user_id = $user.ObjectId | |
Write-Host "-User Id:" $user_id -ForegroundColor Gray | |
Write-Host Assigning the Azure Ad role to the configured user | |
try | |
{ | |
New-AzureADUserAppRoleAssignment -Id $role_id -ResourceId $server_service_principal_id -ObjectId $user_id -PrincipalId $user_id | |
} | |
catch | |
{ | |
if( $_.Exception.Message -like '*Permission being assigned already exists on the object*') | |
{ | |
Write-Host Permission already exists | |
} | |
else | |
{ | |
Write-Error $_.Exception.Message | |
} | |
} | |
} | |
} | |
Write-Host Applying role assigment... done -ForegroundColor Green | |
} |
I hope that this script helps you to accelerate your security automation.
Cheers
Toon