Menu Close

Automate Azure AD Application with PowerShell

In this post I’ll go some of the different steps you need to consider when creating an AzureAD Application to use with for example GRAPH API, what permissions to grant, and how to grant them for your application in the tenant.

The app that you create here, you can in turn use for many different requests, depending on what permissions you have.
On the Graph documentation page, the different permissions are listed for each “Endpoint” that you wish to query.

For example, the Users endpoint (https://graph.microsoft.com/v1.0/users) information can be found here: user resource type – Microsoft Graph v1.0 | Microsoft Docs

The application that we will create here will have the “user.read.all” permission.

First off, we connect to our tenant using AzureAD.

When logged in to AzureAD we can now run New-AzureADApplication to create a new application for our use.
You have to specify an identifierUri(must use a verified domain in the tenant) and a DisplayName for the application as a minimum for now.

$application = New-AzureADApplication -DisplayName "DemoApp_UserRead" -IdentifierUris "https://MSDx504134.onmicrosoft.com/DemoApp_UserRead"

We see here that our application is available and registered in AzureAD
After creating the application, we need to create a ServicePrincipal and connect this to the application created

$applicationSP = New-AzureADServicePrincipal -appid $application.appId

We see here that our ServicePrincial has been created.
This application and serviceprincipal is now just an empty shell, and we need to assign permissions and a credential to the application in order to use it to collect the information we are lookin for. (We could have done this all in the first step, but then again, it would not be a step by step guide 😉 )

If we check the application newly created, we can see that there are no permissions granted yet.

To add the permissions we need to set these permissions on the application.
This is done by creating an object for “Microsoft.Open.AzureAD.Model.ResourceAccess”

# create object of permissions to grant
$resourceAccess = New-Object -TypeName Microsoft.Open.AzureAD.Model.ResourceAccess

This object will be used to add the permissions to the application.
We need to find the ID of the permission we are going to use here, that is located in the Graph API.
To do this we look for the objectId of our Graph API instance in our tenant (ObjectId is different for all tenants, AppId is the same on all tenants for these Microsoft API’s)

$AllSPN = Get-AzureadServicePrincipal -All $true
# This will show all SPN's with Graph in the name, and here you will find the ID we provided as AppId
$allSPN | where-object {$_.DisplayName -like "*Graph*"} 
# "00000003-0000-0000-c000-000000000000" is the same AppId in all tenants.
$graphApi = $allSPN | where-object {$_.AppId -like "00000003-0000-0000-c000-000000000000"}

To find the permission for our task, we can check the $graphApi.approles to find the ID.

# create object of permissions to grant
# First we create an object of type "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"
# $application.RequiredResourceAccess.psobject will show you what object is required.
$requiredAccess = New-Object -TypeName Microsoft.Open.AzureAD.Model.RequiredResourceAccess

Now that we have the $requiredAccess object, we need to populate the values with our permissions.

# After creating this, we create a list (you can use the same method above on $requiredAccess to see what kind of object it takes.)
$requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]
$requiredAccess.ResourceAppId = $graphApi.AppId

# Find the role we are after:
$appRoleObject = $graphApi.AppRoles | where-object {$_.value -like "user.read.all"}

# To populate the $requiredAccess.ResourceAccess we use the following object.
$resourceAccess = New-Object -TypeName Microsoft.Open.AzureAD.Model.ResourceAccess
$resourceAccess.Id = $appRoleObject.Id
$resourceAccess.Type = "Role" # valid values here depending on your permissions is Role and Scope.
$requiredAccess.ResourceAccess = $resourceAccess

Set-AzureADApplication -ObjectId $application.ObjectId -RequiredResourceAccess $requiredAccess

Now that we have set the permission on our app, we can see in the AzureAD admin center that the permissions are added, but have not been “Granted” in the tenant, and thus, we can still not use the app for our queries.

In order for us to use this application, we will also have to add the same permissions that we added through the RequiredResourceAccess on to the ServicePrincipal connected to the application.
This is done through the New-AzureAdServiceAppRoleAssignment.

New-AzureADServiceAppRoleAssignment -ObjectId $applicationSP.ObjectId -Id $appRoleObject.Id -PrincipalId $applicationSP.ObjectId -ResourceId $graphApi.ObjectId

Refresh your Azure AD Admin portal, and you should now have a granted application.

Last step before we can use the application is to assign a credential that we use to aquire a token from our Tenant.

# Create New Azure AD Application Password Credential to be able to aquire token and access the app using GRAPH API
$NewPassword = New-AzureADApplicationPasswordCredential -ObjectId $application.ObjectId -StartDate (get-date) -EndDate (get-date).AddMonths(6) -CustomKeyIdentifier "This is a test"

Your password should now be in the $newPassword variable for you to use.
I have a function that I use to get the AccessToken, the same method is used for different services towards Microsoft.

Function Get-RefreshToken {
    [cmdletbinding()]
    Param($appid,$appSecret,$tenantId,[Validateset("outlook.office.com/.default","manage.office.com","graph.microsoft.com","management.core.windows.net")]$api = "graph.microsoft.com")
 
    $Authority = "https://login.microsoftonline.com/$tenantId"
  
      $tokenEndpointUri = "$authority/oauth2/token"
      $Contentbody = @{"grant_type"="client_credentials";
              "Client_Id"=$AppId;
              "Client_Secret"=$AppSecret;
              "Resource"="https://$api"}
 
              $response = Invoke-RestMethod -Uri $tokenEndpointUri -Body $Contentbody -Method Post -UseBasicParsing
  
      Write-output $response.access_Token
  }
  

 $access_token =  Get-RefreshToken -appid $application.appId -appSecret $NewPassword.Value -tenantid (get-azureadtenantdetail).objectId

# Query the user endpoint in Microsoft Graph
$endpoint = "https://graph.microsoft.com/beta/users"
$graphData = Invoke-RestMethod -Method Get -Uri $endpoint -Headers @{"Authorization"="Bearer $access_Token"}  -DisableKeepAlive -UseBasicParsing
$graphData.value[0]

If an token was successfull we should now be able to list out our users from AzureAD:

Now that the basic around creating an application tailored for your needs are in place, you can create similar reports like this one, on your environment 😉 Export customers’ Microsoft Secure Scores to CSV and HTML reports – GCITS

All the code from the snippets above can be copied from this code block.


$UserName = "admin@MSDx504134.onmicrosoft.com"
Connect-AzureAd -AccountId $UserName

$application = New-AzureADApplication -DisplayName "DemoApp_UserRead_V3" -IdentifierUris "https://MSDx504134.onmicrosoft.com/DemoApp_UserRead_v3"
$applicationSP = New-AzureADServicePrincipal -appid $application.appId

# (Before we can assign the User Read All Role, we need to locate its ID (We know this is located in the GRAPH API (This is known with the appID: 00000003-0000-0000-c000-000000000000)))
$AllSPN = Get-AzureadServicePrincipal -All $true
$allSPN | where-object {$_.DisplayName -like "*Graph*"} # This will show all SPN's with Graph in the name, and here you will find the ID we provided as AppId

$graphApi = $allSPN | where-object {$_.AppId -like "00000003-0000-0000-c000-000000000000"}

# create object of permissions to grant
# First we create an object of type "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"
# $application.RequiredResourceAccess.psobject will show you what object is required.
$requiredAccess = New-Object -TypeName Microsoft.Open.AzureAD.Model.RequiredResourceAccess

# After creating this, we create a list (you can use the same method above on $requiredAccess to see what kind of object it takes.)
$requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]
$requiredAccess.ResourceAppId = $graphApi.AppId

# Find the role we are after:
$appRoleObject = $graphApi.AppRoles | where-object {$_.value -like "user.read.all"}

# To populate the $requiredAccess.ResourceAccess we use the following object.
$resourceAccess = New-Object -TypeName Microsoft.Open.AzureAD.Model.ResourceAccess
$resourceAccess.Id = $appRoleObject.Id
$resourceAccess.Type = "Role" # valid values here depending on your permissions is Role and Scope.
$requiredAccess.ResourceAccess = $resourceAccess

Set-AzureADApplication -ObjectId $application.ObjectId -RequiredResourceAccess $requiredAccess

# Assign User Read Role to the application
New-AzureADServiceAppRoleAssignment -ObjectId $applicationSP.ObjectId -Id $appRoleObject.Id -PrincipalId $applicationSP.ObjectId -ResourceId $graphApi.ObjectId

# Create New Azure AD Application Password Credential to be able to aquire token and access the app using GRAPH API
$NewPassword = New-AzureADApplicationPasswordCredential -ObjectId $application.ObjectId -StartDate (get-date) -EndDate (get-date).AddMonths(6) -CustomKeyIdentifier "This is a test"

Write-Output "################ APP INFO ################"
Write-Output "AppId: $($application.AppId)"
Write-Output "ClientSecret: $($NewPassword.Value)"
Write-Output "TenantId: $((get-azureadtenantdetail).objectId)."
Write-Output "################ APP INFO ################"

And to run a query towards your newly created application: (Maybe select a different name or prefix the function with your own initials as this name may crash with installed modules)

Function Get-RefreshToken {
    [cmdletbinding()]
    Param($appid,$appSecret,$tenantId,[Validateset("outlook.office.com/.default","manage.office.com","graph.microsoft.com","management.core.windows.net")]$api = "graph.microsoft.com")
 
    $Authority = "https://login.microsoftonline.com/$tenantId"
  
      $tokenEndpointUri = "$authority/oauth2/token"
      $Contentbody = @{"grant_type"="client_credentials";
              "Client_Id"=$AppId;
              "Client_Secret"=$AppSecret;
              "Resource"="https://$api"}
 
              $response = Invoke-RestMethod -Uri $tokenEndpointUri -Body $Contentbody -Method Post -UseBasicParsing
  
      Write-output $response.access_Token
  }
  

 $access_token =  Get-RefreshToken -appid $application.appId -appSecret $NewPassword.Value -tenantid (get-azureadtenantdetail).objectId


$endpoint = "https://graph.microsoft.com/beta/users"
$graphData = Invoke-RestMethod -Method Get -Uri $endpoint -Headers @{"Authorization"="Bearer $access_Token"} -DisableKeepAlive -UseBasicParsing
$graphData.value[0]