Connecting to Microsoft 365 SharePoint and Graph API from Azure Function App

Let say you need to run some code against Microsoft 365 on a scheduled basis. For this, you can use Azure Function App and timer-triggered Functions. From the code you’d call Microsoft Graph API and/or SharePoint API, so you’d need your Function to get credentials on the fly and use it to call APIs. For this – you’d have a key vault where credentials will be be stored and retrieved by functions when needed. Here we’ll create, configure and secure a scheduled Azure Function and Azure Key Vault.

First, we’ll create a resource group, e.g. “Azure-Func-Secured”.

Next, we’ll create a function app:

We’ll avoid the default “Flex Consumption” service plan (as it is Linux-only and does not support dependencies) and select “Consumption” a hosting option for now:

Runtime stack we’ll be using is PowerShell Core 7.4, but you can choose your own (options are – Python, .Net (C#), Node.js, Java). Let us leave other configuration settings by default (e.g. Enable Public access: On) for now, we’ll fix (secure) it later.

Ok, the function app has been created. Notice that app service plan, application insights and storage account, as well as some other services were created automatically under the same resource group:

Now we can create one or more functions under this function app, also we’d need to create a key vault and a registered app. Let us start with function and VS Code would be our environment of choice.

Let us start Visual Studio Code in a separate folder. Ensure you have “Azure Functions”, “Azure Resources” and PowerShell extensions by Microsoft. You’d sign-in to Azure:

and create a function project:

You’d choose “Timer triggered” function (as we need the function running on schedule), the function name would be MyScheduledFunct01 and we’d leave the default schedule as “0 */5 * * * *” – so our function will be triggered every 5 minutes.

Let us deploy the function right away and see if it works. For this, we can use “deploy” icon under “WORKSPACE” panel:

or “Deploy to function app” option from the function app context pop-up menu under the Azure “RESOURCES” panel:

After successful deployment give it some time, then check function invocations and ensure function is triggered an running:

Next, we’d update “requirements.psd1” to include Azure Key Vault and PnP PowerShell modules as it takes some time for the function app to pull in and install dependencies. Requirements.psd1:

# This file enables modules to be automatically managed by the Functions service.
# See https://aka.ms/functionsmanageddependency for additional information.
#
@{
    # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. Uncomment the next line and replace the MAJOR_VERSION, e.g., 'Az' = '5.*'
    # 'Az' = 'MAJOR_VERSION.*'
    'Az.KeyVault' = '6.*'
    'PnP.PowerShell' = '2.*'
}

And we’d update function itself to monitor if dependencies are installed, than we’d deploy the function again so time would work for us. MyScheduledFunction/run.ps1:

# Input bindings are passed in via param block.
param($Timer)
$currentUTCtime = (Get-Date).ToUniversalTime()
if ($Timer.IsPastDue) {
    Write-Host "PowerShell timer is running late!"
}
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
#############################################################################################
Write-Host "Check modules installed:"
Import-Module PnP.PowerShell 
Import-Module Az.KeyVault
Get-Module PnP.PowerShell
Get-Module Az.KeyVault
Write-Host "Check command available:"
Get-Command -Name Connect-PnPOnline -Module PnP.PowerShell

At first, we might see warning like “The first managed dependency download is in progress, function execution will continue when it’s done. Depending on the content of requirements.psd1, this can take a few minutes. Subsequent function executions will not block and updates will be performed in the background.”. So we’d just wait.

After some time the function will be able to use required modules. Here is the invocation output example:

App Registration
To get unattended access to SharePoint (or Teams or Exchange etc.) as a service (daemon) application – we need credentials. It is done via “App Registration” under Entra Id (Azure AD) – here is the detailed step-by-step guide on registering apps in Azure.

Finally, we’d have a service principal with permissions we need:

We do not hard-code secrets, so let us create a key vault to keep secrets safe:

We’d leave all other configuration option by default, including “Azure role-based access control”, Allow access from: All Networks etc.

Here is the trick: even if I created this key vault and have an admin-level access, I’m not able to work with secrets values. So I need to provide additional access to myself. Being at the key vault, navigate to “Access control (IAM)” and add role assignment…

We’d select “Key Vault Secrets Officer”, next, select members… and select own name:

From this moment you should be able to CRUD secrets with values.

Now, let us generate a secret under App registration and copy secret value:

Then navigate to the Key Vault -> Object/Secrets -> Generate/Import – and save the secret value there:

Now you can get this secret… but the function cannot reach the secret… Here is the proof. Let us update the function code with:

Write-Host "Get secret from the key vault:"
$vaultName = "azure-func-secrets"
$secretName = "azure-func-secured-01"
$kvSecret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $secretName -AsPlainText
Write-Host "Secret:" $kvSecret.Substring(0,3)

and check the function output. You’d see something like (which is not very descriptive):

So, how’d we allow key vault access for the function app? It’s as simple as that:
– first, we’d need some identity assigned to function app
– second, we’d provide access to the key vault to this identity

Managed Identity: assign an identity to the function.
For this, you’d go to function Settings/Identity, and under System Assigned, you’d switch status to On and Save settings.

Then, you’d go to your key vault, Access Control (IAM) and add role assignment. But this time, you’d select the role “Key Vault Secrets User” (more about roles), and “Assign access to” Managed Identity, and select members – select your function app identity (notice, identity is assigned to function app, so all functions under the app will be able to use the identity):

Now, we’d check the next function invocation detail, and voila:

You can see that azure function was able on the fly to pull secret from the key vault, so now we should be able to use these credentials to access SharePoint.

As you know, having client id and client secret would allow you to call MS Graph API. But calling SharePoint API would require authentication with a certificate. “PnP.PowerShell” module use both APIs under the hood, so we’d need a certificate to connect to tenant and work with SharePoint using “PnP.PowerShell” module. Please refer to this article on how to run Connect-PnPOnline with Certificate stored in the Key Vault.

References:

Leave a Reply

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