Skip to main content

Command Palette

Search for a command to run...

Provision S3 + CloudFront Static Website Using Terraform (Production Guide)

Build AWS infrastructure using Infrastructure as Code instead of manual setup

Published
6 min read
L

I build and deploy cloud-native applications using AWS and DevOps practices. I share practical tutorials on CI/CD pipelines, serverless architectures, and real project learnings, and I’m exploring MLOps.

Introduction

In the previous blogs, we manually created AWS resources such as S3, CloudFront, IAM users, and policies using the AWS Management Console. While this approach works for learning, it quickly becomes inefficient and risky as environments grow.

In real-world DevOps and SRE teams, infrastructure is never managed manually. Clicking through the AWS console for every change leads to inconsistency, human error, and poor scalability.

This is where Infrastructure as Code (IaC) comes in.

In this blog, we will use Terraform to provision and manage AWS infrastructure in a fully automated, secure, and reproducible way, following real production practices.

Prerequisites

  • AWS account

  • Terraform installed

  • AWS CLI configured with an IAM user that has permissions to create IAM, S3, and CloudFront resources

  • Terraform Modules


Problem with Manual AWS Console Setup

Managing infrastructure manually through the AWS Console introduces several problems:

  • No version control for infrastructure changes

  • Difficult to replicate environments (dev, staging, prod)

  • High chance of misconfiguration

  • Manual steps are error-prone

  • Not suitable for team collaboration

  • Hard to audit who changed what and when

As infrastructure grows, manual setup becomes unmanageable and unsafe for production workloads.


Why Infrastructure as Code (IaC) Matters

Infrastructure as Code means defining your infrastructure using code, not clicks.

With IaC:

  • Infrastructure is stored in version control (Git)

  • Changes are reviewed like application code

  • Environments are consistent and repeatable

  • Infrastructure can be recreated at any time

  • Rollbacks are possible

  • Automation becomes simple and reliable

IaC is a core requirement in modern DevOps, Cloud, and SRE roles.


Terraform in Real-World DevOps

Terraform is one of the most widely used IaC tools in the industry.

In real-world DevOps teams, Terraform is used to:

  • Provision AWS, Azure, GCP infrastructure

  • Create IAM users, roles, and policies

  • Manage networking (VPC, subnets, gateways)

  • Deploy CloudFront, S3, Load Balancers

  • Standardize infrastructure using modules

  • Integrate with CI/CD pipelines

Terraform allows teams to manage infrastructure safely, predictably, and at scale.


What You Will Build in This Blog

By the end of this blog, you will build:

  • A private S3 bucket using Terraform

  • A CloudFront distribution with Origin Access Control (OAC)

  • Secure S3 bucket policy allowing access only from CloudFront

  • An IAM user and policy for Terraform deployments

  • A modular Terraform project structure

  • Production-ready AWS infrastructure using code

This setup mirrors how static websites are deployed in real production environments.


What is Terraform?

Terraform is an Infrastructure as Code (IaC) tool developed by HashiCorp that allows you to define, provision, and manage infrastructure using configuration files.


Infrastructure as Code Tool

Terraform lets you describe your infrastructure in simple configuration files, instead of manually creating resources in the cloud console.

Once defined, Terraform automatically creates and manages those resources for you.


Declarative Configuration

Terraform uses a declarative approach, meaning:

  • You define what infrastructure you want

  • Terraform figures out how to create it

You don’t need to write procedural scripts. You simply describe the desired end state.


Reproducible Infrastructure

With Terraform:

  • Infrastructure can be recreated at any time

  • Environments remain consistent

  • No configuration drift

  • Same code works across teams and regions

This makes Terraform ideal for production systems.

Terraform Project Structure (Modules-Based)

As infrastructure grows, keeping all Terraform code in a single file becomes hard to manage.
In real-world DevOps projects, Terraform is organized using modules to improve readability, reusability, and scalability.

A modules-based structure allows teams to:

  • Reuse infrastructure components

  • Maintain clean separation of concerns

  • Apply best practices consistently

  • Scale infrastructure safely

This is the recommended approach for production environments.


Why Use Terraform Modules?

Terraform modules help solve common problems in large infrastructures:

  • Avoid code duplication

  • Standardize infrastructure patterns

  • Improve maintainability

  • Enable team collaboration

  • Make environments (dev, stage, prod) easy to manage

In short, modules turn Terraform into production-grade infrastructure code.

terraform-static-website/
├── modules/
│   ├── iam/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   │
│   ├── s3/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   │
│   ├── cloudfront/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│
├── main.tf
├── variables.tf
├── outputs.tf
├── providers.tf
├── terraform.tfvars
└── README.md

Structure Explanation

modules/

Contains reusable Terraform modules.
Each module is responsible for one logical component.


modules/iam/

Handles:

  • IAM user creation

  • IAM policies

  • Programmatic access for Terraform / CI/CD

This keeps security and access management isolated and easy to audit.


modules/s3/

Handles:

  • Private S3 bucket creation

  • Bucket policies

  • Versioning and encryption (optional)

The bucket is designed to be accessible only via CloudFront.


modules/cloudfront/

Handles:

  • CloudFront distribution

  • Origin Access Control (OAC)

  • S3 origin configuration

  • Cache behavior

This ensures secure global content delivery.


Root Files

providers.tf

Defines the AWS provider and region.

main.tf

Calls all modules and connects them together.

variables.tf

Defines input variables for the root module.

terraform.tfvars

Stores environment-specific values (bucket name, region, etc.).

outputs.tf

Exports important values such as:

  • CloudFront distribution ID

  • S3 bucket name

  • IAM user access details

Complete Terraform Implementation (GitHub Repository)

To keep this blog clean and focused on architecture and concepts, the full Terraform implementation is maintained in a GitHub repository.

The repository contains production-ready Terraform code that covers:

👉 Creating IAM User and IAM Policy using Terraform
👉 Provisioning a Private S3 Bucket with CloudFront Origin Access Control (OAC)
👉 Deploying a CloudFront Distribution securely connected to the S3 bucket
👉 Applying correct S3 bucket policy to allow access only via CloudFront


GitHub Repository

🔗 Terraform Code Repository:
👉 https://github.com/nlokeshbabu1/AWS-S3

This repository follows a modules-based Terraform structure and reflects real-world DevOps best practices.

Verification

After applying the Terraform configuration, verify that the infrastructure is working as expected:

  • The S3 bucket is private

  • Public access to the S3 bucket is blocked

  • The S3 bucket policy allows access only from CloudFront

  • CloudFront distribution status is Deployed

  • The website is accessible using the CloudFront domain name

Successful verification confirms that the setup is secure, scalable, and production-ready.


Terraform makes it easy to remove infrastructure when it is no longer required.

To avoid unnecessary AWS costs, the resources can be destroyed using Terraform.

The cleanup steps are documented in the GitHub repository and can be executed safely when needed.

This highlights one of the major benefits of Infrastructure as Code — easy creation and clean teardown of resources.


Conclusion

In this blog, we moved from manual AWS resource creation to a fully automated, Infrastructure as Code approach using Terraform.

By using Terraform modules, we achieved:

  • Reproducible and version-controlled infrastructure

  • Secure S3 and CloudFront integration using OAC

  • Clean separation of concerns using modules

  • Production-ready AWS infrastructure

This approach reflects how real DevOps and SRE teams manage cloud infrastructure at scale.

In the next blog, we will extend this setup by integrating Terraform with CI/CD pipelines to automate infrastructure provisioning and updates.