Azure Functions Zip Deploy with Azure AD Authentication

I was recently faced with a scenario where I needed a script to deploy an Azure Function. Specifically, there was a desire to use a REST API to deploy a .zip file of the Function app.

The documentation for “Deploy ZIP file with REST APIs” indicates this is possible via a HTTP POST request to https://{APP-NAME}.scm.azurewebsites.net/api/zipdeploy. One challenge with the current documentation is the stated need to use HTTP BASIC authentication. Needing to use BASIC authentication is a problem if BASIC authentication is disabled.

As stated in the documentation, the SCM REST API is backed by Azure RBAC. I couldn’t find any reference on how to use the SCM REST API to perform a zip deployment. Does the /zipdeploy endpoint work with AAD-based authentication?

UPDATE: As of December 8th, the documentation shows how to use Azure AD authentication with ZIP deploy for the SCM REST API.

I reached out to a colleague who helped figure out that, yes, it is possible to use the /zipdeploy SCM REST API with AAD-based authentication!

Let’s go through the steps.

Jump to my GitHub repo get started quickly!

Create an Azure Function

I’m going to use Azure Bicep to create an Azure Function (Consumption) app, the required Azure Storage dependency, and Application Insights. You can find the full Bicep files in my GitHub repo. The only “special” thing about my setup is that I’ve disabled basic authentication for the SCM site, and disabled FTP publishing.

 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
resource azureFunction 'Microsoft.Web/sites@2020-12-01' = {
  name: azureFunctionAppName
  location: location
  kind: 'functionapp'
  properties: {
    // ommitted for brevity
  }

  resource config 'config' = {
    name: 'web'
    properties: {
      ftpsState: 'Disabled'
      minTlsVersion: '1.2'
    }
  }

  resource publishingScmCredentialPolicies 'basicPublishingCredentialsPolicies' = {
    name: 'scm'
    location: location
    properties: {
      allow: false
    }
  }

  resource publishingFtpCredentialPolicies 'basicPublishingCredentialsPolicies' = {
    name: 'ftp'
    location: location
    properties: {
      allow: false
    }
  }
}

I can use the Azure CLI to deploy the Bicep template.

1
2
3
4
5
6
7
RESOURCE_GROUP_NAME="rg-azure-function-zip-deploy"

az deployment sub create \
    --template-file ../iac/main.bicep \
    --parameter resourceGroupName="$RESOURCE_GROUP_NAME" \
    --location eastus \
    --confirm-with-what-if

Create a service principal (optional)

I’m going to need a valid Azure account to authenticate against the SCM REST API. I could use my account, which is what I’ve logged into via the Azure CLI. However, to demonstrate that the deployment works with a restricted set of rights and scope, I’m going to create a service principal and give that service principal “Website Contributor” rights to only the newly created Azure Function app.

1
2
3
4
5
6
7
SCOPE='/subscriptions/[SUBSCRIPTION-ID]/resourceGroups/rg-azure-function-zip-deploy/providers/Microsoft.Web/sites/[FUNCTION-APP-NAME]'
SERVICE_PRINCIPAL_NAME='azfuncdeploy-sp'

az ad sp create-for-rbac \
    --name "$SERVICE_PRINCIPAL_NAME" \
    --role "Website Contributor" \
    --scope "$SCOPE"

The az ad sp create-for-rbac command will display output similar to the snippet below. I need to be sure to save the ‘appId’, ‘password’, and ‘tenant’ returned from the command. I’ll need that data to get an access token used to authenticate against the SCM REST API.

1
2
3
4
5
6
7
{
  "appId": "123456aa-aaaa-bbbb-cccc-0123456789123",
  "displayName": "azfuncdeploy-sp",
  "name": "123456aa-aaaa-bbbb-cccc-0123456789123",
  "password": "xxyyxxyyxxyyxyxyxyxyxx",
  "tenant": "123456789-1234-abcd-vxyz-012345678901"
}

Deploy the code

At this point, I have a very basic Azure Function deployment and service principal.

Azure Function deployment

Now I need to deploy the code using the /zipdeploy API. To do so, I need to create a zip file for my function app.

1
2
3
4
5
6
7
8
# Publish the .NET Azure Function.
dotnet publish --configuration Release "../src/MyFunctionApp"

# Change to the directory where the .NET Azure Function is published.
cd ../src/MyFunctionApp/bin/Release/netcoreapp3.1/publish/ 

# Create a zip archive of the published function.
zip -r ../../../../code.zip .

I now need to get the access token that will be used to authenticate against the SCM REST API. I can use the az account get-access-token command to get the token. However, since I created a service principal, I need to login to that account first. I’ll then retrieve the token and save it in a variable for later use.

1
2
3
4
5
6
az login --service-principal \
    --username "$SERVICE_PRINCIPAL_NAME" \
    --password "$SERVICE_PRINCIPAL_PASSWORD" \
    --tenant "$SERVICE_PRINCIPAL_TENANT_ID"

TOKEN=$(az account get-access-token -o tsv --query accessToken)

I now have a valid AAD authentication token, and a zip file containing my function app. The next step is to execute an HTTP POST request against the /zipdeploy endpoint of the SCM REST API. I’ll put the AAD token in the Authorization header.

1
2
3
4
curl -X POST \
    --data-binary @../src/MyFunctionApp/code.zip \
    -H "Authorization: Bearer $TOKEN" \
    "https://${APP_NAME}.scm.azurewebsites.net/api/zipdeploy"

You can find the full code deployment script in my GitHub repo.

That’s it! It’ll take a few seconds for the deployment to complete. I can go to the Azure portal and see my newly deployed function.

Azure Function with deployed function

Summary

It is possible to use an Azure AD identity to publish an Azure Function app via zip deployment. You can also disable HTTP BASIC authentication for the Azure Function’s SCM site to be sure BASIC authentication isn’t used. To do so, you’ll need to do the following:

  1. Deploy an Azure Function.
  2. Disable BASIC authentication (optional).
  3. Create a zip file for the function app.
  4. Obtain the Azure AD authentication token for the identity to authenticate against the SCM REST API.
  5. POST the zip file to the SCM API’s /zipdeploy endpoint, providing the token as part of an Authorization header.

Special thanks to Matthew Henderson for his assistance!!!