iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Terraform on AWS

Terraform is the de facto cross-cloud IaC. For AWS specifically it gives you the same provider as everyone else, predictable plan/apply, and a rich module ecosystem.

Terraform on AWS - first stack

EXAMPLE
# providers.tf
terraform {
  required_version = '>= 1.7'
  required_providers {
    aws = { source = 'hashicorp/aws', version = '~> 5.40' }
  }
  backend 's3' {
    bucket         = 'tfstate-myapp'
    key            = 'prod/terraform.tfstate'
    region         = 'ap-southeast-2'
    dynamodb_table = 'tfstate-lock'
    encrypt        = true
  }
}

provider 'aws' {
  region = 'ap-southeast-2'
  default_tags {
    tags = {
      Environment = var.env
      ManagedBy   = 'terraform'
      Owner       = 'platform'
    }
  }
}

# variables.tf
variable 'env'         { type = string; default = 'prod' }
variable 'domain_name' { type = string }
variable 'cert_arn'    { type = string }

# main.tf - static site bucket + CF distribution
resource 'aws_s3_bucket' 'site' {
  bucket = var.domain_name
}

resource 'aws_s3_bucket_ownership_controls' 'site' {
  bucket = aws_s3_bucket.site.id
  rule { object_ownership = 'BucketOwnerEnforced' }
}

resource 'aws_s3_bucket_public_access_block' 'site' {
  bucket                  = aws_s3_bucket.site.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource 'aws_cloudfront_origin_access_control' 'oac' {
  name                              = '${var.domain_name}-oac'
  origin_access_control_origin_type = 's3'
  signing_behavior                  = 'always'
  signing_protocol                  = 'sigv4'
}

resource 'aws_cloudfront_distribution' 'site' {
  enabled             = true
  default_root_object = 'index.html'
  aliases             = [var.domain_name]

  origin {
    domain_name              = aws_s3_bucket.site.bucket_regional_domain_name
    origin_id                = 's3'
    origin_access_control_id = aws_cloudfront_origin_access_control.oac.id
    s3_origin_config { origin_access_identity = '' }
  }

  default_cache_behavior {
    target_origin_id       = 's3'
    viewer_protocol_policy = 'redirect-to-https'
    allowed_methods        = ['GET', 'HEAD']
    cached_methods         = ['GET', 'HEAD']
    cache_policy_id        = '658327ea-f89d-4fab-a63d-7e88639e58f6'
  }

  restrictions { geo_restriction { restriction_type = 'none' } }

  viewer_certificate {
    acm_certificate_arn      = var.cert_arn
    ssl_support_method       = 'sni-only'
    minimum_protocol_version = 'TLSv1.2_2021'
  }
}

# outputs.tf
output 'distribution_id'   { value = aws_cloudfront_distribution.site.id }
output 'distribution_url'  { value = aws_cloudfront_distribution.site.domain_name }

# Workflow
# terraform fmt -recursive
# terraform validate
# terraform plan -out tf.plan
# terraform apply tf.plan

Why it matters

Treat Terraform state like production data - remote backend, locking, encrypted at rest, never committed. Use modules for repeated patterns (a project per env, not a workspace), pin provider versions, and review every plan before apply.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
resource "aws_s3_bucket" "main" {
    bucket = "my-bucket"
}
Try it Yourself »

Discussion

Loading…