← Back to Blog

Creating an Azure App Service Environment with Terraform

AzureTerraformIaCDevOps

Creating an Azure App Service Environment with Terraform

Hey there! So, you're looking to set up a reliable hosting environment for your web apps? Well, you're in the right place. Today, I'm going to walk you through creating an Azure App Service environment using Terraform. Trust me, it's not as scary as it sounds, and I'll break it down into bite-sized pieces.

Prerequisites

Before we dive in, let's make sure you've got all the tools you need:

  • Terraform installed (version 1.0.0+) - don't worry if you're new to this, it's pretty straightforward
  • Azure CLI configured - if you haven't done this yet, I've been there, it takes about 5 minutes
  • An active Azure subscription - can't do much without this one!

Project Structure

First things first, let's set up our Terraform project structure. Nothing fancy here, just a simple folder with a few files:

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

Provider Configuration

Let's kick things off with our provider configuration in main.tf. This is just telling Terraform that we want to work with Azure:

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

provider "azurerm" {
  features {}
}

Resource Group and App Service Plan

Next up, we'll create a resource group and an App Service Plan. Think of the resource group as a container for all your Azure stuff, and the App Service Plan as the server that'll run your app:

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 for the main event - creating the actual App Service. This is where your app will live. I've included some best practices here that I've learned the hard way (trust me, you'll thank me later):

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. Nothing too exciting, just defining the variables we'll need:

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. This is just so we can see some useful information after our deployment is done:

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

I've built in several security best practices here (because nobody wants to be on the front page of Hacker News for the wrong reasons):

  1. TLS Configuration: We're enforcing TLS 1.2 minimum - because it's 2024, and security matters!
  2. FTPS Disabled: We're disabling FTPS to reduce attack surface - one less thing to worry about
  3. Managed Identity: We're using system-assigned managed identity - way better than storing secrets
  4. HTTP/2 Enabled: We've got HTTP/2 for better performance - your users will thank you
  5. VNet Integration Ready: We're enabling VNet routing for network isolation - because who doesn't love a good network boundary?

Applying the Configuration

Ready to make this happen? Here's how:

  1. Initialize Terraform:
terraform init
  1. See what changes will be made:
terraform plan
  1. Make it so!
terraform apply

Conclusion

And there you have it! You've just deployed an Azure App Service using Terraform. It wasn't so bad, right? This setup gives you a solid foundation that you can build upon for your specific application needs.

I've used this setup for several personal projects and learning experiments, and it's worked great. Feel free to tweak it to match what you're trying to build. Happy deploying!