Search code examples
azureazure-devopsazure-pipelinescontainersazure-resource-manager

Deploying Container App with Arm Template via Azure Devops: UNAUTHORIZED: authentication required


For the first time I am trying to deploy the container app.

I have a Azure Container Registry already deployed. I have set the permissions for the Azure Service Connection for ACR pull / push etcetera on subscription level.

First thing I do is building and pushing the image to the registry via Azure Devops pipeline. This works good. I also have the container app environment Second I am using an Arm template for creating the container app.

- task: AzureResourceManagerTemplateDeployment@3
  displayName: "Create Container App"
  inputs:
    deploymentScope: 'Resource Group'
    azureResourceManagerConnection: $(serviceConnection)
    subscriptionId: '$(SubscriptionID)'
    action: 'Create Or Update Resource Group'
    resourceGroupName: '$(ResourceGroupName)'
    location: 'West Europe'
    templateLocation: 'Linked artifact'
    csmFile: '$(Pipeline.Workspace)/drop/armtemplates/container_deployment.json'
    csmParametersFile: '$(Pipeline.Workspace)/drop/armtemplates/container_parameters.json'
    deploymentMode: 'Incremental'
  continueOnError: false

The resource part of the template:

    "resources": [
        {
            "type": "Microsoft.App/containerApps",
            "apiVersion": "2023-05-01",
            "name": "[parameters('containerAppName')]",
            "location": "[parameters('location')]",
            "properties": {
                "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]",
                "configuration": {
                    "ingress": {
                        "external": true,
                        "targetPort": "[parameters('targetPort')]",
                        "allowInsecure": false,
                        "traffic": [
                            {
                                "latestRevision": true,
                                "weight": 100
                            }
                        ]
                    }
                },
                "template": {
                    "revisionSuffix": "firstrevision",
                    "containers": [
                        {
                            "name": "[parameters('containerAppName')]",
                            "image": "[parameters('containerImage')]",
                            "resources": {
                                "cpu": "[json(parameters('cpuCore'))]",
                                "memory": "[format('{0}Gi', parameters('memorySize'))]"
                            }
                        }
                    ],
                    "scale": {
                        "minReplicas": "[parameters('minReplicas')]",
                        "maxReplicas": "[parameters('maxReplicas')]"
                    }
                }
            }
        }
    ]
}

Error details: The following field(s) are either invalid or missing. Field 'template.containers.NameContainerApp.image' is invalid with details: 'Invalid value: "containerregistryname.azurecr.io/todolistapp:latest": GET https:?scope=repository%3Atodolistapp%3Apull&service=containerregistryname.azurecr.io: UNAUTHORIZED: authentication required

What needs exactly the required authorization in this case?


Solution

  • In your template, you didn't provide credentials in the Container Apps configuration when you deploy images hosted on private registries.

    Solution:

    1. Provide credentials in the Container Apps configuration

    You can deploy images hosted on private registries by providing credentials in the Container Apps configuration.

    You can define the registry in the registries array in the properties.configuration section of the container app resource template. The passwordSecretRef field identifies the name of the secret in the secrets array name where you defined the password.

    Steps:

    • Enable the admin user in the Access keys and you will get the password.

      admin user

    • In the pipeline, set a secret variable named secrets, the value is the password you get in the Access keys.

      secret

    • In your container_deployment.json, add the secrets and registries in properties configuration.

    Sample resources part:

      "resources": [
        {
          "type": "Microsoft.App/containerApps",
          "apiVersion": "2023-05-01",
          "name": "[parameters('containerAppName')]",
          "location": "[parameters('location')]",
          "properties": {
            
            "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]",
            "configuration": {
              "ingress": {
                "external": true,
                "targetPort": "[parameters('targetPort')]",
                "allowInsecure": false,
                "traffic": [
                  {
                    "latestRevision": true,
                    "weight": 100
                  }
                ]
              },
                "secrets": [
                  {
                    "name": "myregistrypassword",
                    "value": "[parameters('registry_password')]"
                  }
                ],
                "registries": [
                  {
                    "server": "miao0909.azurecr.io",
                    "username": "miao0909",
                    "passwordSecretRef": "myregistrypassword"
                  }
                ],
              "activeRevisionsMode": "Single"
            },
            "template": {
              "revisionSuffix": "firstrevision",
              "containers": [
                {
                  "name": "[parameters('containerAppName')]",
                  "image": "[parameters('containerImage')]",
                  "resources": {
                    "cpu": "[json(parameters('cpuCore'))]",
                    "memory": "[format('{0}Gi', parameters('memorySize'))]"
                  }
                }
              ],
              "scale": {
                "minReplicas": "[parameters('minReplicas')]",
                "maxReplicas": "[parameters('maxReplicas')]"
              }
            }
          }
        }
      ]
    
    • Override the registry_password parameter with Parameters: '-registry_password $(secrets)' in the AzureResourceManagerTemplateDeployment@3 task.
    - task: AzureResourceManagerTemplateDeployment@3
      displayName: 'ARM Template deployment: Resource Group scope'
      inputs:
        deploymentScope: 'Resource Group'
        azureResourceManagerConnection: ''
        subscriptionId: ''
        action: 'Create Or Update Resource Group'
        resourceGroupName: 'Default'
        location: ''
        templateLocation: 'Linked artifact'
        csmFile: '$(Pipeline.Workspace)/drop/armtemplates/container_deployment.json'
        csmParametersFile: '$(Pipeline.Workspace)/drop/armtemplates/container_parameters.json'
        overrideParameters: '-registry_password $(secrets)'
        deploymentMode: 'Incremental'
    
    1. Use Managed identity with Azure Container Registry

    You can use an Azure managed identity to authenticate with Azure Container Registry instead of using a username and password. For more information, see Managed identities in Azure Container Apps.

    To use managed identity with a registry, the identity must be enabled in the app and it must be assigned acrPull role in the registry. To configure the registry, use the managed identity resource ID for a user-assigned identity, or system for the system-assigned identity in the identity property of the registry. Don't configure a username and password when using managed identity.

    Steps:

    • Create a user-assigned identity
    • Assign the ACR pull / push role to the user-assigned identity
    • Add the managed identity resource ID in the identity property of the registry in the template.

    Sample resources part:

      "resources": [
        {
          "identity": {
            "userAssignedIdentities": {
              "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/Default/providers/Microsoft.ManagedIdentity/userAssignedIdentities/username": {}
            },
            "type": "UserAssigned"
          },
          "type": "Microsoft.App/containerApps",
          "apiVersion": "2023-05-01",
          "name": "[parameters('containerAppName')]",
          "location": "[parameters('location')]",
          "properties": {
            
            "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]",
            "configuration": {
              "ingress": {
                "external": true,
                "targetPort": "[parameters('targetPort')]",
                "allowInsecure": false,
                "traffic": [
                  {
                    "latestRevision": true,
                    "weight": 100
                  }
                ]
              },
                "registries": [
                  {
                    "server": "miao0909.azurecr.io",
                    "identity": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/Default/providers/Microsoft.ManagedIdentity/userAssignedIdentities/username"
                  }
                ],
              "activeRevisionsMode": "Single"
            },
            "template": {
              "revisionSuffix": "firstrevision",
              "containers": [
                {
                  "name": "[parameters('containerAppName')]",
                  "image": "[parameters('containerImage')]",
                  "resources": {
                    "cpu": "[json(parameters('cpuCore'))]",
                    "memory": "[format('{0}Gi', parameters('memorySize'))]"
                  }
                }
              ],
              "scale": {
                "minReplicas": "[parameters('minReplicas')]",
                "maxReplicas": "[parameters('maxReplicas')]"
              }
            }
          }
        }
      ]