In this article, we will explain how to create a new Azure AD application, configure API permissions, create an Enterprise Application (Service Principal) for the new app, and provide user and admin consent to the app using the PowerShell script. We will also demonstrate how to acquire an app access token by using the newly created application and access Microsoft Graph API resources.
If you are new to using Azure AD applications, then we would recommend reading this post to know more about how to register Azure AD application and how to configure permissions and provide consent to the app from the Azure AD portal.
Before you start, install the Azure AD V2 PowerShell module and run the following command to connect the module.
Connect-AzureAD
Summary
- Create a new Azure AD Application
- Configure required API Permissions in Azure AD Application
- Create client secret or Application password
- Create new Service Principal or Enterprise Application
- Grant consent (user and admin) to Enterprise Application
- Get access token on behalf of the app
- Get access token on behalf of a user
- Use the access token to call Microsoft Graph
Create a new Azure AD Application using PowerShell
We can use the New-AzureADApplication cmdlet to create a new Azure Active Directory application. Run the following command to create a new app.
$aadApplication = New-AzureADApplication -DisplayName "MTS Demo App" -IdentifierUris "http://mtsdemoapp.contoso.com" -HomePage "http://mtsdemo.contoso.com"
You can view the newly created app in the App registrations blade, under All applications in the Azure portal. Run the following commands to add the current user as Owner of the new app, hence you will also see the app under Owned applications.
$currentUser = (Get-AzureADUser -ObjectId (Get-AzureADCurrentSessionInfo).Account.Id)
Add-AzureADApplicationOwner -ObjectId $aadApplication.ObjectId -RefObjectId $currentUser.ObjectId
Configure required API Permissions in Azure AD Application
In Azure AD Portal, we can select the required app in App registrations and assign the required permissions under the section Manage -> API permissions. First, we need to select the required resource API (Ex: Microsoft Graph) and choose the required permissions (Ex: User.Read.All, Reports.Read.All). The permission can either be Application permissions or Delegated permissions. Here, we are going to execute the same steps with the PowerShell script.
Run the following commands to configure the required Application and Delegated permissions in the newly created Azure AD application.
#Get Service Principal of Microsoft Graph Resource API
$graphSP = Get-AzureADServicePrincipal -All $true | Where-Object {$_.DisplayName -eq "Microsoft Graph"}
#Initialize RequiredResourceAccess for Microsoft Graph Resource API
$requiredGraphAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess
$requiredGraphAccess.ResourceAppId = $graphSP.AppId
$requiredGraphAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]
#Set Application Permissions
$ApplicationPermissions = @('User.Read.All','Reports.Read.All')
#Add app permissions
ForEach ($permission in $ApplicationPermissions) {
$reqPermission = $null
#Get required app permission
$reqPermission = $graphSP.AppRoles | Where-Object {$_.Value -eq $permission}
if($reqPermission)
{
$resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess
$resourceAccess.Type = "Role"
$resourceAccess.Id = $reqPermission.Id
#Add required app permission
$requiredGraphAccess.ResourceAccess.Add($resourceAccess)
}
else
{
Write-Host "App permission $permission not found in the Graph Resource API" -ForegroundColor Red
}
}
#Set Delegated Permissions
$DelegatedPermissions = @('Directory.Read.All', 'Group.ReadWrite.All') #Leave it as empty array if not required
#Add delegated permissions
ForEach ($permission in $DelegatedPermissions) {
$reqPermission = $null
#Get required delegated permission
$reqPermission = $graphSP.Oauth2Permissions | Where-Object {$_.Value -eq $permission}
if($reqPermission)
{
$resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess
$resourceAccess.Type = "Scope"
$resourceAccess.Id = $reqPermission.Id
#Add required delegated permission
$requiredGraphAccess.ResourceAccess.Add($resourceAccess)
}
else
{
Write-Host "Delegated permission $permission not found in the Graph Resource API" -ForegroundColor Red
}
}
#Add required resource accesses
$requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]
$requiredResourcesAccess.Add($requiredGraphAccess)
#Set permissions in existing Azure AD App
$appObjectId=$aadApplication.ObjectId
#$appObjectId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Set-AzureADApplication -ObjectId $appObjectId -RequiredResourceAccess $requiredResourcesAccess
Note: The above script updates the permissions in the existing Azure AD application. You can also use the same script to set permissions while creating a new application.
New-AzureADApplication -DisplayName "Permission Test App" -IdentifierUris "http://ptdemoapp.contoso.com" -RequiredResourceAccess $requiredResourcesAccess
Create client secret or Application password
When you request a token from a client application, a secret key is required to prove the application identity. The client secret key can also be referred to as the application password. You can create multiple keys and specify the validity period for your key. The below command creates a client secret key that will expire in two years. You can also specify a StartDate if you do not want the key activated immediately.
$appObjectId=$aadApplication.ObjectId
#$appObjectId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$appPassword = New-AzureADApplicationPasswordCredential -ObjectId $appObjectId -CustomKeyIdentifier "AppAccessKey" -EndDate (Get-Date).AddYears(2)
$appPassword.Value #Display app secret key
Create new Service Principal or Enterprise Application for Azure AD Application
Once we created an Azure AD application, a service principal object (Enterprise application) is required for the application to access resources that are secured by Azure AD tenant. The security principal defines the access policy and permissions for the application in the Azure AD tenant. This enables core features such as authentication of the user/application during sign-in, and authorization during resource access.
Run the following commands to create a new service principal for the Azure AD application.
#Provide Application (client) Id
$appId=$aadApplication.AppId
#$appId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$servicePrincipal = New-AzureADServicePrincipal -AppId $appId -Tags @("WindowsAzureActiveDirectoryIntegratedApp")
When you register a new application from the App registrations blade in the Azure portal, a service principal object for the application will be automatically created in your tenant and it will be available under Enterprise applications blade in the portal. If you created the app as a multi-tenant app and want to use it in different tenants such as a customer tenant, the service principal object will be created in the customer tenant when the customer provides permission to access resources in their tenant (upon registration or user consent or admin consent). They can also use PowerShell, Azure CLI, and other tools to create this security principal.
The service principal object is the local representation or application instance of a global Azure AD application in a single tenant or directory. The service principal can also be called as Enterprise Application or Managed Application in the local directory. You can refer to this post to know more about service principal: Azure AD Application and Service principal object.
Grant consent (user and admin) to Service Principal/Enterprise Application
As we already explained, the service principal (aka Enterprise application) of the application will control the resource access for the application in the Azure AD tenant. To acquire an access token for the configured permissions, the required consent (admin or user consent) should already have been provided for the required permissions in the service principal object.
Here, we are explaining how to grant consent for Application and Delegated permissions using the PowerShell script. We can also provide admin consent through the Azure AD portal or with admin consent URL, you can refer to this post for more details: Grant tenant-wide admin consent to an application.
$requiredResourcesAccess – In the below scripts we have used the object $requiredResourcesAccess, you can refer to the above section Configure required API Permissions to form this object. If the Azure AD application available in your Tenant, then you can get the configured permissions by running the following commands.
$appObjectId=$aadApplication.ObjectId
#$appObjectId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$requiredResourcesAccess=(Get-AzureADApplication -ObjectId $appObjectId).RequiredResourceAccess
$servicePrincipal – In the below scripts we have used the object $servicePrincipal to get the Object Id of the service principal (Enterprise application). You can run the following commands to get the service principal object of your Azure AD application by providing the application’s AppId (Application client id).
#Provide Application (client) Id
$appId=$aadApplication.AppId
#$appId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$servicePrincipal = Get-AzureADServicePrincipal -All $true | Where-Object {$_.AppId -eq $appId}
Grant Admin consent for Application permissions
Run the following commands to grant admin consent for the application permissions that are configured in the app.
ForEach ($resourceAppAccess in $requiredResourcesAccess)
{
$resourceApp = Get-AzureADServicePrincipal -All $true | Where-Object {$_.AppId -eq $resourceAppAccess.ResourceAppId}
ForEach ($permission in $resourceAppAccess.ResourceAccess)
{
if ($permission.Type -eq "Role")
{
New-AzureADServiceAppRoleAssignment -ObjectId $servicePrincipal.ObjectId -PrincipalId $servicePrincipal.ObjectId -ResourceId $resourceApp.ObjectId -Id $permission.Id
}
}
}
Grant User or Admin consent for Delegated permissions
We are going to use Microsoft Graph API to grant consent for the delegated permissions, so we need to get an access token with the required permissions to create or update a delegated permission grant (oAuth2PermissionGrant).
$accessToken – Run the following commands to retrieve access token by using the AzureAD Powershell app which provides a token with the required privilege.
# Set ADAL (Microsoft.IdentityModel.Clients.ActiveDirectory.dll) assembly path from Azure AD module location
$AADModule = Import-Module -Name AzureAD -ErrorAction Stop -PassThru
$adalPath = Join-Path $AADModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalformPath = Join-Path $AADModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
[System.Reflection.Assembly]::LoadFrom($adalPath) | Out-Null
[System.Reflection.Assembly]::LoadFrom($adalformPath) | Out-Null
# Azure AD PowerShell client id.
$ClientId = "1950a258-227b-4e31-a9cf-717495945fc2"
$RedirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceURI = "https://graph.microsoft.com"
$authority = "https://login.microsoftonline.com/common"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
# Get token by prompting login window.
$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Always"
$authResult = $authContext.AcquireTokenAsync($resourceURI, $ClientID, $RedirectUri, $platformParameters)
$accessToken = $authResult.Result.AccessToken
Run the following commands to grant consent for all the users or required users. By default, the script gives consent for all the users (admin consent), you can set the value for $GrantConsnetForAllUsers as $false and provide the required user id to give consent only for the required user (user consent).
#$requiredResourcesAccess - Refer Configure required API permissions section to form this detail or get it from Get-AzureADApplication for your app
#$servicePrincipal - The servicePrincipal (Enterprise application) object of your Azure AD application
#$accessToken - Require graph access token with required permissions to update oauth2PermissionGrants
$GrantConsnetForAllUsers=$true #Set $true to give consent for all users and set $false to give consent for individual user
if ($GrantConsnetForAllUsers) {
#Grant consent for all users
$consentType = "AllPrincipals"
$principalId = $null
} else {
#Grant consent for the required user alone
$consentType = "Principal"
#Get or provide object id for the required Azure AD user
$principalId = (Get-AzureADUser -SearchString "[email protected]").ObjectId
#$principalId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
ForEach ($resourceAppAccess in $requiredResourcesAccess)
{
$delegatedPermissions = @()
#$resourceApp - get servicePrincipal of Resource API App(ex: Microsoft Graph, Office 365 SharePoint Online)
$resourceApp = Get-AzureADServicePrincipal -All $true | Where-Object {$_.AppId -eq $resourceAppAccess.ResourceAppId}
ForEach ($permission in $resourceAppAccess.ResourceAccess)
{
if ($permission.Type -eq "Scope")
{
$permissionObj = $resourceApp.OAuth2Permissions | Where-Object {$_.Id -contains $permission.Id}
$delegatedPermissions += $permissionObj.Value
}
}
if($delegatedPermissions)
{
#Get existing grant entry
$existingGrant = Get-AzureADOAuth2PermissionGrant -All $true | Where { $_.ClientId -eq $servicePrincipal.ObjectId -and $_.ResourceId -eq $resourceApp.ObjectId -and $_.PrincipalId -eq $principalId}
if(!$existingGrant){
#Create new grant entry
$postContent = @{
clientId = $servicePrincipal.ObjectId
consentType = $consentType
principalId = $principalId
resourceId = $resourceApp.ObjectId
scope = $delegatedPermissions -Join " "
}
$requestBody = $postContent | ConvertTo-Json
Write-Host "Grant consent for $delegatedPermissions ($($resourceApp.DisplayName))" -ForegroundColor Green
$headers = @{Authorization = "Bearer $accessToken"}
$response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" -Body $requestBody -Method POST -Headers $headers -ContentType "application/json"
} else {
#Update existing grant entry
$delegatedPermissions+=$existingGrant.Scope -Split " "
$delegatedPermissions = $delegatedPermissions | Select -Unique
$patchContent = @{
scope = $delegatedPermissions -Join " "
}
$requestBody = $patchContent | ConvertTo-Json
Write-Host "Update consent for $delegatedPermissions ($($resourceApp.DisplayName))" -ForegroundColor Green
$headers = @{Authorization = "Bearer $accessToken"}
$response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/oauth2PermissionGrants/$($existingGrant.ObjectId)" -Body $requestBody -Method PATCH -Headers $headers -ContentType "application/json"
}
}
}
Get access token on behalf of the app – Application permissions
Once you have successfully created and configured an Azure AD application, run the following commands to get a resource access token with configured Application permissions.
#Get or provide your Office 365 Tenant Id or Tenant Domain Name
$tenantId = (Get-AzureADTenantDetail).ObjectId
#$tenantId = "contoso.onmicrosoft.com"
#Provide Application (client) Id of your app
$appClientId=$aadApplication.AppId
#$appClientId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
#Provide Application client secret key
$clientSecret = $appPassword.Value
#$clientSecret ="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$requestBody = @{client_id=$appClientId;client_secret=$clientSecret;grant_type="client_credentials";scope="https://graph.microsoft.com/.default";}
$oauthResponse = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token -Body $requestBody
$accessToken = $oauthResponse.access_token
Get access token on behalf of a user – Delegated permissions
For Delegated permissions token, we would recommend you to use a sign-in library such as MSAL (or ADAL) and prompt the user to log-in. For unattended sign-in access (without login-prompt window), we can use OAuth 2.0 Resource Owner Password Credentials flow.
Run the following commands to get an access token on behalf of a user by sending the required username and plain password.
#Get or provide your Office 365 Tenant Id or Tenant Domain Name
$tenantId = (Get-AzureADTenantDetail).ObjectId
#$tenantId = "contoso.onmicrosoft.com"
#Provide Application (client) Id of your app
$appClientId=$aadApplication.AppId
#$appClientId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
#Provide Application client secret key
$clientSecret = $appPassword.Value
#$clientSecret ="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$username= "[email protected]"
$password= "user_password"
$scope="Directory.Read.All openid profile offline_access"
$requestBody = @{client_id=$appClientId;client_secret=$clientSecret;grant_type="password";username=$username;password=$password;scope=$scope;}
$oauthResponse = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token -Body $requestBody
$accessToken = $oauthResponse.access_token
Use the access token to call Microsoft Graph
The below command calls the users endpoint in the Microsoft Graph API resource and retrieves Azure AD user details.
$apiUrl = "https://graph.microsoft.com/v1.0/users"
$users = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl -Method Get
Hello Morgan,
Thank you very much for the code, this is exactly what I was looking for!
I keep running into an issue where when trying to grant consent I get the following error when PowerShell attempts to Invoke the oauth2PermissionGrants API
{“error”:{“code”:”Request_BadRequest”,”message”:”Specified clientId was not found.”}}
I’m quite positive the clientId is correct and it does indeed exist within my Tenant. Any thoughts on what could be the issue? This is a real head-scratcher for me.
Thanks,
Matthew
Ensure that the $servicePrincipal is not a null object and run the below command to return the Object Id of the Enterprise Application (Service Principal object of your Azure AD app).
$servicePrincipal.ObjectId
If $servicePrincipal is a null object, then run the below command to get the Enterprise App (Azure AD Service Principal) object.
#Provide Application (client) ID of your App Registration (Azure AD Application)
$appId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
#$appId=$aadApplication.AppId
#Get Enterpise application (Service Principal object) for your Azure AD app
$servicePrincipal = Get-AzureADServicePrincipal -All $true | Where-Object {$_.AppId -eq $appId}
Actually here we are using the Object ID of the Enterprise Application (Service Principal object of your Azure AD app) as ClientID. So, go to Azure AD portal > Azure Active Directory > Enterprise applications > Open (Double-click) the required app, now cross-check the Object ID value with the value returned in the below command.
$servicePrincipal.ObjectId
Ah, I see now. The difference is I am using “New-AzureADApplication” instead of “New-AzureADServicePrincipal”, which creates the App Registration, but doesn’t make it available yet under “Enterprise Applications”. Would you say that a simple fix is just to use “New-AzureADServicePrincipal” instead, or perhaps can you suggest a way to adapt the above with “New-AzureADApplication”?
My purpose here is to create some (2) App Registrations for a Web App with a middle-tier Web API, and then automatically have them granted Admin Consent during the onboarding (PowerShell deployment) process.
Very much appreciated!
-Matthew
Got it all figured out, thanks so much for your help. Wonderful post!
-Matthew
Hellow Morgan,
I have a question about the accessToken and is why we need to login again to get the accesstoken in this part of the code?
$platformParameters = New-Object “Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters” -ArgumentList “Always”
$authResult = $authContext.AcquireTokenAsync($resourceURI, $ClientID, $RedirectUri, $platformParameters)
$accessToken = $authResult.Result.AccessToken
is not better get the accesstoken when we login using the command Connect-AzureAD?
Also, you know if its possible to add the platform Mobile and Desktop application for the Reply URLs that the application needs?
thanks for your time and for the code it has been very useful!
Sorry, I don’t have any idea to get access token from the Connect-AzureAD context. Can you guide me if you know any method to get the token from existing Azure AD context?
Hello, Thanks for the post
I am getting error : New-AzureADServiceAppRoleAssignment : Error occurred while executing NewServicePrincipalAppRoleAssignment
Code: Request_BadRequest
Message: Permission being assigned already exists on the object
Can you help me in this.