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.
Discussion
Loading…