DEV Community

Revathi Joshi for AWS Community Builders

Posted on

How to Create a simple static Amazon S3 website using Terraform

What is Terraform?

  • HashiCorp Terraform is an infrastructure as code (IaC) tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share.

Please visit my previuos article on create a simple static website using amazon s3

Please visit my GitHub Repository for S3 articles on various topics being updated on constant basis.

Please visit my GitHub Repository for Terraform articles on various topics being updated on constant basis.

Let’s get started!

Objectives:

1. Sign in to AWS Management Console

2. Create the following organizational structure

3. Create files - index.html and error.html

4. Under s3_files directory: Create 4 files - variables.tf, terraform.tfvars, main.tf, outputs.tf

5. Initialize Terraform

6. To generate the action plans

7. Create all the resources declared in main.tf configuration file

8. Validate all resources created

9. Test the S3 Static web site on the new web browser

Pre-requisites:

  • AWS user account with admin access, not a root account.
  • Cloud9 IDE with AWS CLI.

Resources Used:

Terraform documentation for AMI.

Tutorial: Configuring a static website on Amazon S3.

Steps for implementation to this project:

1. Sign in to AWS Management Console

  • Make sure you're in the N. Virginia (us-east-1) region

2. Let’s create the following organizational structure as shown below:

Image description

3. Create files - index.html and error.html

  • Under s3_files directory, create a html folder, and then create error.html and index.html files in it.

  • index.html

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <title>Creating a Static Website Using Amazon S3</title>

  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

  <style>
    body {
      padding-top: 5rem;
    }

    .starter-template {
      padding: 3rem 1.5rem;
      text-align: center;
    }

    .bd-placeholder-img {
      font-size: 1.125rem;
      text-anchor: middle;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
    }

    @media (min-width: 768px) {
      .bd-placeholder-img-lg {
        font-size: 3.5rem;
      }
    }
  </style>
</head>

<body>
  <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
    <a class="navbar-brand" href="#">My Static Website</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
      aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarsExampleDefault">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Link</a>
        </li>
        <li class="nav-item">
          <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
        </li>
        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true"
            aria-expanded="false">Dropdown</a>
          <div class="dropdown-menu" aria-labelledby="dropdown01">
            <a class="dropdown-item" href="#">Action</a>
            <a class="dropdown-item" href="#">Another action</a>
            <a class="dropdown-item" href="#">Something else here</a>
          </div>
        </li>
      </ul>
      <form class="form-inline my-2 my-lg-0">
        <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
        <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
      </form>
    </div>
  </nav>

  <main role="main" class="container">

    <div class="starter-template">
      <h1>Creating a Static Website Using Amazon S3</h1>
      <p class="lead">Using Terraform, welcome to a simple static website, a cost-efficient website hosting for sites that consist of files like HTML, CSS, JavaScript, fonts, and images with a custom error page. 
      </p>
    </div>

    <div class="starter-template alert alert-light" role="alert">
      Navigation and search functions are intentionally not implemented.
    </div>

  </main><!-- /.container -->

  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
    integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous">
  </script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
    integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous">
  </script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
    integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous">
  </script>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode
  • error.html
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <title>Creating a Static Website Using Amazon S3</title>

  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

  <style>
    body {
      padding-top: 5rem;
    }

    .starter-template {
      padding: 3rem 1.5rem;
      text-align: center;
    }

    .bd-placeholder-img {
      font-size: 1.125rem;
      text-anchor: middle;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
    }

    @media (min-width: 768px) {
      .bd-placeholder-img-lg {
        font-size: 3.5rem;
      }
    }

  </style>
</head>

<body>
  <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
    <a class="navbar-brand" href="#">My Static Website</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
      aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarsExampleDefault">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Link</a>
        </li>
        <li class="nav-item">
          <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
        </li>
        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true"
            aria-expanded="false">Dropdown</a>
          <div class="dropdown-menu" aria-labelledby="dropdown01">
            <a class="dropdown-item" href="#">Action</a>
            <a class="dropdown-item" href="#">Another action</a>
            <a class="dropdown-item" href="#">Something else here</a>
          </div>
        </li>
      </ul>
      <form class="form-inline my-2 my-lg-0">
        <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
        <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
      </form>
    </div>
  </nav>

  <main role="main" class="container">

    <div class="starter-template">
      <h1>Custom Error Page</h1>
      <p class="lead">Go to <a href="index.html">index.html page again</a>?</p>
      </p>
    </div>

    <div class="starter-template alert alert-light" role="alert">
      Navigation and search functions are intentionally not implemented.
    </div>

  </main><!-- /.container -->

  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

4. Under s3_files directory:

  • Create 4 files - variables.tf, terraform.tfvars, main.tf, outputs.tf

    1. variables.tf - to declare all the global variables with a short description and a default value.
# required for AWS
variable "access_key" {
    description = "Access key to AWS console"
}
variable "secret_key" {
    description = "Secret key to AWS console"
}
variable "region" {
    description = "Region of AWS VPC"
}
Enter fullscreen mode Exit fullscreen mode
  • 2. terraform.tfvars - Replace the values of access_key and secret_key by copying your AWS Access Key ID and Secret Access Key ID.
region = "us-east-1"                
access_key = "<YOUR AWS CONSOLE ACCESS ID>"             
secret_key = "<YOUR AWS CONSOLE SECRET KEY>"                
Enter fullscreen mode Exit fullscreen mode
  • 3. main.tf - Creating a S3 bucket and its components
provider "aws" {
  region     = var.region
  access_key = var.access_key
  secret_key = var.secret_key
}

##### Creating a Random String #####
resource "random_string" "random" {
  length = 6
  special = false
  upper = false
} 

##### Creating an S3 Bucket #####
resource "aws_s3_bucket" "bucket" {
  bucket = "revbucket-${random_string.random.result}"
  force_destroy = true
}

resource "aws_s3_bucket_website_configuration" "blog" {
  bucket = aws_s3_bucket.bucket.id
  index_document {
    suffix = "index.html"
  }
  error_document {
    key = "error.html"
  }
}

resource "aws_s3_bucket_public_access_block" "public_access_block" {
  bucket = aws_s3_bucket.bucket.id
  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

##### will upload all the files present under HTML folder to the S3 bucket #####
resource "aws_s3_object" "upload_object" {
  for_each      = fileset("html/", "*")
  bucket        = aws_s3_bucket.bucket.id
  key           = each.value
  source        = "html/${each.value}"
  etag          = filemd5("html/${each.value}")
  content_type  = "text/html"
}
Enter fullscreen mode Exit fullscreen mode
  • Apply Bucket Policy
  • in S3, click the Permissions tab
  • replace with your S3 bucket ARN
  • Note: Ensure the trailing /* is present so the policy applies to all objects within the bucket.
{
    "Version": "2012-10-17",
    "Id": "Policy1234567890123",
    "Statement": [
        {
            "Sid": "Stmt1234567890123",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::arn:aws:s3:::revbucket-npau9q/*"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode
  • Click Save changes

Image description

  • 4. outputs.tf - displays the output as website endpoint.
output "s3_bucket_id" {
  value = aws_s3_bucket_website_configuration.blog.website_endpoint
}
Enter fullscreen mode Exit fullscreen mode

5. Initialize Terraform

  • terraform version

Image description

  • terraform init will check for all the plugin dependencies and download them if required, this will be used for creating a deployment plan.

  • Run terraform init

Image description

6. To generate the action plans, run the below command:

  • Run terraform plan

7. Create all the resources declared in main.tf configuration file, run the below command:

  • Run terraform apply

  • Wait for 4-5 minutes to create all the resources.

Image description

Image description

8. Validate all resources created

  • s3 bucket

Image description

  • index.html, error.html

Image description

9. Test the S3 Static web site on the new web browser

  • Go to the properties tab of S3 bucket and scroll to the end of this page to find Static website hosting options.

  • Copy the Bucket website endpoint and paste into the new tab of your web browser, with /index.html at the end.

Image description

  • test error.html feature is working or not, replace index.html with rev.html.
  • Now you should see the contents of error.html

Image description

Cleanup

  • Run terraform destroy

  • Delete Cloud9 environment

Image description

What we have done so far

  • Using Terraform, We created a static site using an AWS S3 bucket with the pre-created HTML files for your static web site.

Top comments (1)

Collapse
 
hectorfernandezdev profile image
Hector Fernandez CloudparaTodo

Please don’t use public s3 bucket to create websites. Instead use Cloudfront with OAC. Great post!