Back to Blog

Automating Serverless Infrastructure with Terraform & GitHub Actions

How I migrated a manual AWS serverless backend into 100% version-controlled Infrastructure as Code (IaC) with remote state locking, pull-request automation, and seamless CloudFront same-origin routing.

1. Architectural Overview

To eliminate manual AWS Console configurations ("clickops"), I codified the entire portfolio infrastructure using HashiCorp Terraform. All backend resources are defined in declarative HCL files, managed via an encrypted remote state backend, and automatically deployed through a GitHub Actions CI/CD pipeline.

2. Remote State & Concurrent Lock Safety

To prevent state loss and ensure execution lock safety during pipeline runs, state is stored in S3 and locked using DynamoDB.

  • S3 Backend Bucket: Encrypted storage with versioning enabled for state recovery.
  • DynamoDB Lock Table: Primary key LockID prevents concurrent pipeline runs from corrupting state.
# backend.tf
terraform {
  backend "s3" {
    bucket         = "alain-puron-terraform-state-bucket-2026"
    key            = "global/s3/terraform.tfstate"
    region         = "us-west-2"
    dynamodb_table = "alain-puron-terraform-state-locks-2026"
    encrypt        = true
  }
}

3. Continuous Integration & Deployment Pipeline

Every code modification follows a strict Pull Request workflow. Pushing to a feature branch triggers terraform plan in GitHub Actions to preview resource changes. Merging into main triggers terraform apply automatically.

# .github/workflows/terraform.yml snippet
- name: Terraform Plan
  if: github.event_name == 'pull_request'
  run: terraform plan -no-color

- name: Terraform Apply
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  run: terraform apply -auto-approve

4. Codified Serverless Stack

The entire serverless core is managed in modular Terraform files:

  • dynamodb.tf: On-demand pay-per-request visitor counter table.
  • iam.tf: Least-privilege execution roles for AWS Lambda, SES, and DynamoDB.
  • lambda.tf: Automated zip archiving and deployment of Python handler scripts.
  • api_gateway.tf: HTTP API Gateway routes (GET / and POST /api/contact).

5. Same-Origin CloudFront Routing (Zero CORS)

By mapping CloudFront origin request behaviors to route /api/* traffic straight to API Gateway, the browser treats backend API calls as same-origin requests. This completely eliminates CORS preflight (OPTIONS) latency overhead and keeps frontend scripts calling relative paths like fetch('/api/contact') cleanly.

6. Key Technical Takeaways

  • Resource Import: Adopted live legacy resources into HCL state tracking using terraform import without downtime.
  • Automated Code Formatting: Integrated terraform fmt -check in CI/CD to enforce clean coding standards across commits.
  • Zero-Trust IAM: Provisioned dedicated CI/CD IAM service users restricted specifically to infrastructure deployment tasks.