Enterprise Azure Terraform Lab Phase 1 Part 1: Foundation and Remote State

๐Ÿ“… Created: 2026-04-24 | โฑ๏ธ Read Time: 7 mins

๐Ÿ“ GitHub Repository: lwc-wk/enterprise-azure-lab1

Enterprise Azure Terraform Lab Phase 1 Part 1: Foundation and Remote State

๐Ÿงฉ Background

This lab is not about building a fancy frontend application.

The real goal is to prove that I can design, deploy, explain, validate, and clean up Azure infrastructure using Terraform.

The core idea is simple:

Fake UI is acceptable. Fake architecture is not.

The React dashboard can stay simple. The infrastructure behind it must be real, explainable, repeatable, and cost-aware.

This Phase 1 lab simulates a small enterprise-style Azure environment for an internal business application. It focuses on:

  • Resource organization
  • Terraform Infrastructure as Code
  • Remote state
  • Virtual networking
  • Subnet segmentation
  • Storage Account security
  • Monitoring foundation
  • App Service hosting
  • Budget alerts
  • Basic Azure RBAC
  • Destroy-after-validation discipline

Screenshot


๐Ÿง  Design Philosophy

Before writing Terraform, I defined the engineering principles first.

Least Privilege

Nobody should receive more access than required.

In Phase 1, this means avoiding broad Owner or Contributor access unless absolutely necessary.

Segmentation

Different functions should be separated.

Even though this is a low-cost lab, the network is designed with separate subnets:

  • Application subnet
  • Management subnet

This keeps the design simple while still showing enterprise-style thinking.

Auditability

I should be able to explain:

  • What exists
  • Why it exists
  • Who can access it
  • How it is monitored
  • How it would change in production

Repeatability

The lab must be deployable, testable, and destroyable with Terraform.

terraform init
terraform fmt
terraform validate
terraform plan
terraform apply
terraform destroy

Low Cost

This lab intentionally avoids unnecessary expensive services.

Phase 1 does not use:

  • Virtual Machines
  • Bastion
  • Load Balancer
  • Azure Firewall
  • Always-on paid tiers unless temporarily needed

Paid tiers are only used briefly for validation screenshots, then scaled down or destroyed.


๐Ÿ—๏ธ Architecture Model

The lab is split into two layers.

Permanent Layer

This layer stores Terraform state and should normally stay alive.

It is created manually first with Azure CLI.

rg-tfstate-enterprise-lab
โ””โ”€โ”€ Storage Account
    โ””โ”€โ”€ Blob container: tfstate

Why this exists:

  • Terraform needs a safe place to store state.
  • Local terraform.tfstate is fragile.
  • The backend should survive lab destroy operations.
  • It separates long-lived state infrastructure from short-lived lab infrastructure.

Screenshot Screenshot

Ephemeral Lab Layer

This is the actual portfolio lab.

It can be deployed when needed and destroyed afterward.

rg-infra-dev-azlab2
โ”œโ”€โ”€ Virtual Network
โ”‚   โ”œโ”€โ”€ subnet-app
โ”‚   โ””โ”€โ”€ subnet-management
โ”œโ”€โ”€ Network Security Group
โ”œโ”€โ”€ Storage Account
โ”œโ”€โ”€ Log Analytics Workspace
โ”œโ”€โ”€ App Service Plan
โ”œโ”€โ”€ Azure Linux Web App
โ”œโ”€โ”€ Diagnostic Settings
โ”œโ”€โ”€ Resource Group Budget
โ””โ”€โ”€ Optional RBAC Assignments

This layer is designed to be disposable.

Screenshot Screenshot


๐Ÿ“ Repository Structure

The repository is organized to separate app code, Terraform code, environment variables, screenshots, and helper scripts.

enterprise-azure-terraform-lab/
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ architecture.md
โ”œโ”€โ”€ SOUL.md
โ”œโ”€โ”€ app/
โ”‚   โ””โ”€โ”€ React + Vite + TypeScript dashboard
โ”œโ”€โ”€ docs/
โ”‚   โ”œโ”€โ”€ screenshots/
โ”‚   โ””โ”€โ”€ diagrams/
โ”œโ”€โ”€ env/
โ”‚   โ”œโ”€โ”€ dev.tfvars
โ”‚   โ”œโ”€โ”€ demo.tfvars
โ”‚   โ”œโ”€โ”€ local.secrets.tfvars      # not committed
โ”‚   โ””โ”€โ”€ local.rbac.tfvars         # not committed
โ”œโ”€โ”€ infra/
โ”‚   โ”œโ”€โ”€ backend.tf
โ”‚   โ”œโ”€โ”€ versions.tf
โ”‚   โ”œโ”€โ”€ providers.tf
โ”‚   โ”œโ”€โ”€ locals.tf
โ”‚   โ”œโ”€โ”€ generic-input-variables.tf
โ”‚   โ”œโ”€โ”€ random-resources.tf
โ”‚   โ”œโ”€โ”€ resource-group.tf
โ”‚   โ”œโ”€โ”€ network.tf
โ”‚   โ”œโ”€โ”€ security.tf
โ”‚   โ”œโ”€โ”€ storage.tf
โ”‚   โ”œโ”€โ”€ monitoring.tf
โ”‚   โ”œโ”€โ”€ diagnostics.tf
โ”‚   โ”œโ”€โ”€ app-service.tf
โ”‚   โ”œโ”€โ”€ budget.tf
โ”‚   โ”œโ”€โ”€ rbac.tf
โ”‚   โ”œโ”€โ”€ outputs.tf
โ”‚   โ”œโ”€โ”€ autoscale.tf.disabled
โ”‚   โ””โ”€โ”€ app-service-slot.tf.disabled
โ””โ”€โ”€ scripts/
    โ”œโ”€โ”€ init-backend.sh
    โ”œโ”€โ”€ deploy-infra.sh
    โ”œโ”€โ”€ deploy-app.sh
    โ””โ”€โ”€ destroy.sh

๐Ÿงพ Why Split Terraform Files?

The Terraform code is split by responsibility.

File Purpose
versions.tf Terraform and provider version requirements
providers.tf AzureRM provider configuration
backend.tf Remote state backend configuration
generic-input-variables.tf External configuration variables
locals.tf Naming prefix and common tags
random-resources.tf Random suffixes for globally unique names
resource-group.tf Azure Resource Group
network.tf VNet and subnets
security.tf NSG and subnet associations
storage.tf Storage Account
monitoring.tf Log Analytics Workspace
diagnostics.tf Diagnostic settings
app-service.tf App Service Plan and Linux Web App
budget.tf Resource Group budget alert
rbac.tf Optional Azure RBAC role assignments
outputs.tf Terraform outputs

This makes the code easier to understand, maintain, and explain in an interview.


๐Ÿงฑ Terraform Backend

Terraform backend is created manually first.

The backend uses:

  • Resource Group
  • Storage Account
  • Blob container

Example backend configuration:

terraform {
  backend "azurerm" {
    resource_group_name  = "rg-tfstate-enterprise-lab"
    storage_account_name = "sttfstatexxxxx"
    container_name       = "tfstate"
    key                  = "enterprise-azure-lab-dev.tfstate"
  }
}

Important lesson:

The backend is not part of the destroyable lab layer.

The backend Resource Group should stay:

rg-tfstate-enterprise-lab

The lab Resource Group can be destroyed:

rg-infra-dev-azlab2

๐Ÿท๏ธ Variables, Locals, and Naming

Environment-specific values are placed in env/dev.tfvars.

business_division       = "infra"
environment             = "dev"
project_name            = "azlab2"
resource_group_location = "centralus"
owner                   = "lw"

monthly_budget_amount = 10
budget_start_date     = "2026-05-01T00:00:00Z"

During testing, the first Resource Group had partial deployment and App Service capacity issues. Changing the project name to azlab2 forced Terraform to create a clean resource naming set.

Example naming pattern:

infra-dev-azlab2

Resources become:

rg-infra-dev-azlab2
vnet-infra-dev-azlab2
nsg-infra-dev-azlab2
law-infra-dev-azlab2
asp-infra-dev-azlab2
app-infra-dev-azlab2-xxxxxx

Common tags are used for governance and cost awareness.

common_tags = {
  Project     = "enterprise-azure-lab"
  Environment = var.environment
  Owner       = var.owner
  ManagedBy   = "terraform"
  CostControl = "destroy-after-validation"
}

๐Ÿงน Resource Group as Lifecycle Boundary

The Resource Group is the lifecycle boundary of the lab.

It groups all ephemeral lab resources together.

This makes cleanup simple:

terraform destroy -var-file="../env/dev.tfvars"

๐Ÿงพ Summary

In this first part, I built the foundation of the lab:

  • Defined the architecture principles
  • Separated permanent backend resources from disposable lab resources
  • Created a Terraform remote state backend
  • Organized Terraform files by responsibility
  • Standardized naming and tags
  • Used the Resource Group as the lifecycle and cost boundary

This is the base layer. Without this, the rest of the lab would just be random Azure clicking with better lighting.