0
votes

Short version
I want to avoid username/passwords/secrets/key valults/etc. by using Managed Identity, in a PowerShell script, running on Azure Functions.
It seems to fail on:

Import-Module Microsoft.Graph.Authentication
Connect-MgGraph -Scopes 'Reports.Read.All', 'Group.ReadWrite.All' 

Background
I have followed Access Microsoft Graph from a secured app as the app, and the setup seems to be fine.
I enable System Identity on the Azure Function and give permission to Microsoft Graph, when I check Enterprise App, permissions seems fine. And I find the managed identity in GraphAggregatorService (00000003-0000-0000-c000-000000000000).

When I debug from VScode, with my identity, the script works perfectly.

When Azure Functions runs the code, the following happens:

WARNING: Interactive authentication is not supported in this session,
falling back to DeviceCode. Future versions will not automatically fallback to DeviceCode.

Then

ERROR: Could not find file 'C:\home\site\wwwroot\.graph'. 
Exception : Type : System.IO.FileNotFoundException 
Message : Could not find file 'C:\home\site\wwwroot\.graph'. 
FileName : C:\home\site\wwwroot\.graph TargetSite : 
Name : MoveNext DeclaringType : Microsoft.Graph.PowerShell.Authentication.Cmdlets.ConnectMgGraph+<ProcessRecordAsync>d__52, Microsoft.Graph.Authentication, Version=1.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 
...<cut>
Line : Connect-MgGraph -Scopes 'Reports.Read.All', 'Group.ReadWrite.All' PositionMessage : At C:\home\site\wwwroot\TestPowerShellTimer\run.ps1:15 char:1 + Connect-MgGraph -Scopes 'Reports.Read.All', 'Group.ReadWrite.All' 
...<cut>

Code

# Input bindings are passed in via param block.
param($Timer)

Import-Module Microsoft.Graph.Authentication
Connect-MgGraph -Scopes 'Reports.Read.All', 'Group.ReadWrite.All' 

$reportJson = Invoke-GraphRequest -Uri 'https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails?$top=5000' -Method GET

Why
I could have used Connect-MsOnline (doing the same with msonline), but the code uses uses significantly longer time to complete.
What
I am looping through our users to see if they have set up MFA, and add them to a security group. No, there is no way to do this with azure ad dynamic groups afaik. But if someone has suggestions to other ways of achieving this, feel free to mention it...

My thoughts
Obviously, I am missing something or totally misunderstood something.
The part of the errormsg "C:\home\site\wwwroot.graph", I cannot figure out. Google is not my friend at this point.

I guess I have to, somehow, instruct AzF to use the managed identity:

Import-Module Microsoft.Graph.Authentication
Connect-MgGraph -Scopes -TenantId -ClientId 

but I cannot figure out how. I realize that I have ended up with guessing, and then it is time to ask for help.

How to use managed identity in Azure Functions with the Microsoft.Graph.Authentication module?

2

2 Answers

1
votes

Since you are using a system identity, which is already connected to Azure, you can generate an access token and pass it down to Connect-MGGraph -AccessToken

Here's a function I made about a year or two ago that serve that exact purpose. (I needed any authenticated users to be able to gain a token to any azure endpoints based on their currently connected identity without having any other form of credential validation)

function Get-AzToken {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [String]
        $ResourceUri,
        [Switch]$AsHeader
    ) 
    $Context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
    $Token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $ResourceUri).AccessToken
    if ($AsHeader) {
        return @{Headers = @{Authorization = "Bearer $Token" } }
    }
    return $Token
    
}

$Token = Get-AzToken -ResourceUri 'https://graph.microsoft.com/'
Connect-MgGraph -AccessToken $Token

Note that for this to work, in your Azure function profile.ps1, you should have the Connect-AzAccount -Identity line uncommented.

Profile.ps1 snippet


if ($env:MSI_SECRET) {
    Disable-AzContextAutosave -Scope Process | Out-Null
    Connect-AzAccount -Identity 
}

This method will work to obtain token for any of the Azure endpoints, including your own functions, should you enable the Azure AD authentication.

Bonus

Here is how you would use it to call your Azure function if the Azure function is protected by Azure AD authentication.

# This work assuming you connecteded through Connect-AzAccount at some point.
# ResourceUri is the ClientID of your application.
$Headers = Get-AzToken -ResourceUri '3953d051-c61f-43c5-8848-487a921aae31' -AsHeader
$base = 'https://YourFunctionUrl.azurewebsites.net/api'
Invoke-RestMethod -Method Get -Uri "$base/MyApiEndpoint" @Headers 
0
votes

Here is a quicker way to get MS Graph token and Azure AD Graph token. It's 4 lines from below. I provide more to cover all use cases.

# use this line for system managed identity
Connect-AzAccount -Identity
# use this line for user managed identity, specify its AppID as AccountId
Connect-AzAccount -Identity -AccountId <ClientID>

    $context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
    $graphToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, "https://graph.microsoft.com").AccessToken
    $aadToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, "https://graph.windows.net").AccessToken
    
    Write-Output "Hi I'm $($context.Account.Id)"
    
    # Connect to AAD to use Azure AD Graph
    Connect-AzureAD -AadAccessToken $aadToken -AccountId $context.Account.Id -TenantId $context.tenant.id

    # To use MS Graph use the below line
    Connect-MgGraph -AccessToken $graphToken

Note that Azure AD Graph is retiring on 30 June 2022 and is not recommended anymore. Accordingly you should not use AzureAD commandlets which are based on this SDK.