Creating an Azure AD Application and Granting Application Admin Consent with Powershell

by | Jan 28, 2022 | IT Tips | 2 comments

https://blog.nico-schiering.de/granting-azure-ad-admin-consent-programmatically/

https://samcogan.com/provide-admin-consent-fora-azure-ad-applications-programmatically/

Standing on the shoulders of giants here is a script that creates an Azure AD Application grants it access to the MS Graph API and the Mail.Send role ability.

It could be modified to grant access to different resources and permissions

There are two functions that can do an admin consent for Application Permissions

Grant-AdminConsentPermissionsToApp

Grant-OAuth2PermissionsToApp

The first function specifically approves the Application to use Microsoft Graph Mail.Send role with this function you would need to loop through each role to approve each one. The Grant-OAuth2PermissionsToApp function will approve multiple roles at once. I am using auth tokens taken from the context or Get-AzAccessToken check the code for specifics.

I logged in as a Global Administrator to run this script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# run as admin
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {  
    $arguments = "& '" + $myinvocation.mycommand.definition + "'"
    Start-Process powershell -Verb runAs -ArgumentList $arguments
    Break
}
 
function logThis ($text, $logFile) {
    "[$(Get-Date -format 'yyyy-MM-dd HH:mm:ss')] $($text)" | out-file $logFile -append
}
 
 
# place to save output
$tmpPath = "C:\Temp\Test"
$appName = "My Test App 6";
 
#Setup Logging
$logFile = "$($tmpPath)\AppInstallationLog.txt"
if (!(Test-Path "$($tmpPath)")) { New-Item -ItemType directory -path "$($tmpPath)" }
 
logThis "[INFO] ******************* SCRIPT START *******************" $logFile
 
$error.clear()
 
#Define Functions:
# working
 
<#
 This function can approve an individual role
 So in this case it is approving the application to access the Microsoft Graph API and specifically Mail.Send
 This is NOT doing delegated approval but Application approval (you need this so that a program can send email as any user)
#>
Function Grant-AdminConsentPermissionsToApp {
     
    $token = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com/").Token
    
    $msGraphObjectId = (Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq "Microsoft Graph" }).ObjectId
     
    $apiUrl = "https://graph.microsoft.com/v1.0/servicePrincipals/$($msGraphObjectId)/appRoleAssignments"
 
    # The Object ID of the service principal
    $principalId = (Get-AzureADServicePrincipal -All $true | Where-Object { $_.AppId -eq $newAppId }).ObjectId
     
    # Mail.Send Id
    $appRoleId = ((Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq "Microsoft Graph" }).AppRoles |
        Where-Object { $_.Value -eq "Mail.Send" }).Id
 
    $body = @{
        principalId = $principalId
        resourceId  = $msGraphObjectId
        appRoleId   = $appRoleId
    }
 
    Invoke-RestMethod -Uri $apiUrl -Headers @{Authorization = "Bearer $($token)" }  -Method POST -Body $($body | convertto-json) -ContentType "application/json"
}
 
Function Grant-OAuth2PermissionsToApp {
    $context = Get-AzContext
         
    if ($null -eq $context) {
        $null = Connect-AZAccount -EA stop
        $context = Get-AzContext
    }
    
    #  get an access token to access resource https://main.iam.ad.ext.azure.com / 74658136-14ec-4630-ad9b-26e160ff0fc6
    $token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, "74658136-14ec-4630-ad9b-26e160ff0fc6")
 
    $header = @{
        'Authorization'          = 'Bearer ' + $token.AccessToken
        'X-Requested-With'       = 'XMLHttpRequest'
        'x-ms-client-request-id' = [guid]::NewGuid()
        'x-ms-correlation-id'    = [guid]::NewGuid()
    }
     
    $url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$newAppId/Consent?onBehalfOfAll=true"
 
    Invoke-RestMethod -Uri $url -Headers $header -Method POST -ErrorAction Stop
}
 
 
if (Get-Module -ListAvailable -Name AzureAD)
{}
else {
    Install-Module -Name AzureAD
    logThis "[INFO] Installing Module AzureAD" $logFile
}
 
Write-Host                          "Please Log in with an Azure AD Admin Account"
Connect-AzureAD
 
Write-Host                          "Creating Application with Mail.Send Permissions within your Azure AD Tenant...Please wait..."
 
$tenant = Get-AzureADTenantDetail
 
# first get the MS Graph service principal
$msGraphPrincipal = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq "Microsoft Graph" }
$requiredResourceAccess = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"
$requiredResourceAccess.ResourceAppId = $msGraphPrincipal.AppId
 
$msgraphRole = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList "b633e1c5-b582-4048-a93e-9f11b44c7e96", "Role"
# Duplicate each role here you want to add here
#$msgraphRole2 = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList "GUID of Role look in manifest in the portal to find this", "Role"
 
$requiredResourceAccess.ResourceAccess = $msgraphRole #, $msgraphRole2 pass each role  you want to add on this line as an array
 
$newApp = New-AzureADApplication -DisplayName $appName -RequiredResourceAccess @($requiredResourceAccess)
 
logThis "[INFO] Application Created" $logFile
 
# password with 5 year expiry
$appSecret = New-AzureADApplicationPasswordCredential -ObjectId $newApp.ObjectId -CustomKeyIdentifier "$($appName) Key" -EndDate (get-date).AddYears(5)
 
logThis "[INFO] Secret Key Created" $logFile
 
# needed for granting admin consent
# create a service principal and associate it with the Azure Application
 
$svcPrincipal = New-AzureADServicePrincipal -AppId $newApp.AppId -Tags @("WindowsAzureActiveDirectoryIntegratedApp")
 
logThis "[INFO] Service Principal Created" $logFile
 
$newAppId = $newApp.AppId
$tenantId = $tenant.ObjectId
$appSecretValue = $appSecret.Value
$tenantDomain = ($tenant.VerifiedDomains | ? { $_._Default -eq $true }).Name
 
 
logThis "[INFO] Sleep Started" $logFile
Write-Host "Sleeping 30 seconds"
Start-Sleep -s 30
 
 
 
# this works to do app consent for individual roles but you would need to loop through each role to consent
# Grant-AdminConsentPermissionsToApp
 
logThis "[FUNCTIONCALL] Grant-OAuth2PermissionsToApp" $logFile
# this bulk approves all the roles
Grant-OAuth2PermissionsToApp
 
#Write-Out Access Details to Log File
logThis "Application Created Successfully" $logFile
logThis "Application (client) ID: " + $newAppId $logFile
logThis "Directory (tenant) ID: " + $tenantId $logFile
logThis "Client Key: " + $appSecretValue $logFile
logThis "Tenant Domain: " + $tenantDomain $logFile
 
 
#Error Collection
if ($error.count -gt 0) {
    logThis "[WARN] Error Count: " + $error.count $logFile
    logThis "[WARN] Error Text: " $logFile
    logThis $error $logFile
    logThis "*** End Error Text ***" $logFile
}
else
{ logThis "[INFO] No Errors" $logFile }
 
logThis "[INFO] ******************* SCRIPT END *******************" $logFile
 
Write-Host                          "Application successfully created..."
Write-Host                          " "
Write-Host                          "Application (client) ID:" $newAppId
Write-Host                          "Directory (tenant) ID:" $tenantId
Write-Host                          "Client Key:" $appSecretValue
Write-Host                          "Tenant Domain:" $tenantDomain
Write-Host                          " "
Write-Host                          "Please store the following file safely (i.e. encrypted somewhere):"
Write-Host                          "$($logFile)"
pause

2 Comments

  1. Kristy

    Thank you so much! I don't even want to count the hours I spent trying to get this working via powershell. Really appreciate you working this our and writing it up!

    Reply

Submit a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.