Home > Software design >  Functionapp With Custom Runtime Image, Use ACR Admin Credentials in ARM Template
Functionapp With Custom Runtime Image, Use ACR Admin Credentials in ARM Template

Time:10-11

I'm in the process of migrating our functionapps to custom runtime containers. I'm doing this through ARM templates.

I've got to the point where I can do this, however, in order to get it to work, I have to manually open the Deployment Center and hit save after provisioning, otherwise the functionapp cannot pull down from the ACR (and the logs say there's an auth error).

2022-10-10T22:25:29.055Z INFO - Recycling container because of AppSettingsChange and isMainSite = True 2022-10-10T22:25:32.116Z ERROR - DockerApiException: Docker API responded with status code=InternalServerError, response={"message":"Get Screenshot of Deployment Centre

As soon as I click save (I don't even change anything) it pulls down and deploys correctly.

Whilst I don't need to reprovision often, this manual step is a pain and I want to fix it, what do I need to add to my ARM template to facilitate this?

The relevent section of the ARM template is:

{
      "type": "Microsoft.Web/sites",
      "apiVersion": "2022-03-01",
      "name": "[parameters('functionAppName')]",
      "location": "[parameters('location')]",
      "kind": "functionapp,linux,container",
      "identity": {
        "type": "SystemAssigned"
      },
      "dependsOn": [
        "[variables('appServicePlanResourceId')]",
        "[variables('deploymentStorageAccountId')]",
        "[variables('networkResourceId')]",
        "[resourceId('microsoft.insights/components', parameters('functionAppName'))]"
      ],
      "tags": {
        "Product": "[variables('productTag')]",
        "Environment": "[parameters('environmentTag')]"
      },
      "properties": {
        "ftpsState": "FtpsOnly",
        "httpsOnly": true,
        "reserved": true,
        "serverFarmId": "[variables('appServicePlanResourceId')]",
        "siteConfig": {
          "appSettings": [
            {
              "name": "AzureWebJobsStorage",
              "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('deploymentStorageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(variables('deploymentStorageAccountId'), '2019-06-01').keys[0].value)]"
            },
            {
              "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
              "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('deploymentStorageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(variables('deploymentStorageAccountId'), '2019-06-01').keys[0].value)]"
            },
            {
              "name": "WEBSITE_CONTENTSHARE",
              "value": "[toLower(parameters('functionAppName'))]"
            },
            {
              "name": "FUNCTIONS_EXTENSION_VERSION",
              "value": "~3"
            },
            {
              "name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
              "value": "[concat('InstrumentationKey=', reference(resourceId('Microsoft.Insights/components', parameters('functionAppName')), '2020-02-02-preview').instrumentationKey)]"
            },
            {
              "name": "FUNCTIONS_WORKER_RUNTIME",
              "value": "dotnet"
            },
            {
              "name": "EventGridTopicEndpoint",
              "value": "[reference(variables('eventGridTopicId')).endpoint]"
            },
            {
              "name": "EventGridTopicAccessKey",
              "value": "[listKeys(variables('eventGridTopicId'), '2020-06-01').key1]"
            },
            {
              "name": "WEBSITE_DNS_SERVER",
              "value": "redacted"
            },
            {
              "name": "WEBSITE_VNET_ROUTE_ALL",
              "value": 1
            },
            {
              "name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
              "value": "false"
            }
          ],
          "linuxFxVersion": "[parameters('linuxFxVersion')]",
          "acrUseManagedIdentityCreds": false
        }
      },
      "resources": [
        {
          "type": "networkConfig",
          "apiVersion": "2019-08-01",
          "name": "virtualNetwork",
          "dependsOn": [ "[variables('functionAppResourceId')]" ],
          "properties": {
            "subnetResourceId": "[variables('subnetResourceId')]",
            "isSwift": true
          }
        }
      ]
    }

[parameters('linuxFxVersion')] evaluates to DOCKER|redacted.azurecr.io/redacted:preview


Every answer that I've found so far requires either adding config options with docker usernames and passwords, or using a managed identity, neither of which is what we want.

CodePudding user response:

You need to add an RBAC assignment to your ACR instance granting the system-assigned identity of your function app the AcrPull role.

The alternative is using admin credentials.

When you hit "Save" in the deployment center, it's using one of those two methods -- it's retrieving the admin credentials from the ACR and applying them to the app service. It's not doing anything special, it's doing exactly what you can do yourself.

I recommend using managed identities instead. You can even create a single user-assigned identity and share it across multiple function apps, if you really want to.

CodePudding user response:

Reference a secret in a key vault:

"adminPassword": {
    "reference": {
        "keyVault": {
        "id": "/subscriptions/<SubscriptionID>/resourceGroups/mykeyvaultdeploymentrg/providers/Microsoft.KeyVault/vaults/<KeyVaultName>"
        },
        "secretName": "vmAdminPassword"
    }
}

CodePudding user response:

So with hints taken from the other two answers and from here, I've devised two solutions.

Using Service Principal Role

  1. Add "acrUseManagedIdentityCreds": true to the siteConfig in my ARM template
  2. Assign the AcrPull role to the service principal of the functionapp (I've not tested this snippet because perms weren't set-up quite right and it's too late for me to ask someone to change them)
"resources": [
        {
          "type": "Microsoft.Authorization/roleAssignments",
          "apiVersion": "2018-09-01-preview",
          "name": "[guid(resourceGroup().id)]",
          "dependsOn": [
            "[parameters('functionAppName')]"
          ],
          "properties": {
            "roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]",
            "principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2022-03-01').identity.principalId]"
          }
        }
]

Getting Admin Creds with Reference

  1. Add these variables to my template:
"registryName": "containerRegName",
    "registrySubscriptionId": "container-reg-sub-id",
    "registryResourceGroup": "container-reg-rg",
    "registryResourceId": "[resourceId(variables('registrySubscriptionId'), variables('registryResourceGroup'), 'Microsoft.ContainerRegistry/registries', variables('registryName'))]"
  },
  1. Then add these configuration options to my appsettings:
{
    "name": "DOCKER_REGISTRY_SERVER_URL",
    "value": "[reference(variables('registryResourceId'), '2019-05-01').loginServer]"
},
{
    "name": "DOCKER_REGISTRY_SERVER_USERNAME",
    "value": "[listCredentials(variables('registryResourceId'), '2019-05-01').username]"
},
{
    "name":  "DOCKER_REGISTRY_SERVER_PASSWORD",
    "value": "[listCredentials(variables('registryResourceId'), '2019-05-01').passwords[0].value]"
}
  • Related