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
Granting Admin Consent
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 { $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 |
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!
You're most welcome.