Project 4: Creating Serverless azure infra and deploying the application using Azure Devops.

Project 4: Creating Serverless azure infra and deploying the application using Azure Devops.

We are going to create container apps using terraform.

  1. Install the terraform, azurecli in your device and login to your azure account through your terminal/powershell.

  2. I wrote the terraform to create new resources from scarch by creating child modules of each resources

     terraform
     ├── main.tf
     ├── variables.tf
     ├── outputs.tf
     ├── modules
     │   ├── container_app
     │   │   ├── main.tf
     │   │   ├── variables.tf
     │   │   ├── outputs.tf
     │   ├── network
     │   │   ├── main.tf
     │   │   ├── variables.tf
     │   │   ├── outputs.tf
     │   ├── log_analytics
     │   │   ├── main.tf
     │   │   ├── variables.tf
     │   │   ├── outputs.tf
     │   ├── acr
     │   │   ├── main.tf
     │   │   ├── variables.tf
     │   │   ├── outputs.tf
     │   ├── identity
     │   │   ├── main.tf
     │   │   ├── variables.tf
     │   │   ├── outputs.tf
     │   ├── grafana
     │       ├── main.tf
     │       ├── variables.tf
     │       ├── outputs.tf
    

    a) Log-analytics

    # modules/LogAnalytics/main.tf
    
    resource "azurerm_log_analytics_workspace" "main" {
      name                = var.name
      location            = var.location
      resource_group_name = var.resource_group_name
      sku                 = "PerGB2018"
      retention_in_days   = 30
    
      tags = {
        "Created By" = var.tag_CreatedBy
        "Created Date" = var.tag_CreatedDate
      }
    
    }
    
    output "workspace_id" {
      value = azurerm_log_analytics_workspace.main.id
    }
    
    ----------------------------------------------------------------------
    
    # modules/LogAnalytics/variable.tf
    
    variable "location" {}
    variable "resource_group_name" {}
    variable "name" {}
    variable "tag_CreatedBy" {}
    variable "tag_CreatedDate" {}
    

    b) User Assigned Identity

    # modules/user_assigned_identity/main.tf
    
    resource "azurerm_user_assigned_identity" "main" {
      name                = "High5Identity"
      location            = var.location
      resource_group_name = var.resource_group_name
    }
    
    output "id" {
      value = azurerm_user_assigned_identity.main.id
    }
    
    output "principal_id" {
      value = azurerm_user_assigned_identity.main.principal_id
    }
    
    ----------------------------------------------------------------------
    
    # modules/user_assigned_identity/variables.tf
    
    variable "location" {}
    variable "resource_group_name" {}
    

    c) ACR (Azure Container Registry)

    # modules/acr/main.tf
    
    resource "azurerm_container_registry" "main" {
      name                = "high5"
      location            = var.location
      resource_group_name = var.resource_group_name
      sku                 = "Premium"
      admin_enabled       = true
    
      identity {
        type         = "UserAssigned"
        identity_ids = [var.identity_id]
      }
    
      tags = {
        "Created By" = var.tag_CreatedBy
        "Created Date" = var.tag_CreatedDate
      }
    
      lifecycle {
        prevent_destroy = true               # Reference link https://developer.hashicorp.com/terraform/tutorials/state/resource-lifecycle#prevent-resource-deletion
      }  
    
    }
    
    resource "azurerm_role_assignment" "acr_pull" {
      scope              = azurerm_container_registry.main.id
      role_definition_name = "AcrPull"
      principal_id       = var.principal_id
    }
    
    output "login_server" {
      value = azurerm_container_registry.main.login_server
    }
    
    
    ----------------------------------------------------------------------
    
    # modules/acr/variables.tf
    
    variable "location" {}
    variable "resource_group_name" {}
    variable "identity_id" {}
    variable "principal_id" {}
    variable "tag_CreatedBy" {}
    variable "tag_CreatedDate" {}
    

    d) Azure Container App Environment

    # modules/container_environment/main.tf
    
    resource "azurerm_container_app_environment" "main" {
      name                       = var.env_name
      location                   = var.location
      resource_group_name        = var.resource_group_name
      log_analytics_workspace_id = var.log_analytics_workspace_id
      infrastructure_subnet_id   = azurerm_subnet.main.id
      zone_redundancy_enabled    = true
    
      workload_profile {
        name = "Consumption"
        workload_profile_type = "Consumption"
      }
    
      tags = {
        "Created By" = var.tag_CreatedBy
        "Created Date" = var.tag_CreatedDate
      }
    
    }
    
    
    resource "azurerm_virtual_network" "main" {
      name                = var.virtual_network_name
      location            = var.location
      resource_group_name = var.resource_group_name
      address_space       = ["10.0.0.0/16"]
    
      tags = {
        "Created By" = var.tag_CreatedBy
        "Created Date" = var.tag_CreatedDate
      }
    
    }
    
    resource "azurerm_subnet" "main" {
      name                 = "ContainerSubnet"
      resource_group_name  = var.resource_group_name
      virtual_network_name = azurerm_virtual_network.main.name
      address_prefixes     = ["10.0.0.0/23"]
    
      delegation {
        name = "appservices-delegation"
        service_delegation {
          name = "Microsoft.App/environments"
          actions = [
            "Microsoft.Network/virtualNetworks/subnets/join/action",
            "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action",
          ]
        }
      }
    
    }
    
    
    output "id" {
      value = azurerm_container_app_environment.main.id
    }
    
    output "vnet_id" {
      value = azurerm_virtual_network.main.id
    }
    
    output "subnet_id" {
      value = azurerm_subnet.main.id
    }
    
    
    ----------------------------------------------------------------------
    
    # modules/container_environment/variables.tf
    
    variable "location" {}
    variable "resource_group_name" {}
    variable "log_analytics_workspace_id" {}
    variable "env_name" {}
    variable "tag_CreatedBy" {}
    variable "tag_CreatedDate" {}
    variable "virtual_network_name" {}
    

    e) Azure Container App

    # modules/container_app/main.tf
    
    resource "azurerm_container_app" "main" {
      name                = var.name
      resource_group_name = var.resource_group_name
      container_app_environment_id = var.environment_id
      revision_mode = "Single"
    
      identity {
        type = "UserAssigned"
        identity_ids = [var.identity_id]
      }
    
    
      ingress {
        external_enabled = true
        target_port      = var.ingress_port
        traffic_weight {
          latest_revision = true
          percentage = 100
        }
      }
    
      registry {
        server =  var.acr_login_server
        identity = var.identity_id
      }
    
      template {
     #   min_replicas = 2
     #   max_replicas = 3
        container {
          name   = var.name
        #  image  = "${var.acr_login_server}/${var.name}:latest"
          image = var.image
          cpu    = 0.5
          memory = "1Gi"
    
          env {
            name  = "EXAMPLE_ENV_VAR"
            value = "examplevalue"
          }
        }
      }
    
      tags = {
        "Created By" = var.tag_CreatedBy
        "Created Date" = var.tag_CreatedDate
      }
    
    }
    
    ----------------------------------------------------------------------
    
    # modules/container_app/main.tf
    
    variable "name" {}
    variable "resource_group_name" {}
    variable "environment_id" {}
    variable "identity_id" {}
    variable "acr_login_server" {}
    variable "ingress_port" {}
    variable "image" {}
    variable "tag_CreatedBy" {}
    variable "tag_CreatedDate" {}
    

    f) Grafana

    # modules/grafana/main.tf
    
    resource "azurerm_dashboard_grafana" "grafana" {
      name                              = "azure-grafana-High5"
      resource_group_name               = var.resource_group_name
      location                          = var.location
      sku                               = "Standard"
      grafana_major_version             = "10"
      zone_redundancy_enabled           = true
      api_key_enabled                   = true
      deterministic_outbound_ip_enabled = true
      public_network_access_enabled     = true
    
      identity {
        type = "SystemAssigned"
      }
    
      tags = {
        "Created By" = var.tag_CreatedBy
        "Created Date" = var.tag_CreatedDate
      }
    
      lifecycle {
        prevent_destroy = true               # Reference link https://developer.hashicorp.com/terraform/tutorials/state/resource-lifecycle#prevent-resource-deletion
    }
    
    }
    
    data "azurerm_client_config" "current" {}
    
    resource "azurerm_role_assignment" "role_grafana_admin" {
      scope                = azurerm_dashboard_grafana.grafana.id
      role_definition_name = "Grafana Admin"
      principal_id         = data.azurerm_client_config.current.object_id
    }
    
    data "azurerm_subscription" "current" {}
    
    resource "azurerm_role_assignment" "role_monitoring_reader" {
      scope                = data.azurerm_subscription.current.id
      role_definition_name = "Monitoring Reader"
      principal_id         = azurerm_dashboard_grafana.grafana.identity.0.principal_id
    }
    -----------------------------------------------------------------------
    
    # modules/grafana/variables.tf
    
    variable "location" {}
    variable "resource_group_name" {}
    variable "tag_CreatedBy" {}
    variable "tag_CreatedDate" {}
    

    f) Root folder

    # main.tf
    
    
    resource "azurerm_resource_group" "dev" {
      name     = var.resource_group_name
      location = var.location
      tags = {
        Status = "Running application"
      }
    }
    
    module "log_analytics" {
      source              = "./modules/LogAnalytics"
      name                = var.log_analytics_name
      resource_group_name = azurerm_resource_group.dev.name
      location            = azurerm_resource_group.dev.location
      tag_CreatedBy       = var.tag_CreatedBy
      tag_CreatedDate     = var.tag_CreatedDate
    
      depends_on = [azurerm_resource_group.dev]
    
    }
    
    module "user_assigned_identity" {
      source              = "./modules/user_assigned_identity"
      resource_group_name = "Devops_Tools"
      location            = azurerm_resource_group.dev.location
    
      depends_on = [azurerm_resource_group.dev]
    }
    
    module "acr" {
      source              = "./modules/acr"
      resource_group_name = "Devops_Tools"
      location            = "centralus"
      identity_id         = module.user_assigned_identity.id
      principal_id        = module.user_assigned_identity.principal_id
      tag_CreatedBy       = var.tag_CreatedBy
      tag_CreatedDate     = var.tag_CreatedDate
    
      depends_on = [module.user_assigned_identity]
    
    }
    
    module "environment" {
      source                     = "./modules/container_environment"
      env_name                   = var.environment_name
      resource_group_name        = azurerm_resource_group.dev.name
      location                   = azurerm_resource_group.dev.location
      log_analytics_workspace_id = module.log_analytics.workspace_id
      virtual_network_name       = var.vnet_name
      tag_CreatedBy              = var.tag_CreatedBy
      tag_CreatedDate            = var.tag_CreatedDate
    
      depends_on = [module.log_analytics,
      module.user_assigned_identity]
    
      # lifecycle {
      # prevent_destroy = true               # Reference link https://developer.hashicorp.com/terraform/tutorials/state/resource-lifecycle#prevent-resource-deletion
      #}
    }
    
    module "container_app_1" {
      source              = "./modules/container_app"
      name                = var.app1_name
      resource_group_name = azurerm_resource_group.dev.name
      environment_id      = module.environment.id
      acr_login_server    = module.acr.login_server
      identity_id         = module.user_assigned_identity.id
      image               = "ghcr.io/houssemdellai/containerapps-album-frontend:v1"
      ingress_port        = var.app1_ingress_port
      tag_CreatedBy       = var.tag_CreatedBy
      tag_CreatedDate     = var.tag_CreatedDate
    
      depends_on = [module.environment,
      module.acr]
    }
    
    module "container_app_2" {
      source              = "./modules/container_app"
      name                = var.app2_name
      resource_group_name = azurerm_resource_group.dev.name
      environment_id      = module.environment.id
      acr_login_server    = module.acr.login_server
      identity_id         = module.user_assigned_identity.id
      image               = "ghcr.io/houssemdellai/containerapps-album-backend:v1"
      ingress_port        = var.app2_ingress_port
      tag_CreatedBy       = var.tag_CreatedBy
      tag_CreatedDate     = var.tag_CreatedDate
    
      depends_on = [module.environment,
      module.acr]
    }
    
    
    module "grafana" {
      source              = "./modules/grafana"
      resource_group_name = "Devops_Tools"
      location            = "eastus"
    }
    
    ----------------------------------------------------------------------
    
    # variables.tf
    
    variable "location" {
      description = "The Azure region where the resources should be created."
      type        = string
    }
    
    variable "resource_group_name" {
      description = "The name of the resource group."
      type        = string
    }
    
    variable "environment_name" {
      description = "The name of the container app environment."
      type        = string
    }
    
    variable "app1_name" {
      description = "The name of the first container app."
      type        = string
    }
    
    variable "app2_name" {
      description = "The name of the second container app."
      type        = string
    }
    
    variable "app1_ingress_port" {
      description = "The ingress port for the first container app."
      type        = number
    }
    
    variable "app2_ingress_port" {
      description = "The ingress port for the second container app."
      type        = number
    }
    
    variable "log_analytics_name" {
      description = "Log Analytics name."
      type        = string
    }
    
    variable "tag_CreatedBy" {
      description = "Created By ___"
      type        = string
    }
    
    variable "tag_CreatedDate" {
      description = "Enter Date of creation tag"
    }
    
    variable "vnet_name" {
      description = "Enter the name of the vnet"
      type        = string
    }
    ----------------------------------------------------------------------
    # terraform.tfvars
    
    location            = "eastus"
    resource_group_name = "High-UA-RG"
    environment_name    = "UAEnv"
    app1_name           = "uafrontend"
    app2_name           = "uabackend"
    log_analytics_name  = "UAlogAnalyticsWorkspace"
    app1_ingress_port   = 3000
    app2_ingress_port   = 5001
    tag_CreatedBy       = "Tara Prasad Sarangi"
    tag_CreatedDate     = "08-08-2024"
    vnet_name           = "UAEnvVNet"
    
    ----------------------------------------------------------------------
    
    # provider.tf
    
    terraform 
    {
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "3.113.0"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    
    }
    
  3. But let's suppose you want to import some of the existing resources from the azure portal. You can use terraform import command by refering the terraform document provided for the perticular resources.

    Suppose we want to import existing MSSQL server and DB, Follow below steps

    a) Go to the terraform document in azure provider azurerm and search for the resource. In our case it is mssql_server . Then copy the an example for the mssql_server to the VS Code. And change the name to the actual name that you have kept for the mssql_server in azure portal.

    resource "azurerm_mssql_server" "example" {
      name                         = "high5uaserver"
      resource_group_name          = azurerm_resource_group.example.name
      location                     = azurerm_resource_group.example.location
      version                      = "12.0"
      administrator_login          = "missadministrator"
      administrator_login_password = "thisIsKat11"
      minimum_tls_version          = "1.2"
    
      azuread_administrator {
        login_username = "AzureAD Admin"
        object_id      = "00000000-0000-0000-0000-000000000000"
      }
    
      tags = {
    
      }
    }
    

    b) Go to the terraform resource document same page from where you have copied the example code. Right ride you'll see import. Click on that.
    It'll show something like below. Replace with your Subscription, resource group name and msserver name.

    terraform import <resource>.<terraform_resource_name> <Resource ID> # structure
    
    terraform import azurerm_mssql_server.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my_aks_poc/providers/Microsoft.Sql/servers/high5stageserver
    

    c) Or Let's suppose you want to import terraform resource which you have metioned in a module. We can do so by using below command.

     terraform import module.<module_name>.<resource>.<terraform_resource_name> <Resource ID> # structure
    eg.,
     terraform import module.mssql_server.azurerm_mssql_server.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my_aks_poc/providers/Microsoft.Sql/servers/high5stageserver
    

    d) Paste it in your terminal after replacing it with your values and hit enter. It will import your resource details.

    e) Now type the below command to see view your state file and update the same with the information shown in your statefile.

    terraform show terraform.tfstate
    

    f) If you want to delete a perticular module from terraform state file, you can use below command.

    terraform state rm <resource>.<terraform_resource_name>
    eg.,
    terraform state rm azurerm_resource_group.dev
    

    g) To prevent the changes or accidental deletion of the current imported resource include the below code in your terraform resource which you have updated from your imported resource terraform state file.

      # Keep this to prevent destroying of any Resources where you don't want any deletion or replacement
      lifecycle {
        prevent_destroy = true    
      }
    
    resource "azurerm_mssql_server" "example" {
      name                         = "high5uaserver"
      resource_group_name          = azurerm_resource_group.example.name
      location                     = azurerm_resource_group.example.location
      version                      = "12.0"
      administrator_login          = "missadministrator"
      administrator_login_password = "thisIsKat11"
      minimum_tls_version          = "1.2"
    
      azuread_administrator {
        login_username = "AzureAD Admin"
        object_id      = "00000000-0000-0000-0000-000000000000"
      }
    
      tags = {}
    
      # Keep this to prevent destroying of any Resources where you don't want any deletion or replacement
      lifecycle {
        prevent_destroy = true    
      }
    }
    

    h) After replacing type the below command to check whether everything you have written write or wrong. What are the changes it is going to make if you apply.

    terraform plan
    

    i) Similarly follow the same for mssql_database.

  4. Now we need to work on terraform backend / state file storage. If we don't create any tf file to store the backend file, then generally state file will be stored locally which is not advisable. Storing the Terraform state file locally risks data loss, concurrent modifications, and lack of collaboration. Using Azure Storage for the state file offers centralized management, versioning and secure access.

    a) Creating backend.tf and Storing State in Azure Storage with Versioning Enabled

    Step 1: Create an Azure Storage Account and Blob Container

    • Create a Resource Group:

        az group create --name myResourceGroup --location eastus
      
    • Create a Storage Account:

        az storage account create --name mystorageaccount --resource-group myResourceGroup --location eastus --sku Standard_LRS
      
    • Create a Blob Container:

        az storage container create --name terraformstate --account-name mystorageaccount
      

    Step 2: Enable Versioning on the Storage Account

    az storage account blob-service-properties update --account-name mystorageaccount --enable-change-feed true --enable-versioning true
    

    Step 3: Get Access Key for the Storage Account

    az storage account keys list --resource-group myResourceGroup --account-name mystorageaccount --query "[0].value" --output tsv
    

    Note the output, as it will be the access key you use in your backend.tf configuration.

    Step 4: Create backend.tf

    Create a backend.tf file in your Terraform configuration directory with the following content:

    terraform {
      backend "azurerm" {
        resource_group_name   = "myResourceGroup"
        storage_account_name  = "mystorageaccount"
        container_name        = "terraformstate"
        key                   = "terraform.tfstate" # This is the name of the state file
      }
    }
    

    Step 5: Initialize Terraform with the Backend Configuration

    Run the following command to initialize the backend configuration:

    terraform init
    

    Terraform will prompt you for the storage account access key. You can either provide it interactively or set it as an environment variable:

    export ARM_ACCESS_KEY="your_access_key_here"
    

    Then run terraform init again to initialize the backend configuration.

  5. Create a Dockerfile for your frontend and backend. Place dockerfile in the root frontend and backend folder separtly.

    I have created multi-staging Dockerfile to save build space and has many other advantages.

    a) Dockerfile for frontend

    # Stage 1: Build the React app
    FROM node:16.15.0 AS build
    WORKDIR /frontend
    COPY package*.json ./
    RUN npm install
    COPY . ./
    RUN npm run build
    
    # Stage 2: Serve the app with Node.js
    FROM node:16.15.0-alpine
    WORKDIR /app
    COPY --from=build /frontend/build /app/build
    RUN npm install -g serve
    EXPOSE 3000 
    CMD ["serve", "-s", "build", "-l", "3000" ]
    

    b) Dockerfile for backend

    # Stage 1: Build stage
    FROM node:16.15.0 AS build
    WORKDIR /backend
    COPY package*.json ./
    RUN npm install
    COPY . .
    
    # Stage 2: Runtime stage
    FROM node:16.15.0-alpine
    WORKDIR /backend
    COPY --from=build /backend /backend
    EXPOSE 5001
    CMD [ "node", "Server.js" ]
    
  6. Create CICD for Azure DevOps. I have taken YAML approch and save it as azure-pipeline.yaml.

    # Build and push an image to Azure Container Registry
    # https://docs.microsoft.com/azure/devops/pipelines/languages/docker
    
    trigger:
      - DevopsStage
    
    resources:
    - repo: self
    
    variables:
      # Container registry service connection established during pipeline creation
      ContainerApp_env: 'UAEnv'
      ContainerApp_frontend_name: 'uafrontend'
      ContainerApp_backend_name: 'uabackend'
      rg: 'High-UA-RG'
      frontendimageRepository: 'frontend'
      backendimageRepository: 'backend'
      containerRegistry: 'high5.azurecr.io'
      frontend_dockerfilePath: '$(Build.SourcesDirectory)/Frontend/dockerfile'
      backend_dockerfilePath: '$(Build.SourcesDirectory)/Backend/dockerfile'
      tag: '$(Build.BuildId)'
    
      # Agent VM image name
      vmImageName: 'ubuntu-latest'
    
    pool:
      vmImage: $(vmImageName)
    
    stages:
    - stage: Frontend
      displayName: Frontend
      jobs:
      - job: Build_Frontend
        displayName: Frontend
    
        steps:
    
        - script: npm install --force
          workingDirectory: Frontend
          displayName: Install Node.js
    
        - task: Npm@1
          displayName: Jest Unit Testing
          inputs:
            command: 'custom'
            customCommand: 'run test'   
            workingDir: Frontend
    
        - task: Docker@2
          displayName: Image creation
          inputs:
            containerRegistry: 'high5'
            repository: '$(frontendimageRepository)'
            command: 'build'
            Dockerfile: '$(frontend_dockerfilePath)'
            tags: '$(tag)'
    
        - task: Docker@2
          displayName: Image push
          inputs:
            containerRegistry: 'high5'
            repository: '$(frontendimageRepository)'
            command: 'push'
            tags: '$(tag)'
    
        - task: AzureContainerApps@1
          displayName: Deploying to ContainerApp '$(ContainerApp_frontend_name)'
          inputs:
            azureSubscription: 'ARM'
            imageToDeploy: '$(containerRegistry)/$(frontendimageRepository):$(tag)'
            containerAppName: '$(ContainerApp_frontend_name)'
            resourceGroup: '$(rg)'
            containerAppEnvironment: '$(ContainerApp_env)'
            targetPort: '3000'
            ingress: 'external'
    
    
    - stage: Backend
      displayName: Backend
      jobs:
      - job: Build_Backend
        displayName: Backend
    
        steps:
    
        - task: NodeTool@0
          displayName: Use Node Version
          inputs:
            versionSpec: 16.15.0
    
        - script: npm install
          workingDirectory: Backend
          displayName: Install Node.js
    
        - task: Npm@1
          displayName: Jest Unit Testing
          inputs:
            command: 'custom'
            customCommand: 'run test'  
            workingDir: Backend
    
        - task: Docker@2
          displayName: Image creation
          inputs:
            containerRegistry: 'high5'
            repository: '$(backendimageRepository)'
            command: 'build'
            Dockerfile: '$(backend_dockerfilePath)'
            tags: '$(tag)'
    
        - task: Docker@2
          displayName: Image push
          inputs:
            containerRegistry: 'high5'
            repository: '$(backendimageRepository)'
            command: 'push'
            tags: '$(tag)'
    
        - task: AzureContainerApps@1
          displayName: Deploying to ContainerApp '$(ContainerApp_backend_name)'
          inputs:
            azureSubscription: 'ARM'
            imageToDeploy: '$(containerRegistry)/$(backendimageRepository):$(tag)'
            containerAppName: '$(ContainerApp_backend_name)'
            resourceGroup: '$(rg)'
            containerAppEnvironment: '$(ContainerApp_env)'
            targetPort: '5001'
            ingress: 'external'
    

    Make sure you have created the "service connections" in Azure devops for acr as high5repo as shown in CICD YAML and azure subscription named as ARM as shown in CICD YAML.

    Specify the name which you have given to your Container Registry and Azure Subscription in service connection.

    You can Add Service connection by going to the location- Azure devops > Choose the organisation > Choose the project > Go to project setting > Go to service connections > New service connection.

  7. To make sure pipeline works, go to the portal.azure.com after running terraform and creating the infra. Follow below steps to check infra and do some changes.

    a) Check all resources are created successfully.

    b) Go to Container registry which you created and navigate to Access keys and make sure Admin user is checked.

    c) Now go to Container environment devenv in my case and choose and click on container app devfrontend.

    Navigate to Containers and click on Edit and deploy.

    Click on the container and edit it with correct repo and image with managed identity. Also edit the CPU and memory and save it.

    Go to Scale and choose the desired scaling option based on traffic. Container app that I have chosen is serverless so it can scale down to 0 when not active. And we will be charged based on our usage.

    Now go to Revisions and Replicas, You can see 3 options,

    • Active revisions - This will show all the active containers

    • Inactive revisions - This will show all the inactive containers which you can activate if you want to rollback.

    • Replicas - And this will show number of replicas with restart count

    Now Click on Overview and click on Application URL to check if application works and also if you want you can create a custom domain placed under setting.

    If you want to check logs you can click on logstream and also you can view matrics by following the below screenshot.

    d) Follow the above steps for backend container app as well.