Reputation: 33
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
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