Azure AD – Consenting to an application with script (using Graph)

Some times I need to consent to an application using a script, rather than having an administrator consenting to an application in the Azure Portal. This is perfectly possible, but the documentation does not really scream “here is how to do it” and “I am tagged with the correct meta data so you can actually find me”.

Let’s say you use the following PowerShell to create an application:

$requiredGrants = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]::new(
    "00000003-0000-0000-c000-000000000000", # Microsoft Graph
    @(
        #[Microsoft.Open.AzureAD.Model.ResourceAccess]::new("e1fe6dd8-ba31-4d61-89e7-88639da4683d","Scope") # OpenID
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("0e263e50-5827-48a4-b97c-d940288653c7","Scope") # Directory.AccessAsUser.All
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("a154be20-db9c-4678-8ab7-66f6cc099a59","Scope") # openid
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("63dd7cd9-b489-4adf-a28c-ac38b9a0f962","Scope") # User.Invite.All
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("204e0828-b5ca-4ad8-b9f3-f32a958e7cc4","Scope") # User.ReadWrite.All
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("d01b97e9-cbc0-49fe-810a-750afd5527a3","Scope") # RoleManagement.ReadWrite.Directory
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("3c3c74f5-cdaa-4a97-b7e0-4e788bfcfb37","Scope") # PrivilegedAccess.ReadWrite.AzureAD
    )
)

New-AzureADApplication -AvailableToOtherTenants:$false -DisplayName "Test app" -RequiredResourceAccess $requiredGrants

After this, the Azure Portal will contain a new app, but under “API Permissions”, you’ll find the following:

Now, you can click “Grant admin consent”, but that is the manual way that we want to avoid. Over to the bad news; There is no way to do this using the current version of the Azure AD powershell module… However, I have used this blog post as inspiration for creating the Get-AzureADAccessTokenFromActiveSession cmdlet, that returns the access token for the Azure AD PowerShell Module, that we can use for stuff!

function Get-AzureADAccessTokenFromActiveSession
{
    [CmdletBinding()]
    Param()

    Process
    {
        # Trigger dummy request to make sure we have an access token to graph.microsoft.com
        Get-AzureADMSGroup -Top:1 | Out-Null

        $AadModule = Get-Command Connect-AzureAD | Select-Object -ExpandProperty Source | Get-Module
        $adal = Join-Path -Path $AadModule.ModuleBase -ChildPath 'Microsoft.IdentityModel.Clients.ActiveDirectory.dll'
        $adalforms = Join-Path -Path $AadModule.ModuleBase -ChildPath 'Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll'    
        [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
        [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null
   
        $currentSession = Get-AzureADCurrentSessionInfo
        $authority = "https://login.microsoftonline.com/$($currentSession.TenantId)"

        $authContext = New-Object -TypeName 'Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext' -ArgumentList $authority
        $authContext.TokenCache.ReadItems() | ? Resource -eq 'https://graph.microsoft.com' | Select-Object -ExpandProperty AccessToken
    }
}

# A little demo:
$AccessToken = Get-AzureADAccessTokenFromActiveSession
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users" -Headers @{Authorization = "Bearer $AccessToken"}

Now that we have an access token from our active session, we can POST to the oauth2PermissionGrants the following way:

function Get-AzureADAccessTokenFromActiveSession
{
    [CmdletBinding()]
    Param()

    Process
    {
        # Trigger dummy request to make sure we have an access token to graph.microsoft.com
        Get-AzureADMSGroup -Top:1 | Out-Null

        $AadModule = Get-Command Connect-AzureAD | Select-Object -ExpandProperty Source | Get-Module
        $adal = Join-Path -Path $AadModule.ModuleBase -ChildPath 'Microsoft.IdentityModel.Clients.ActiveDirectory.dll'
        $adalforms = Join-Path -Path $AadModule.ModuleBase -ChildPath 'Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll'    
        [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
        [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null
   
        $currentSession = Get-AzureADCurrentSessionInfo
        $authority = "https://login.microsoftonline.com/$($currentSession.TenantId)"

        $authContext = New-Object -TypeName 'Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext' -ArgumentList $authority
        $authContext.TokenCache.ReadItems() | ? Resource -eq 'https://graph.microsoft.com' | Select-Object -ExpandProperty AccessToken
    }
}

# Build a list of required resource accesses to Graph
$requiredGrants = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]::new(
    "00000003-0000-0000-c000-000000000000", # Microsoft Graph
    @(
        #[Microsoft.Open.AzureAD.Model.ResourceAccess]::new("e1fe6dd8-ba31-4d61-89e7-88639da4683d","Scope") # OpenID
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("0e263e50-5827-48a4-b97c-d940288653c7","Scope") # Directory.AccessAsUser.All
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("a154be20-db9c-4678-8ab7-66f6cc099a59","Scope") # openid
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("63dd7cd9-b489-4adf-a28c-ac38b9a0f962","Scope") # User.Invite.All
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("204e0828-b5ca-4ad8-b9f3-f32a958e7cc4","Scope") # User.ReadWrite.All
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("d01b97e9-cbc0-49fe-810a-750afd5527a3","Scope") # RoleManagement.ReadWrite.Directory
        [Microsoft.Open.AzureAD.Model.ResourceAccess]::new("3c3c74f5-cdaa-4a97-b7e0-4e788bfcfb37","Scope") # PrivilegedAccess.ReadWrite.AzureAD
    )
)

# Create applicaiton and service principal
$Application = New-AzureADApplication -AvailableToOtherTenants:$false -DisplayName "Test app" -RequiredResourceAccess $requiredGrants
$ServicePrincipal = New-AzureADServicePrincipal -AppId $Application.AppId

# Get access token from active session
$AccessToken = Get-AzureADAccessTokenFromActiveSession

# Get the service principal for the Microsoft Graph
$MSGraphServicePrincipal = (Get-AzureADServicePrincipal -All:$true | ? AppId -eq $requiredGrants[0].ResourceAppId)

# Build a scope string such as: "Directory.AccessAsUser.All User.ReadWrite.All User.Read.All User.Invite.All PrivilegedAccess.ReadWrite.AzureAD"
$Scope = ($MSGraphServicePrincipal.Oauth2Permissions | ? id -in $requiredGrants[0].ResourceAccess.Id).Value -join " "

# Post the grant
$body = @{
    clientId = $ServicePrincipal.ObjectId
    consentType = "AllPrincipals"
    principalId = $null # Get-AzureADUser -ObjectId (Get-AzureADCurrentSessionInfo).Account | Select -ExpandProperty ObjectId
    resourceId = $MSGraphServicePrincipal.ObjectID
    scope = $Scope
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" -Headers @{Authorization = "Bearer $AccessToken"} -Body $body -Method Post -ContentType "application/json"

Of course you can also get the access token that you require by other means, this was just handy 🙂

Anyway, now we suddenly have an auto granted application!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s