TerraformPilot

Terraform

Azure App Service with Terraform - Deploy Web Apps

Deploy Azure App Service web apps with Terraform. Linux and Windows apps, deployment slots, custom domains, SSL certificates, and autoscaling.

LLuca Berton1 min read

Quick Answer

#
resource "azurerm_service_plan" "main" {
  name                = "my-plan"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  os_type             = "Linux"
  sku_name            = "B1"
}
 
resource "azurerm_linux_web_app" "main" {
  name                = "my-web-app"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  service_plan_id     = azurerm_service_plan.main.id
 
  site_config {
    application_stack {
      node_version = "20-lts"
    }
  }
}

Production Web App

#
resource "azurerm_resource_group" "main" {
  name     = "${var.project}-${var.environment}-rg"
  location = var.location
}
 
resource "azurerm_service_plan" "main" {
  name                = "${var.project}-${var.environment}-plan"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  os_type             = "Linux"
  sku_name            = "P1v3"
 
  tags = { Environment = var.environment }
}
 
resource "azurerm_linux_web_app" "api" {
  name                = "${var.project}-${var.environment}-api"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  service_plan_id     = azurerm_service_plan.main.id
  https_only          = true
 
  site_config {
    always_on        = true
    minimum_tls_version = "1.2"
    ftps_state       = "Disabled"
    health_check_path = "/health"
 
    application_stack {
      docker_image_name   = "${var.project}/api:${var.image_tag}"
      docker_registry_url = "https://${azurerm_container_registry.main.login_server}"
    }
  }
 
  app_settings = {
    ENVIRONMENT                    = var.environment
    WEBSITES_ENABLE_APP_SERVICE_STORAGE = "false"
    DOCKER_REGISTRY_SERVER_URL     = "https://${azurerm_container_registry.main.login_server}"
    DOCKER_REGISTRY_SERVER_USERNAME = azurerm_container_registry.main.admin_username
    DOCKER_REGISTRY_SERVER_PASSWORD = azurerm_container_registry.main.admin_password
  }
 
  identity {
    type = "SystemAssigned"
  }
 
  logs {
    http_logs {
      file_system {
        retention_in_days = 7
        retention_in_mb   = 35
      }
    }
    application_logs {
      file_system_level = "Warning"
    }
  }
 
  tags = { Environment = var.environment }
}

Deployment Slots (Blue-Green)

#
resource "azurerm_linux_web_app_slot" "staging" {
  name           = "staging"
  app_service_id = azurerm_linux_web_app.api.id
 
  site_config {
    application_stack {
      docker_image_name   = "${var.project}/api:${var.staging_tag}"
      docker_registry_url = "https://${azurerm_container_registry.main.login_server}"
    }
  }
 
  app_settings = azurerm_linux_web_app.api.app_settings
}
 
# Swap slots for zero-downtime deployment
# az webapp deployment slot swap -g rg -n app --slot staging

Custom Domain and SSL

#
resource "azurerm_app_service_custom_hostname_binding" "main" {
  hostname            = "api.${var.domain_name}"
  app_service_name    = azurerm_linux_web_app.api.name
  resource_group_name = azurerm_resource_group.main.name
}
 
resource "azurerm_app_service_managed_certificate" "main" {
  custom_hostname_binding_id = azurerm_app_service_custom_hostname_binding.main.id
}
 
resource "azurerm_app_service_certificate_binding" "main" {
  hostname_binding_id = azurerm_app_service_custom_hostname_binding.main.id
  certificate_id      = azurerm_app_service_managed_certificate.main.id
  ssl_state           = "SniEnabled"
}

SKU Comparison

#
SKUvCPURAMFeaturesCost/month
F1Shared1 GBFree tier, no custom domainFree
B111.75 GBCustom domains, SSL~$13
S111.75 GB+ Slots, autoscale~$70
P1v328 GB+ VNet, more slots~$115
#

Conclusion

#

Use Linux App Service with Docker containers for production, deployment slots for zero-downtime deploys, managed certificates for free SSL, and system-assigned managed identity for secure access to other Azure services. Always set https_only = true and ftps_state = "Disabled".

#Terraform#Azure#App Service#Infrastructure as Code

Share this article