Mark Lowel Montealto — Full Stack Developer & DevOps Engineer

Mark Lowel
Montealto

Full Stack Developer & DevOps Engineer

Blog

June 2025

·

2 min read

Infrastructure as Code with Terraform: Your First AWS Deployment

Get started with Terraform: write your first HCL configuration, deploy AWS resources, understand state, and follow the plan/apply workflow — no prior IaC experience needed.

Introduction

  • Infrastructure as Code (IaC): define cloud resources in version-controlled files instead of clicking through consoles.
  • Terraform: the most widely used IaC tool — provider-agnostic, declarative, state-driven.
  • Goal: deploy a real AWS resource (S3 bucket + CloudFront distribution) by the end of this article.

Installing Terraform

# macOS
brew tap hashicorp/tap && brew install hashicorp/tap/terraform
 
# Verify
terraform version
  • Recommended: use tfenv for version management across projects (like nvm for Node).

HCL Basics: Providers, Resources, Variables, Outputs

Provider

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
 
provider "aws" {
  region = "ap-southeast-1"
}

Resource

resource "aws_s3_bucket" "assets" {
  bucket = "my-app-assets-prod"
 
  tags = {
    Project     = "my-app"
    Environment = "production"
  }
}

Variable

variable "environment" {
  type    = string
  default = "production"
}
 
variable "bucket_name" {
  type = string
}

Output

output "bucket_arn" {
  value = aws_s3_bucket.assets.arn
}

The Plan → Apply Workflow

# Initialise — download providers
terraform init
 
# Preview changes (no-op)
terraform plan
 
# Apply changes (prompts for confirmation)
terraform apply
 
# Destroy all resources (careful!)
terraform destroy
  • terraform plan shows + (create), ~ (modify), - (destroy) — review before every apply.
  • Use terraform apply -auto-approve in CI/CD only, never locally.

Terraform State

  • State file (terraform.tfstate) is Terraform's source of truth about what's deployed.
  • Never commit state to git — it may contain secrets.
  • Remote state (see Module article): store in S3 + DynamoDB locking for team use.

First Real Deployment: S3 + CloudFront

# main.tf
resource "aws_s3_bucket" "site" {
  bucket = var.bucket_name
}
 
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" "default" {
  name                              = "s3-oac"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}
 
resource "aws_cloudfront_distribution" "cdn" {
  enabled = true
 
  origin {
    domain_name              = aws_s3_bucket.site.bucket_regional_domain_name
    origin_id                = "s3-site"
    origin_access_control_id = aws_cloudfront_origin_access_control.default.id
  }
 
  default_cache_behavior {
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    target_origin_id       = "s3-site"
    forwarded_values {
      query_string = false
      cookies { forward = "none" }
    }
  }
 
  restrictions { geo_restriction { restriction_type = "none" } }
  viewer_certificate { cloudfront_default_certificate = true }
}
 
output "cloudfront_domain" {
  value = aws_cloudfront_distribution.cdn.domain_name
}

Variables File and .tfvars

# terraform.tfvars (gitignored for secrets; safe for non-sensitive config)
bucket_name = "my-app-assets-prod"
environment = "production"

Common Commands

CommandPurpose
terraform initInitialise providers/modules
terraform planPreview changes
terraform applyApply changes
terraform outputShow output values
terraform showInspect state
terraform fmtFormat HCL files
terraform validateSyntax/logic check

What Not to Do as a Beginner

  • Don't manually edit terraform.tfstate.
  • Don't terraform destroy in production without a backup plan.
  • Don't use wildcards in IAM policies generated by Terraform — scope them (see IAM article).
  • Don't store secrets in .tfvars committed to git — use AWS Secrets Manager or Vault.

Conclusion

  • Terraform replaces the AWS Console for infrastructure management — reproducible, reviewable, revertable.
  • The plan output is your most valuable safety net: read it every time.
  • Next steps: structure multi-environment deployments with workspaces or separate state files, extract reusable modules (see: Terraform Modules article).