Deploy a Static Website with Terraform

Use Terraform to deploy a live static website to AWS S3 entirely in code.

Introduction

30 Second Summary

Imagine setting up your entire bedroom by describing it in a text file, hitting enter, and watching everything arrange itself. That is exactly how cloud engineers build infrastructure today.

In this project, you will use Terraform to deploy a static website to AWS S3 entirely through code. No console clicking required.

What You'll Build

A complete Terraform project that creates, configures, and deploys a live static website on AWS S3 with a single command.

By the end of this project, you'll have:

  • A Terraform project with properly structured .tf files that define your entire infrastructure as code.
  • A live website URL hosted on S3, created and managed entirely through Terraform.
  • Hands-on experience with the full Terraform lifecycle: init, plan, apply, and destroy.
  • The ability to spin up a second staging environment using Terraform variables, proving you can replicate infrastructure with a single config change.

Are there any prerequisites?

You need an AWS free tier account. If you have already completed a previous NextWork project on AWS, you are good to go.

Before We Start

Before We Start...

Install Your Tools

Before you can write any Terraform code, you need three tools on your machine.

VS Code is where you will write your Terraform files. The AWS CLI connects your computer to your AWS account so Terraform can create resources on your behalf. Terraform itself is the tool that reads your code and turns it into real cloud infrastructure.

In this step, get ready to:
  • Install VS Code and the Terraform extension for syntax highlighting.
  • Install and configure the AWS CLI so Terraform can talk to your AWS account.
  • Install the Terraform CLI and verify it works.
Download and install VS Code
  • Open your browser and go to https://code.visualstudio.com.
  • Click the large Download for Windows button (or the button matching your operating system).
  • Run the downloaded installer and follow the on-screen prompts to complete the installation.

I already have VS Code

Great. Skip ahead to installing the Terraform extension below.

I just installed it

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Visual Studio Code and press Enter to open it.

Now install the Terraform extension so VS Code can highlight and format your .tf files.

  • In VS Code, click the Extensions icon in the left sidebar (it looks like four small squares).
  • Type HashiCorp Terraform in the search bar.
  • Find the extension published by HashiCorp and click Install.

What does this extension do?

The HashiCorp Terraform extension adds syntax highlighting, auto-completion, and formatting for Terraform files. Without it, your .tf files would look like plain text with no color coding.

Install the AWS CLI

Terraform needs a way to authenticate with your AWS account. The AWS CLI provides that connection by storing your credentials locally.

macOS

  • Open your browser and download the macOS installer from https://awscli.amazonaws.com/AWSCLIV2.pkg.
  • Run the downloaded AWSCLIV2.pkg file and follow the on-screen prompts to complete the installation.
  • Open a new Terminal window. Press Cmd+Space, type Terminal, and press Enter.
  • Verify the installation by running this command:
aws --version

You should see output similar to aws-cli/2.27.41 Python/3.11.6 Darwin/23.3.0. The exact version numbers may differ.

Windows

  • Open your browser and download the Windows MSI installer from https://awscli.amazonaws.com/AWSCLIV2.msi.
  • Run the downloaded AWSCLIV2.msi file and follow the on-screen prompts to complete the installation.
  • Open a new Command Prompt. Press the Windows key, type cmd, and press Enter.

Why a new Command Prompt?

Windows loads your PATH when a Command Prompt opens. If you installed the AWS CLI while a Command Prompt was already open, that window will not know about the new aws command. Always open a fresh one after installing.

  • Verify the installation by running this command:
aws --version

You should see output similar to aws-cli/2.27.41 Python/3.11.6 Windows/10 exe/AMD64. The exact version numbers may differ.

Seeing 'command not found' or 'not recognized'?

  • Make sure you opened a new terminal window after the installation finished.
  • Try closing all terminal windows and opening a fresh one.
  • If it still does not work, re-run the installer to make sure the installation completed successfully.

Help me troubleshoot why aws --version is not working

Now connect the AWS CLI to your AWS account. You will need your AWS access key and secret key for this step.

Where do I find my access keys?

Access keys live in the IAM section of the AWS Console. Go to IAM, then Users, click your username, and open the Security credentials tab. Click Create access key if you do not already have one.

Your secret access key is only shown once when you create it. Copy it somewhere safe before closing the dialog.

  • Configure your AWS credentials by running this command:
aws configure

The command prompts you for four values, one at a time. Enter each value and press Enter after each one:

  • AWS Access Key ID: paste your access key ID.
  • AWS Secret Access Key: paste your secret access key.
  • Default region name: enter us-east-1.
  • Default output format: enter json.

What did aws configure just do?

This command saved your credentials to a local file on your computer. Terraform reads these same credentials when it creates resources in your AWS account. You will not need to enter them again.

Install Terraform

Terraform is distributed as a single binary file. You download it, place it in a folder, and tell your system where to find it.

macOS

On macOS, the easiest way to install Terraform is with Homebrew.

  • Open Terminal and install Terraform by running these commands:
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

What do these commands do?

  • brew tap hashicorp/tap adds HashiCorp's package repository so Homebrew knows where to find Terraform.
  • brew install hashicorp/tap/terraform downloads and installs the Terraform binary. Homebrew handles the PATH automatically.
  • Verify the installation by running:
terraform --version

You should see Terraform v1.14.9 (or a similar recent version) in the output.

Windows

  • Open your browser and go to https://developer.hashicorp.com/terraform/install.
  • Scroll down to the Windows section.
  • Click the AMD64 link under Binary download to download the zip file.
  • Create a new folder called C:\terraform on your computer.
  • Open the downloaded zip file and extract the terraform.exe file into the C:\terraform folder.

Your system does not know where to find terraform.exe yet. You need to add its folder to your system PATH.

  • Press the Windows key, type environment variables, and click Edit the system environment variables.
  • Click Environment Variables... at the bottom of the System Properties window.
  • Under System variables, find and select the row named Path.
  • Click Edit.
  • Click New and type C:\terraform.
  • Click OK on all three open dialogs to save your changes.

What is the PATH?

The PATH is a list of folders your system checks when you type a command. By adding C:\terraform to the PATH, Windows knows to look in that folder when you type terraform.

  • Open a new Command Prompt (close any existing ones first).
  • Verify the installation by running this command:
terraform --version

You should see Terraform v1.14.9 (or a similar recent version) in the output.

terraform --version not working?

  • On Windows, make sure you opened a new Command Prompt after editing the PATH. Existing windows do not pick up PATH changes.
  • Double-check that terraform.exe is inside C:\terraform and not inside a nested subfolder.
  • On macOS, make sure the Homebrew install completed without errors. Run brew doctor to check for issues.

Help me troubleshoot my Terraform installation

Your environment is ready. You have a code editor, a connection to AWS, and the Terraform CLI. Next up, you will write your first Terraform configuration file and preview the infrastructure it creates.

Write Your First Terraform Configuration

In your previous S3 project, you created a bucket by clicking through the AWS Console. That works for one bucket, but imagine recreating 50 buckets across three environments by hand.

Terraform lets you define your infrastructure in code files. You describe what you want, and Terraform figures out how to build it. In this step, you will write your first two Terraform files and preview what they will create.

In this step, get ready to:
  • Create a Terraform project folder with a providers.tf file that tells Terraform to use AWS.
  • Write a main.tf file that defines your S3 bucket as code.
  • Run terraform init and terraform plan to preview your infrastructure before creating anything.
Create your project directory and provider file

Every Terraform project lives in its own folder. Terraform reads every .tf file in that folder and combines them into a single configuration.

  • Open VS Code's built-in terminal by pressing Ctrl+` (Windows) or Cmd+` (macOS).
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir terraform-s3-site
cd terraform-s3-site
  • Confirm the folder exists by running:
ls

The folder will be empty for now. That is expected.

Now you need to tell Terraform which cloud provider to use and which version of Terraform your project requires. This goes in a file called providers.tf.

  • Open the terraform-s3-site folder in VS Code by clicking File in the top menu bar.
  • Click Open Folder.
  • Select the terraform-s3-site folder on your Desktop, then click Open.
  • Create a new file by clicking File then New File.
  • Name the file providers.tf and press Enter.
  • Paste the following code into providers.tf:
# Define the minimum Terraform version and the AWS provider
terraform {
  required_version = ">= 1.2"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

# Configure the AWS provider with your default region
provider "aws" {
  region = "us-east-1"
}

What does this code do?

  • The terraform block sets two constraints. required_version ensures anyone running this project has Terraform 1.2 or newer. required_providers tells Terraform to download the official AWS provider (any 6.x version).
  • The provider "aws" block configures that provider. The region setting tells AWS where to create your resources. us-east-1 is the same region you set during aws configure in Step 1.
  • Save providers.tf by pressing Ctrl+S (Windows) or Cmd+S (macOS).

Not seeing syntax highlighting?

If your .tf file looks like plain text with no colors, make sure you installed the HashiCorp Terraform extension in Step 1. Click the Extensions icon in the left sidebar and search for HashiCorp Terraform to install it.

Help me fix my Terraform syntax highlighting in VS Code.

Define your S3 bucket resource

In your previous project, you clicked Create bucket in the AWS Console and filled out a form. In Terraform, you define that same bucket as a resource block in code.

  • Create a new file called main.tf in the same terraform-s3-site folder.
  • Paste the following code into main.tf:
# Create an S3 bucket for hosting a static website
resource "aws_s3_bucket" "website" {
  bucket = "[[YOURNAME="your-name"]]-nextwork-terraform-site"
}

What does this code do?

  • The resource keyword tells Terraform you want to create something on AWS.
  • "aws_s3_bucket" is the resource type. It maps directly to an S3 bucket in AWS.
  • "website" is the local name you give this resource inside your Terraform code. You will use it later to reference this bucket from other resources.
  • The bucket argument sets the bucket's name on AWS. S3 bucket names are globally unique, so including your name prevents collisions with other AWS users.

Pro tip: bucket naming

S3 bucket names must be lowercase, between 3 and 63 characters, and can only contain lowercase letters, numbers, and hyphens. No spaces or uppercase letters allowed.

  • Save main.tf.
  • Confirm both files exist by running this in your terminal:
ls

You should see main.tf and providers.tf listed in the output.

Don't see both files?

Make sure your terminal is inside the terraform-s3-site folder. Run pwd to check your current directory. If you are somewhere else, run cd ~/Desktop/terraform-s3-site to get back.

Help me find my Terraform files.

Initialize and preview your infrastructure

Your Terraform files are written, but Terraform does not know how to talk to AWS yet. The terraform init command downloads the AWS provider plugin so Terraform can create AWS resources.

  • Initialize your Terraform project by running this command:
terraform init

What does this command do?

  • Terraform reads your providers.tf file and downloads the AWS provider plugin (version 6.x) into a hidden .terraform folder.
  • It also creates a .terraform.lock.hcl file that locks the exact provider version. This ensures everyone on your team uses the same version.

You should see output that includes Terraform has been successfully initialized! at the bottom.

Seeing an error during init?

If you see a message about credentials or authentication, your AWS CLI may not be configured. Run aws configure and enter your access key, secret key, and set the region to us-east-1. Then try terraform init again.

Help me fix my terraform init error.

Now that Terraform has its provider plugin, you can preview what it will create. The terraform plan command reads your code and shows you exactly what changes Terraform would make to your AWS account. Nothing gets created yet.

  • Preview your infrastructure plan by running:
terraform plan

What does this command do?

Terraform compares your code (the desired state) against what currently exists on AWS (the actual state). Since you have not created anything yet, the plan shows one resource to add. Look for the + create symbol next to your S3 bucket.

At the bottom of the output, you will see a summary like Plan: 1 to add, 0 to change, 0 to destroy. This confirms Terraform knows about your bucket and is ready to create it.

You should see your bucket name in the plan output, along with the + create marker.

Seeing a credentials error?

If terraform plan fails with an authentication error, double-check that your AWS CLI credentials are set up. Run aws sts get-caller-identity to verify your credentials are working. If that command fails too, re-run aws configure with your access key and secret key.

Help me fix my terraform plan credentials error.

What is Infrastructure as Code?

What you just did is the core idea behind Infrastructure as Code (IaC). Instead of clicking through a console to create resources, you wrote code that describes what you want. Terraform reads that code and handles the creation for you.

The real power shows up when you need to recreate everything. If you accidentally delete your bucket, you just run terraform apply again. No clicking, no screenshots, no guessing what settings you used last time.

Awesome, I've got everything!

Great. Double-check you have saved both providers.tf and main.tf.

I'd like to double check the full code

Here is what your providers.tf file should look like:

# Define the minimum Terraform version and the AWS provider
terraform {
  required_version = ">= 1.2"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

# Configure the AWS provider with your default region
provider "aws" {
  region = "us-east-1"
}

Here is what your main.tf file should look like:

# Create an S3 bucket for hosting a static website
resource "aws_s3_bucket" "website" {
  bucket = "[[YOURNAME="your-name"]]-nextwork-terraform-site"
}

You have written your first Terraform configuration and previewed it with terraform plan. Next up, you will run terraform apply to actually create the bucket on AWS and configure it for website hosting.

Deploy Your S3 Bucket and Configure Website Hosting

In the previous step, you wrote your first Terraform configuration and previewed what it would create. terraform plan showed you the blueprint, but nothing exists on AWS yet.

Now you will run terraform apply to make it real. You will also add website hosting configuration and upload an HTML file, all through code. By the end of this step, your S3 bucket will be ready to serve a website.

In this step, get ready to:
  • Deploy your S3 bucket to AWS with terraform apply.
  • Add website hosting configuration to your bucket.
  • Upload an HTML file to your bucket using Terraform code.
Apply your configuration to create the bucket

Running terraform apply tells Terraform to create the resources defined in your configuration files. Terraform will show you a plan first and ask for confirmation before making any changes on AWS.

  • Run terraform apply in your VS Code terminal by running this command:
terraform apply

What happens when you run this?

Terraform generates a fresh execution plan (just like terraform plan) and displays it. Then it asks you to confirm before creating anything.

Terraform displays a confirmation prompt that says "Do you want to perform these actions?" with the message "Only 'yes' will be accepted to approve."

  • Type yes and press Enter to confirm.

You should see output ending with Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

What just happened?

Terraform called the AWS API to create your S3 bucket. It also created a terraform.tfstate file in your project folder. This state file tracks every resource Terraform manages so it knows what exists and what needs to change next time you run apply.

Seeing an error?

  • If you see BucketAlreadyExists, your bucket name is taken. S3 bucket names are globally unique across all AWS accounts. Go back to main.tf and change the bucket name to something more unique (add extra numbers or your full name).
  • If you see InvalidClientTokenId or NoCredentialProviders, your AWS CLI credentials are not configured correctly. Run aws configure again and double-check your access key and secret key.
  • Still stuck? Help me debug my terraform apply error.

You can optionally verify the bucket exists in the AWS Console. Open the S3 service in your browser and look for your bucket name in the list. The bucket you see was created entirely by your Terraform code.

Add website hosting configuration

Your S3 bucket exists, but it cannot serve a website yet. You need to enable static website hosting and tell S3 which file to show when someone visits your site.

  • Open main.tf in VS Code.
  • Add the following resource block below your existing aws_s3_bucket resource:
# Enable static website hosting on the bucket
resource "aws_s3_bucket_website_configuration" "website" {
  bucket = aws_s3_bucket.website.id

  # The default page visitors see
  index_document {
    suffix = "index.html"
  }

  # The page shown for 404 errors
  error_document {
    key = "error.html"
  }
}

What does this code do?

  • aws_s3_bucket_website_configuration is a separate Terraform resource that configures website hosting on an existing bucket.
  • bucket = aws_s3_bucket.website.id links this configuration to your bucket. Terraform knows to apply this setting to the bucket you already created.
  • index_document tells S3 to serve index.html when someone visits the root URL of your site.
  • error_document tells S3 to show error.html for any 4XX errors (like page not found).

Before applying, let's preview what Terraform plans to do.

  • Preview the changes by running:
terraform plan

You should see Plan: 1 to add, 0 to change, 0 to destroy. Terraform knows your bucket already exists and only plans to add the new website configuration resource.

Why didn't Terraform recreate the bucket?

Terraform tracks every resource it creates in its state file. When you run plan or apply, it compares your code to the current state and only makes the minimum changes needed. This is one of Terraform's biggest strengths.

  • Save main.tf.

Seeing more than 1 resource to add?

If Terraform wants to add or destroy more than just the website configuration, you may have accidentally changed your aws_s3_bucket resource. Check that your original bucket block is unchanged. Why does my terraform plan show unexpected changes?

Add the HTML file as a Terraform resource

In your previous S3 project, you uploaded files to your bucket by hand through the AWS Console. This time, you will define the file directly in your Terraform code. Terraform will upload it for you every time you run apply.

  • Add the following resource block at the bottom of main.tf:
# Upload the index.html file to the bucket
resource "aws_s3_object" "index" {
  bucket       = aws_s3_bucket.website.id
  key          = "index.html"
  content_type = "text/html"

  # The HTML content of your website
  content = <<EOF
<!DOCTYPE html>
<html>
<head>
    <title>My Terraform Website</title>
</head>
<body>
    <h1>Hello from Terraform!</h1>
    <p>This website was deployed entirely with infrastructure as code.</p>
    <p>No console clicking required.</p>
</body>
</html>
EOF
}

What does this code do?

  • aws_s3_object creates a file inside your S3 bucket. This replaces the manual file upload from the AWS Console.
  • key = "index.html" sets the filename inside the bucket.
  • content_type = "text/html" tells S3 to serve this file as HTML. Without this, browsers might try to download the file instead of displaying it.
  • The content argument uses a heredoc (<<EOF ... EOF) to define multi-line HTML directly in your Terraform file. For a real project you would use the source argument to point to a local file instead.
  • Save main.tf.

Awesome, I've got everything!

Double check you have saved main.tf before moving on.

I'd like to double check the full code

Here is the complete main.tf file so far. Compare yours against it.

# Create the S3 bucket
resource "aws_s3_bucket" "website" {
  bucket = [[BUCKET_NAME="your-unique-bucket-name"]]
}

# Enable static website hosting on the bucket
resource "aws_s3_bucket_website_configuration" "website" {
  bucket = aws_s3_bucket.website.id

  # The default page visitors see
  index_document {
    suffix = "index.html"
  }

  # The page shown for 404 errors
  error_document {
    key = "error.html"
  }
}

# Upload the index.html file to the bucket
resource "aws_s3_object" "index" {
  bucket       = aws_s3_bucket.website.id
  key          = "index.html"
  content_type = "text/html"

  # The HTML content of your website
  content = <<EOF
<!DOCTYPE html>
<html>
<head>
    <title>My Terraform Website</title>
</head>
<body>
    <h1>Hello from Terraform!</h1>
    <p>This website was deployed entirely with infrastructure as code.</p>
    <p>No console clicking required.</p>
</body>
</html>
EOF
}

You now have a bucket, website hosting configuration, and an HTML file all defined in code. In the next step, you will make the bucket public and go live.

Make Your Website Public and Go Live

In the last step, you created your S3 bucket, configured it for website hosting, and uploaded an HTML file. All through Terraform code.

There is one problem. Your bucket is still private, so nobody can actually see your website. In this step, you will open up public access using a bucket policy and then deploy everything with a single command.

In this step, get ready to:
  • Configure public access settings on your S3 bucket.
  • Create an output that displays your live website URL.
  • Deploy everything and visit your site in the browser.
Configure public access settings

By default, AWS blocks all public access to S3 buckets. Before you can attach a public bucket policy, you need to explicitly tell AWS to allow public policies on this bucket.

Why bucket policies instead of ACLs?

In your previous S3 project, you used ACLs (Access Control Lists) to make objects public. AWS now recommends bucket policies as the preferred access control method. Bucket policies give you fine-grained, centralized control over who can access what in your bucket.

  • Open main.tf in VS Code.
  • Add the public access block resource at the bottom of the file by pasting this code:
# Allow public access policies on the bucket
resource "aws_s3_bucket_public_access_block" "website" {
  bucket = aws_s3_bucket.website.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

What does this code do?

  • aws_s3_bucket_public_access_block controls the four public access toggles on your bucket.
  • Setting all four values to false tells AWS to stop blocking public policies on this specific bucket.
  • This is the Terraform equivalent of unchecking the "Block all public access" box in the AWS Console.
  • Now add the bucket policy resource directly below the public access block you just pasted:
# Grant everyone read access to objects in the bucket
resource "aws_s3_bucket_policy" "website" {
  bucket = aws_s3_bucket.website.id

  # Convert a Terraform map into valid JSON for the policy
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource  = "${aws_s3_bucket.website.arn}/*"
      }
    ]
  })

  # Apply the public access block before attaching this policy
  depends_on = [aws_s3_bucket_public_access_block.website]
}

What does this code do?

  • aws_s3_bucket_policy attaches a JSON policy that grants s3:GetObject permission to everyone ("*"). Anyone with the URL can read your files.
  • jsonencode() is a Terraform function that converts a Terraform map into valid JSON. This keeps your policy readable inside HCL instead of writing raw JSON strings.
  • depends_on forces Terraform to apply the public access block before the bucket policy. Without this, Terraform might try to attach the policy while public policies are still blocked, causing an error.
  • Save main.tf.

Not sure where to add the code?

Add both resources at the very bottom of main.tf, after the aws_s3_object resource you created in the previous step. The order of resources in the file does not matter to Terraform, but keeping related resources together makes the file easier to read.

Help me figure out where to put my public access block and bucket policy in main.tf.

Add an output for your website URL

After Terraform deploys your resources, you need to know the URL of your live website. Terraform outputs let you extract useful values and display them in the terminal after every terraform apply.

  • Create a new file called outputs.tf in your project folder (the same folder as main.tf and providers.tf).
  • Add the following output block:
# Display the website URL after every apply
output "website_url" {
  description = "URL of the S3 static website"
  value       = "http://${aws_s3_bucket_website_configuration.website.website_endpoint}"
}

What does this code do?

  • output defines a named value that Terraform prints to the terminal after apply finishes.
  • website_endpoint is an attribute exported by the aws_s3_bucket_website_configuration resource. It contains the S3 website hostname for your bucket.
  • The "http://" prefix turns the hostname into a full URL. S3 website endpoints use HTTP, not HTTPS.
  • Save outputs.tf.

Seeing a file creation error?

Make sure you are creating outputs.tf in the same folder as main.tf and providers.tf. Terraform reads all .tf files in the current directory, so the filename can be anything. The file just needs to live alongside your other configuration files.

Help me troubleshoot my outputs.tf file.

Apply everything and visit your live site

You now have all the pieces in place. The public access block, bucket policy, and output are ready to deploy.

  • Preview what Terraform will create by running this command:
terraform plan

You should see Terraform planning to add 2 new resources (the public access block and the bucket policy) plus 1 new output.

  • Deploy all the new resources by running:
terraform apply
  • Terraform shows you a plan of what it will create.
  • Type yes and press Enter to confirm.

After Terraform finishes, you should see your website URL printed in the terminal output. It looks something like this:

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

website_url = "http://nextwork-yourname-tf-website.s3-website-us-east-1.amazonaws.com"

What is that URL?

This is the S3 website endpoint for your bucket. The format is http://BUCKET-NAME.s3-website-REGION.amazonaws.com. AWS generates this automatically when you enable website hosting on a bucket.

  • Copy the URL from the terminal output.
  • Paste it into your browser's address bar and press Enter.

You should see your HTML page displayed in the browser. Your static website is live on the internet.

Seeing 403 Forbidden?

  • Make sure the depends_on line is present in your aws_s3_bucket_policy resource. Without it, Terraform may have tried to apply the policy before the public access block was ready.
  • Check that all four values in aws_s3_bucket_public_access_block are set to false. If any of them are true, AWS will block your public policy.
  • Try running terraform apply again. Sometimes the resources need a second apply to settle into the correct order.

Help me troubleshoot my 403 Forbidden error.

Awesome, I've got everything!

Great. Double check you have saved both main.tf and outputs.tf.

I'd like to double check the full code

Here is the complete main.tf file with all resources from Steps 2 through 4:

# Create the S3 bucket
resource "aws_s3_bucket" "website" {
  bucket = "nextwork-[[YOURNAME="enter your name"]]-tf-website"
}

# Configure the bucket for static website hosting
resource "aws_s3_bucket_website_configuration" "website" {
  bucket = aws_s3_bucket.website.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "error.html"
  }
}

# Upload the index.html file to the bucket
resource "aws_s3_object" "index" {
  bucket       = aws_s3_bucket.website.id
  key          = "index.html"
  content      = "<html><body><h1>Hello from Terraform!</h1><p>This website is deployed with Infrastructure as Code.</p></body></html>"
  content_type = "text/html"
}

# Allow public access policies on the bucket
resource "aws_s3_bucket_public_access_block" "website" {
  bucket = aws_s3_bucket.website.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

# Grant everyone read access to objects in the bucket
resource "aws_s3_bucket_policy" "website" {
  bucket = aws_s3_bucket.website.id

  # Convert a Terraform map into valid JSON for the policy
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "PublicReadGetObject"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource  = "${aws_s3_bucket.website.arn}/*"
      }
    ]
  })

  # Apply the public access block before attaching this policy
  depends_on = [aws_s3_bucket_public_access_block.website]
}

And here is the complete outputs.tf file:

# Display the website URL after every apply
output "website_url" {
  description = "URL of the S3 static website"
  value       = "http://${aws_s3_bucket_website_configuration.website.website_endpoint}"
}

Your website is live and fully managed by Terraform code. Next up, you will see the true power of Infrastructure as Code by tearing everything down with a single command.

Destroy Your Infrastructure

Your website is live and fully managed by Terraform code. But here is the real power move. Everything you built with one command can be torn down with one command too.

In this step, you will inspect what Terraform is tracking and then destroy all of your AWS resources in a single shot. This is one of the biggest advantages of Infrastructure as Code. No hunting through console screens to delete things one by one.

In this step, get ready to:
  • Review all the resources Terraform is tracking in its state file.
  • Destroy every resource with a single command and verify they are gone.
Review your Terraform state

Terraform keeps track of every resource it creates in a local file called terraform.tfstate. This is how Terraform knows what exists in the real world and what needs to change when you update your code.

  • See everything Terraform is managing by running this command in your VS Code terminal:
terraform state list

You should see a list of five resources:

aws_s3_bucket.website
aws_s3_bucket_policy.website
aws_s3_bucket_public_access_block.website
aws_s3_bucket_website_configuration.website
aws_s3_object.index

What does this list mean?

Each line is a resource that Terraform created and is now tracking. The format is resource_type.resource_name. These five resources map directly to the five resource blocks you wrote in main.tf.

If you open the AWS Console and navigate to your S3 bucket, you will see the same resources there. Everything in the console was created by your code.

What about the state file itself?

The terraform.tfstate file lives in your project folder. For a solo project this is fine. In real teams, state is stored remotely (like in an S3 bucket with a DynamoDB lock table) so everyone shares the same view of infrastructure.

Destroy all resources with one command

The terraform destroy command reads your state file and removes every resource Terraform is tracking. One command deletes everything, in the correct order, with no manual cleanup.

  • Tear down all your AWS resources by running this command:
terraform destroy

Terraform shows you a destruction plan listing every resource it will delete. It looks similar to terraform plan, but every resource is marked with a red minus sign instead of a green plus.

  • Review the plan. You should see Plan: 0 to add, 0 to change, 5 to destroy.
  • Terraform asks: "Do you really want to destroy all resources?" Type yes and press Enter.

Terraform deletes each resource one by one. After a few seconds, you should see:

Destroy complete! Resources: 5 destroyed.

Why is this a big deal?

Imagine you had 50 resources across multiple AWS services. Without Terraform, you would need to visit each service in the console, find the resource, and delete it manually. With terraform destroy, one command handles everything. It also deletes resources in the right order, so you never hit dependency errors.

  • Verify the bucket is gone by opening the AWS Console in your browser.
  • Navigate to the S3 service.
  • Confirm that your bucket no longer appears in the bucket list.

Bucket still showing up?

  • The AWS Console can take a few seconds to refresh. Click the refresh icon in the S3 bucket list.
  • Make sure you typed yes (not "y" or "Y"). Terraform only accepts the full word yes.
  • Check your terminal output for any error messages. If the destroy failed partway through, run terraform destroy again to retry.

Still stuck? Ask for help.

  • Try visiting your website URL one more time in your browser.

You should see a 404 Not Found or a connection error. Your site is gone because the infrastructure behind it no longer exists.

What happens to your Terraform files?

Your .tf files are still on your computer. Only the AWS resources are gone. You can run terraform apply again at any time to recreate everything from scratch. That is the whole point of Infrastructure as Code. Your infrastructure is repeatable.

You just completed the full Terraform lifecycle. You initialized, planned, applied, and destroyed real cloud infrastructure using nothing but code. Next up, put your new skills to the test with a Secret Mission.

Secret mission

Deploy a Staging Environment with Variables

You just proved you can deploy infrastructure with code. But in the real world, teams need more than one environment. Can you use Terraform variables and workspaces to deploy a second, independent copy of your website from the exact same codebase?

Before you go

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. S3 costs for a simple static site are negligible (fractions of a penny per month under free tier), but cleaning up is still good practice.

Resources you used:

  • S3 bucket configured for static website hosting.
  • S3 bucket policy granting public read access.
  • S3 object (index.html) uploaded to the bucket.
  • Local Terraform project files (.tf files, .terraform directory, terraform.tfstate).
  • Secret Mission: a second S3 bucket and website in a staging Terraform workspace (if completed).

Keep everything running

No action needed. Choose this if you want to keep your static website live. S3 charges for a single HTML page are fractions of a penny per month under the free tier.

  • Your website will stay accessible at the S3 website endpoint URL.
  • Your local Terraform files and state file will remain in your project directory.
  • You can run terraform destroy at any time to tear everything down later.

Pause - I'll come back to this later

Tear down the AWS resources to avoid any charges, but keep your local Terraform files so you can redeploy later with a single command.

  • Open a terminal in your project directory.
  • If you completed the Secret Mission, switch to the staging workspace first:
terraform workspace select staging
terraform destroy
  • Then switch back to the default workspace and destroy those resources too:
terraform workspace select default
terraform destroy
  • If you did not complete the Secret Mission, just run terraform destroy from your project directory.
  • Type yes when prompted to confirm each destroy.
  • Keep your local project folder. When you are ready to come back, just run terraform apply to redeploy everything.

Delete - I don't want to use this again

Remove all AWS resources and delete your local project files for a clean slate.

  • Open a terminal in your project directory.
  • If you completed the Secret Mission, destroy resources in both workspaces:
terraform workspace select staging
terraform destroy
terraform workspace select default
terraform destroy
  • If you did not complete the Secret Mission, just run terraform destroy from your project directory.
  • Type yes when prompted to confirm each destroy.
  • Verify in the AWS Console that your S3 buckets are gone.
  • Delete your local project folder entirely.
  • Terraform CLI and AWS CLI can stay installed on your machine for future projects. If you want to remove them, uninstall Terraform by deleting the binary and removing it from your PATH. Uninstall AWS CLI from Apps & features in Windows Settings.

Mission accomplished

That's a Wrap

You did it! You just went from zero Terraform experience to deploying live infrastructure on AWS entirely through code.

Here's what you learned:

  • Wrote Terraform configuration files from scratch to create an S3 bucket, configure website hosting, set up a public bucket policy, and upload content.
  • Ran the full Terraform lifecycle with terraform init, terraform plan, terraform apply, and terraform destroy to create and tear down cloud infrastructure with single commands.
  • Used outputs to extract your live website URL directly from Terraform instead of hunting through the AWS Console.
  • Deployed a second staging environment using Terraform variables and workspaces, proving you can spin up identical infrastructure with a single config change.

Ready to quiz yourself?