Quickstart: Deploy an existing container image to Container Apps using Bicep

Posted by Jason on Thursday, April 7, 2022

Like my last post (Quickstart: Container Apps using Bicep), this post is a supplement to the existing Azure Container Apps docs and provides a Bicep walkthrough to getting started.

Quickstart: Deploy an existing container image to Container Apps using Bicep

Azure Container Apps is a serverless platform to run and orchestrate your container applications.

In this quickstart, you’ll use Bicep to create a Container Apps environment with an existing container image stored in an Azure Container Registry. If you have not used Bicep yet or want to learn more about what it can do, I recommend going through the Fundamentals of Bicep learning path to get you started.

Prerequisites

Setup

First you’ll need to sign in to Azure using the CLI (I typically use the device code option but it may not be necessary for you). Run the command below and follow the prompts to get logged into your account.

az login --use-device-code

If you haven’t used your Azure CLI installation for awhile, you may want to make sure it is up-to-date before continuing on. You can do this by running the following:

az upgrade

Also if you haven’t used Bicep for awhile, you may way want to make sure it is also up-to-date by running:

az bicep upgrade

If you haven’t already installed the Azure Container Apps extension, you will need to run the following command:

az extension add --name containerapp

And if you haven’t registered the Microsoft.App namespace, you can do so by running:

az provider register --namespace Microsoft.App

Run the following in order to set the environment variables we’ll use later:

RESOURCE_GROUP="my-container-apps"
LOCATION="eastus2"
CONTAINERAPP_NAME="my-container-app"
CONTAINERAPPS_ENVIRONMENT="my-environment"

Create the resource group used for resources in this quickstart:

az group create \
  -n $RESOURCE_GROUP \
  -l $LOCATION

Option 1: Use an Existing Container Image

If you have an existing container image in an Azure Container Registry, set the following variables the Container App will use to pull the image from the registry:

CONTAINER_IMAGE_NAME=<CONTAINER_IMAGE_NAME>
REGISTRY_LOGIN_SERVER=<REGISTRY_LOGIN_URL>
REGISTRY_USERNAME=<REGISTRY_USERNAME>
REGISTRY_PASSWORD=<REGISTRY_PASSWORD>

Option 2: Create an Image

If you don’t have a container image to use, I’ve modified the example from Tutorial: Build and deploy container images in the cloud with Azure Container Registry Tasks below to:

  • Clone the sample repo from GitHub
  • Create an Azure Container Registry (ACR)
  • Build the sample container image and store in ACR
  • Create an Azure Key Vault
  • Create a service principal and store the credentials
  • Populate the variables needed for the Bicep template

Before you begin, replace <registry-name> in the script below with at unique name for your container registry. The registry name can only contain lower case letters and needs to be between 5 and 50 characters in length. Then run the following command to set the variables:

ACR_NAME="<registry-name>"
AKV_NAME="$ACR_NAME-vault"

Clone the sample repo from GitHub

Next you’ll need to clone the Azure-Samples/acr-build-helloworld-node to a local directory. Run the following command to clone the repo:

git clone https://github.com/Azure-Samples/acr-build-helloworld-node.git

Create an Azure Container Registry (ACR)

Before you can have ACR build the container for you, you need to run the following command to create the Azure Container Registry we will use in this quickstart:

az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --sku Standard --location $LOCATION

Build the sample container image and store in ACR

First, you’ll need to change to the directory the Dockerfile is in:

cd acr-build-helloworld-node/

Now that you have the code locally, you can use ACR to build the code and store the image for using in the Container App later by running the following command:

az acr build --registry $ACR_NAME --image helloacrtasks:v1 .

NOTE: you may get a message “The resource with name ‘acr name’ and type ‘Microsoft.ContainerRegistry/registries’ could not be found in subscription ‘subscription name’.” - If so, just wait a few seconds and try again.

This will take a little while to build the container. While it is building, your console should show the progress that looks something like this: Screenshot of ACR building image

Create an Azure Key Vault

When we create the Container App later, we need to be able to access the ACR in order to pull the container image we just built. There are a couple of ways to do this: Admin user or Service Principal. We’ll create a Service Principal and store the username and password in Key Vault.

Create an Azure Key Vault using this command:

az keyvault create --resource-group $RESOURCE_GROUP --name $AKV_NAME

Create a service principal and store the credentials

Create a service principal and a secret to store its password in Key Vault by running this command:

az keyvault secret set \
  --vault-name $AKV_NAME \
  --name $ACR_NAME-pull-pwd \
  --value $(az ad sp create-for-rbac --name $ACR_NAME-pull --scopes $(az acr show --name $ACR_NAME --query id --output tsv) --role acrpull --query password --output tsv)

NOTE: This will be used as the REGISTRY_PASSWORD when creating the Container App.

Next store the service principal ID in another secret using this command:

az keyvault secret set \
    --vault-name $AKV_NAME \
    --name $ACR_NAME-pull-usr \
    --value $(az ad sp list --display-name $ACR_NAME-pull --query [].appId --output tsv)

NOTE: This will be used as the REGISTRY_USERNAME when creating the Container App.

Populate the variables needed for the Bicep template

You now have an image in an Azure Container Registry to use in a Container App. Run the following to populate the variables to use with the Bicep template next:

CONTAINER_IMAGE_NAME="$ACR_NAME.azurecr.io/helloacrtasks:v1"
REGISTRY_LOGIN_SERVER="$ACR_NAME.azurecr.io"
REGISTRY_USERNAME=$(az keyvault secret show --vault-name $AKV_NAME --name $ACR_NAME-pull-usr --query value -o tsv)
REGISTRY_PASSWORD=$(az keyvault secret show --vault-name $AKV_NAME --name $ACR_NAME-pull-pwd --query value -o tsv)

Create a Bicep template

You need to create a Bicep template containing all the resources for this quickstart:

  • Log Analytics Workspace
  • Container Apps Environment
  • Container App
  • Secret for ACR

Copy and save the following text into a file named aca-quickstart.bicep:

param location string = resourceGroup().location
param environmentName string
param appName string
param containerRegistry string
param containerRegistryUsername string

@secure()
param containerRegistryPassword string
param secrets array = [
  {
    name: 'acr-password'
    value: containerRegistryPassword
  }
]
var registrySecretRefName = 'acr-password'

param logAnalyticsWorkspaceName string = '${appName}-logs'
param containerImage string
param containerPort int = 80

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-03-01-preview' = {
  name: logAnalyticsWorkspaceName
  location: location
  properties: any({
    retentionInDays: 30
    features: {
      searchVersion: 1
    }
    sku: {
      name: 'PerGB2018'
    }
  })
}

resource environment 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
  name: environmentName
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalyticsWorkspace.properties.customerId
        sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
      }
    }
  }
}

resource containerApp 'Microsoft.App/containerApps@2022-01-01-preview' = {
  name: appName
  location: location
  properties: {
    managedEnvironmentId: environment.id
    configuration: {  
      secrets: secrets
      registries: [
        {
          server: containerRegistry
          username: containerRegistryUsername
          passwordSecretRef: registrySecretRefName
        }
      ]
      ingress: {
        external: true
        targetPort: containerPort
      }
    }
    template: {
      containers: [
        {
          image: containerImage
          name: appName
        }
      ]
    }
  }
}

output fqdn string = containerApp.properties.configuration.ingress.fqdn

Deploy resources

Next you need to navigate to the directory you saved the aca-quickstart.bicep file and run the following command:

az deployment group create \
  -g $RESOURCE_GROUP \
  -f ./aca-quickstart.bicep \
  -p environmentName=$CONTAINERAPPS_ENVIRONMENT \
  -p appName=$CONTAINERAPP_NAME \
  -p containerImage=$CONTAINER_IMAGE_NAME \
  -p containerRegistry=$REGISTRY_LOGIN_SERVER \
  -p containerRegistryUsername=$REGISTRY_USERNAME \
  -p containerRegistryPassword=$REGISTRY_PASSWORD \
  --query properties.outputs.fqdn.value

Once the resources have been deployed, the fully qualified domain name will be returned in the output. Mine looks like this:

Result
-----------------------------------------------------------------
my-container-app.grayrock-8a89bc72.eastus2.azurecontainerapps.io/

Verify

Copy the fully qualified domain name returned from the deployment and paste it into a browser to verify the app is running. The container app should like like this: Screenshot of sample app

Clean up resources

When you are done, don’t forget to delete your resource group to remove all the resources created in this quickstart:

az group delete \
  -g $RESOURCE_GROUP