El-Shorbagy Amr
El-Shorbagy Amr

Reputation: 33

Configure Azure function authentication using Arm or Bicep template

I have a question related to Configuring Azure function authentication using Arm or Bicep template.

When I use the portal to configure authentication for Azure function, a MICROSOFT_PROVIDER_AUTHENTICATION_SECRET gets created automatically in the config. However, when I use the Bicep template below, MICROSOFT_PROVIDER_AUTHENTICATION_SECRET does not get created.

I tried also "az webapp auth microsoft update" command from CLI and got the same result. Authentication config got created without MICROSOFT_PROVIDER_AUTHENTICATION_SECRET

param function1name string = 'testfunctionnum1'
param region string
param srv string = 'xxxxx'

resource site1 'Microsoft.Web/sites@2022-03-01' = {
  name: function1name
  kind: 'functionapp,linux'
  location: region 
  identity:{
    type: 'SystemAssigned'
  }
  properties: {
    // name: function1name
    scmSiteAlsoStopped: false
    clientAffinityEnabled: false
    clientCertEnabled: false
    clientCertMode: 'Required'
    hostNamesDisabled: false
    containerSize: 1536
    dailyMemoryTimeQuota: 0
    httpsOnly: true
    redundancyMode: 'None'
    storageAccountRequired: false
    keyVaultReferenceIdentity: 'SystemAssigned'
    siteConfig: {
      numberOfWorkers:1
      linuxFxVersion:'Python|3.9'
      acrUseManagedIdentityCreds: false
      alwaysOn: false
      http20Enabled: false
      functionAppScaleLimit: 200
      minimumElasticInstanceCount: 0
      ]
    }
    serverFarmId: srv

  }
}


resource fn1config 'Microsoft.Web/sites/config@2022-03-01' = {
  parent: site1
  name: 'web'
  properties:{
    linuxFxVersion: 'PYTHON|3.9'
    ftpsState: 'FtpsOnly'
    
  }
}

resource fn1auth 'Microsoft.Web/sites/config@2022-03-01' = {
  parent: site1
  name: 'authsettingsV2'
  properties:{
    platform: {
      enabled: true
    }
    globalValidation: {
      requireAuthentication: true
      unauthenticatedClientAction: 'Return401'
    }
    identityProviders:{
      azureActiveDirectory:{
        enabled: true
        registration:{
          clientId:'xxx'
          clientSecretSettingName: 'MICROSOFT_PROVIDER_AUTHENTICATION_SECRET'
          openIdIssuer: 'https://sts.windows.net/xxxx/v2.0'
    
        }
        // login:{
        //   disableWWWAuthenticate: false
        // }
        // isAutoProvisioned: false
      }
    }
   login:{
    tokenStore:{
      enabled:true
    }
   } 
  }
}

Upvotes: 3

Views: 3371

Answers (1)

Moffmo
Moffmo

Reputation: 218

This is the only way I have found that works. Hopefully creating AD applications will come to Bicep soon as it's quite frustrating.

You can create the application, and secret in AD with Azure CLI, then use these to pass them down into the bicep, and into the function app auth settings. Note that I save the secret into the config, and use the client id for the auth settings.

For example I have a powershell script:

param(
    [Parameter()]
    [string]$functionAppName = "functionAppName",
    [Parameter()]
    [string]$resourceGroupName = "resourceGroupName"
)

$replyUrl =  "https://$functionAppName.azurewebsites.net/.auth/login/aad/callback"

$existingApp = az ad app list --display-name $functionAppName | ConvertFrom-Json

if($existingApp.Length -eq 0) {
  $app = az ad app create --display-name $functionAppName --web-redirect-uris "$replyUrl" --enable-id-token-issuance --sign-in-audience "AzureADMyOrg" | ConvertFrom-Json
  $appId = $app.appId
} else {
  $appId = $existingApp[0].appId
}

$secret = az ad app credential reset --id $appId --years 1 --append | ConvertFrom-Json
$secretValue = $secret.password

$bicepFilePath = "path-to-bicep.bicep"

az deployment group create `
  --resource-group $resourceGroupName `
  --template-file $bicepFilePath `
  --parameters functionAppClientId=$appId functionAppClientSecret=$secretValue

Bicep:

param location string = resourceGroup().location
param functionAppName string
param functionAppClientId string
@secure()
param functionAppClientSecret string

var functionAppPlanName = '${functionAppName}-plan'
var functionAppStorageName = '${functionAppName}-storage'

// NOTE THAT YOU MIGHT WANT TO PASS THIS TENANT ID DOWN IF TENANT IS DIFFERENT FROM SUBSCRIPTION
var tenantId = subscription().tenantId

resource functionAppPlan 'Microsoft.Web/serverfarms@2021-02-01' = {
  name: functionAppPlanName
  location: location
  sku: {
    name: 'Y1'
    tier: 'Dynamic'
  }
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
  name: functionAppStorageName
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

resource functionApp 'Microsoft.Web/sites@2021-02-01' = {
  name: functionAppName
  location: location
  kind: 'functionapp'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: functionAppPlan.id
    clientAffinityEnabled: false
    httpsOnly: true
    siteConfig: {
      appSettings: [
        {
          name: 'FUNCTIONS_WORKER_RUNTIME'
          value: 'dotnet'
        }
        {
          name: 'AzureWebJobsStorage'
          value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};'
        }
        {
          name: 'FUNCTIONS_AUTH_AAD_SECRET'
          value: functionAppClientSecret
        }
        {
          name: 'FUNCTIONS_EXTENSION_VERSION'
          value: '~4'
        }
      ]
    }
  }
}

resource authsettings 'Microsoft.Web/sites/config@2022-03-01' = {
  parent: functionApp
  name: 'authsettingsV2'
  properties: {
    platform: {
      enabled: true
      runtimeVersion: '2'
    }
    identityProviders: {
      azureActiveDirectory: {
        enabled: true
        registration: {
          clientId: functionAppClientId
          clientSecretSettingName: 'FUNCTIONS_AUTH_AAD_SECRET'
          openIdIssuer: 'https://sts.windows.net/${tenantId}/v2.0'
        }
      }
    }
  }
}

Would just like to point out, I kept this as simple as possible. But best practice would dictate that you actually save the client secret to a keyvault and then reference that in the app settings.

Upvotes: 5

Related Questions