← Back to Blog

Creating an Azure App Service Environment with Terraform

AzureTerraformIaCDevOps

Creating an Azure App Service Environment with Terraform

As a DevOps engineer, one of the most common tasks is setting up reliable and secure hosting environments for web applications. Today, I'll walk you through creating an Azure App Service environment using Terraform, complete with all the necessary components for a production-ready setup.

Prerequisites

Before we begin, make sure you have:

  • Terraform installed (version 1.0.0+)
  • Azure CLI configured
  • An active Azure subscription

Project Structure

First, let's set up our Terraform project structure:

azure-app-service/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Provider Configuration

Let's start with our provider configuration in main.tf:

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}

provider "azurerm" {
  features {}
}

Resource Group and App Service Plan

Next, we'll create a resource group and an App Service Plan:

resource "azurerm_resource_group" "main" {
  name     = var.resource_group_name
  location = var.location

  tags = {
    Environment = var.environment
    Managed_By  = "Terraform"
  }
}

resource "azurerm_service_plan" "main" {
  name                = "${var.project_name}-plan"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  os_type            = "Linux"
  sku_name           = var.sku_name

  tags = {
    Environment = var.environment
    Managed_By  = "Terraform"
  }
}

App Service Configuration

Now, let's create the App Service with some best practices configurations:

resource "azurerm_linux_web_app" "main" {
  name                = "${var.project_name}-app"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  service_plan_id     = azurerm_service_plan.main.id

  site_config {
    application_stack {
      node_version = "18-lts"
    }
    
    always_on                = true
    ftps_state              = "Disabled"
    http2_enabled           = true
    minimum_tls_version     = "1.2"
    vnet_route_all_enabled  = true
  }

  app_settings = {
    "WEBSITE_RUN_FROM_PACKAGE"    = "1"
    "WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"
    "WEBSITE_NODE_DEFAULT_VERSION" = "~18"
    "NODE_ENV"                    = var.environment
  }

  identity {
    type = "SystemAssigned"
  }

  logs {
    application_logs {
      file_system_level = "Information"
    }

    http_logs {
      file_system {
        retention_in_days = 7
        retention_in_mb   = 35
      }
    }
  }

  tags = {
    Environment = var.environment
    Managed_By  = "Terraform"
  }
}

Variables Configuration

Here's our variables.tf file:

variable "project_name" {
  type        = string
  description = "The name of the project"
}

variable "environment" {
  type        = string
  description = "The environment (dev, staging, prod)"
}

variable "location" {
  type        = string
  description = "The Azure region to deploy to"
  default     = "eastus2"
}

variable "resource_group_name" {
  type        = string
  description = "The name of the resource group"
}

variable "sku_name" {
  type        = string
  description = "The SKU name for the App Service Plan"
  default     = "P1v2"
}

Output Configuration

And finally, our outputs.tf:

output "app_service_name" {
  value = azurerm_linux_web_app.main.name
}

output "app_service_default_hostname" {
  value = "https://${azurerm_linux_web_app.main.default_hostname}"
}

output "app_service_identity_principal_id" {
  value = azurerm_linux_web_app.main.identity[0].principal_id
}

Security Considerations

In this configuration, we've implemented several security best practices:

  1. TLS Configuration: We enforce TLS 1.2 minimum
  2. FTPS Disabled: We disable FTPS to reduce attack surface
  3. Managed Identity: We use system-assigned managed identity
  4. HTTP/2 Enabled: We enable HTTP/2 for better performance
  5. VNet Integration Ready: We enable VNet routing for network isolation

Applying the Configuration

To use this configuration:

  1. Initialize Terraform:
terraform init
  1. Create a terraform.tfvars file:
project_name        = "myproject"
environment         = "production"
location           = "eastus2"
resource_group_name = "myproject-rg"
sku_name           = "P1v2"
  1. Plan and apply:
terraform plan
terraform apply

Conclusion

This Terraform configuration creates a production-ready Azure App Service environment with security best practices, monitoring, and proper resource organization. The configuration is modular and can be easily customized for different environments or requirements.

Remember to always review the generated plan before applying it to your infrastructure, and consider adding additional security measures like VNet integration, Azure Front Door, or Web Application Firewall depending on your specific needs.

Feel free to reach out if you have any questions or need clarification on any part of this setup!