Infrastructure as Code with Terraform

Infrastructure as Code with Terraform

Why Infrastructure as Code?

Infrastructure as Code (IaC) is the fundamental pillar of modern DevOps. Instead of manually configuring servers through graphical interfaces, we describe infrastructure in versioned configuration files.

Key advantages

  • Reproducibility: a single file always produces the same infrastructure
  • Version control: every change is tracked in Git
  • Code review: infrastructure changes go through pull requests
  • Automation: deployment with a single terraform apply
  • Living documentation: the code is the documentation

Terraform: advanced fundamentals

Providers and resources

Terraform uses providers to interact with cloud APIs. Each provider exposes resources and data sources.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    bucket = "mon-terraform-state"
    key    = "prod/infrastructure.tfstate"
    region = "eu-west-3"
  }
}

provider "aws" {
  region = var.aws_region
}

Reusable modules

A Terraform module encapsulates a set of resources into a reusable component:

module "vpc" {
  source = "./modules/vpc"

  cidr_block         = "10.0.0.0/16"
  availability_zones = ["eu-west-3a", "eu-west-3b", "eu-west-3c"]
  environment        = var.environment

  tags = local.common_tags
}

module "eks_cluster" {
  source = "./modules/eks"

  cluster_name    = "prod-cluster"
  vpc_id          = module.vpc.vpc_id
  subnet_ids      = module.vpc.private_subnet_ids
  node_count      = 3
  instance_type   = "t3.xlarge"

  depends_on = [module.vpc]
}

State management and workspaces

The state is the file that maintains the mapping between your code and real resources. In a team setting, it must be stored in a remote backend.

# Using workspaces to manage multiple environments
# terraform workspace new staging
# terraform workspace new production

resource "aws_instance" "app" {
  count         = terraform.workspace == "production" ? 3 : 1
  ami           = data.aws_ami.ubuntu.id
  instance_type = terraform.workspace == "production" ? "t3.xlarge" : "t3.medium"

  tags = {
    Environment = terraform.workspace
  }
}

Advanced patterns

Loops and dynamic expressions

variable "services" {
  type = map(object({
    port     = number
    replicas = number
    cpu      = string
    memory   = string
  }))

  default = {
    api = { port = 3000, replicas = 3, cpu = "500m", memory = "512Mi" }
    web = { port = 4200, replicas = 2, cpu = "250m", memory = "256Mi" }
    worker = { port = 0, replicas = 1, cpu = "1000m", memory = "1Gi" }
  }
}

resource "kubernetes_deployment" "service" {
  for_each = var.services

  metadata {
    name = each.key
    labels = {
      app = each.key
    }
  }

  spec {
    replicas = each.value.replicas

    selector {
      match_labels = {
        app = each.key
      }
    }

    template {
      spec {
        container {
          name  = each.key
          image = "${var.registry}/${each.key}:${var.image_tag}"

          resources {
            requests = {
              cpu    = each.value.cpu
              memory = each.value.memory
            }
          }
        }
      }
    }
  }
}

Secret management with Vault

data "vault_generic_secret" "db_credentials" {
  path = "secret/data/production/database"
}

resource "aws_db_instance" "main" {
  engine         = "postgres"
  engine_version = "15.4"
  instance_class = "db.r6g.xlarge"
  username       = data.vault_generic_secret.db_credentials.data["username"]
  password       = data.vault_generic_secret.db_credentials.data["password"]

  storage_encrypted = true
  multi_az          = true

  backup_retention_period = 30
  deletion_protection     = true
}

Best practices

  1. Always use a remote backend for state (S3, GCS, Azure Blob)
  2. Lock provider and module versions
  3. Separate environments with workspaces or distinct directories
  4. Always run terraform plan before apply
  5. Never store secrets in plain text in .tf files
  6. Structure as modules to reuse and test infrastructure
  7. Integrate Terraform into CI/CD with automatic plans on PRs