Photo by william william

Define your containers inside your Azure Container Apps environment using Terraform

Containers in the cloud

Posted by Damien Aicheh on 05/07/2023 · 8 mins

In this tutorial you will start to define your frontend and backend containers to the Azure Container Apps environment and the Azure Cosmos Db associated using Terraform. This will be done in the ressource group made in the previous tutorial.

This tutorial is part of a full series of tutorials on using Azure Container Apps with Terraform.

Deploy the Azure Cosmos Db

To store the result of the API calls you will use an Azure Cosmos Db database and define a mongo database named ratingsdb.

To achieve this, let’s create a file called cosmos-db.tf and add this code:

resource "azurerm_cosmosdb_account" "this" {
  name                = format("cosmos-%s", local.resource_suffix_kebabcase)
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  offer_type          = "Standard"
  kind                = "MongoDB"

  capabilities {
    name = "mongoEnableDocLevelTTL"
  }

  capabilities {
    name = "MongoDBv3.4"
  }

  capabilities {
    name = "EnableMongo"
  }

  capabilities {
    name = "EnableServerless"
  }

  consistency_policy {
    consistency_level       = "Session"
    max_interval_in_seconds = 5
    max_staleness_prefix    = 100
  }

  geo_location {
    location          = azurerm_resource_group.this.location
    failover_priority = 0
  }
}

resource "azurerm_cosmosdb_mongo_database" "this" {
  name                = "ratingsdb"
  resource_group_name = azurerm_cosmosdb_account.this.resource_group_name
  account_name        = azurerm_cosmosdb_account.this.name
}

To continue in the spirit of serverless services like Azure Container Apps, as you can see the Azure Cosmos Db is enabled with the serverless mode.

Define the backend container

In the previous tutorial you created the Azure Container Apps environment. Now you will define the backend container to it.

First, create a file called container-api.tf and add this code:

resource "azurerm_container_app" "api" {
  name                         = format("ca-api-%s", local.resource_suffix_kebabcase)
  container_app_environment_id = azurerm_container_app_environment.this.id
  resource_group_name          = azurerm_resource_group.this.name
  revision_mode                = "Single"

  lifecycle {
    ignore_changes = [
      template.0.container[0].image
    ]
  }

  registry {
    server   = azurerm_container_registry.this.login_server
    username = azurerm_container_registry.this.admin_username
  }

  ingress {
    external_enabled = true
    target_port      = 3000
    traffic_weight {
      percentage      = 100
      latest_revision = true
    }
  }

  template {
    container {
      name   = format("ca-api")
      image  = "crdevwefrt01.azurecr.io/fruits-backend:1.0.0"
      cpu    = 0.25
      memory = "0.5Gi"
    }

    min_replicas = 0
  }
}

Make sure to change the image attribute to your own image registry.

If you look at the Dockerfile inside the GitHub repository, you will see that the port 3000 is exposed. So you need to define it to 3000 in the target_port attribute of the ingress block.

The image attribute of the container is ignore using the lifecycle block so that it can be managed by your CI/CD pipeline.

By default you can see that the min_replicas is set to 0. This means that the container will not be started until the first request is received. This is a great way to save money and resources.

To retreive the Docker image from the Azure Container Registry you need to define the registry attribute and link it to the registry you created in the previous tutorial.

The password will be provided using secrets in the next tutorial.

Define the frontend container

After the backend container, it’s time to define the frontend container. For that, create a file called container-web.tf and add this code:

resource "azurerm_container_app" "web" {
  name                         = format("ca-web-%s", local.resource_suffix_kebabcase)
  container_app_environment_id = azurerm_container_app_environment.this.id
  resource_group_name          = azurerm_resource_group.this.name
  revision_mode                = "Single"

  registry {
    server   = azurerm_container_registry.this.login_server
    username = azurerm_container_registry.this.admin_username
  }

  lifecycle {
    ignore_changes = [
      template.0.container[0].image
    ]
  }

  ingress {
    external_enabled = true
    target_port      = 8080
    traffic_weight {
      percentage      = 100
      latest_revision = true
    }
  }

  template {
    container {
      name   = format("ca-web")
      image  = "crdevwefrt01.azurecr.io/fruits-frontend:1.0.0"
      cpu    = 0.25
      memory = "0.5Gi"
    }
    min_replicas = 0
  }
}

Remember to change the image attribute to your own image registry.

Like the backend container, the frontend container is linked to the Azure Container Registry and the image attribute is ignored. The target_port is set to 8080 as defined in the Dockerfile, see the GitHub repository.

Final touch

You have your Container Apps and Cosmos Db serverless for MongoDb defined. You will find the complete source code in this Github repository.

What’s next?

In the next tutorial of this series we will focus on deploying the Azure Container Apps and declare secrets and environment variables for the containers.

Do not hesitate to follow me on to not miss my next tutorial!