Step-by-Step Guide. Hosting Your Application on AWS S3
Tags: terraform, cicd, deploy, aws
Published: 2023-05-22 05:39:39

---
title: Step-by-Step Guide. Hosting Your Application on AWS S3
published: true
description: A Step-by-Step Guide to Setting Up S3, CloudFront, and CI/CD Pipeline with Terraform.
tags: terraform, cicd, deploy, aws
---

Are you considering hosting your Next.js static web application on AWS? If so, this article is for you! In this article, I will walk you through the process of setting up AWS S3 and CloudFront for hosting your Next.js application using Terraform. Additionally, I will explain how to establish a robust CI/CD pipeline to seamlessly deploy your application to AWS. By following this step-by-step guide, you'll gain valuable insights and practical knowledge to efficiently host your Next.js application on AWS.

## Motivation

Why you should consider hosting CloudFront with S3?
Because it significantly reduces latency, ensuring faster content delivery to end-users. Also, this combination can help reduce costs associated with data transfer and it enhances security measures for your content.
Setting up continuous integration and continuous deployment (CI/CD) to deploy your application becomes easier too.

## What is S3?

AWS provides a storage service called S3, which allows you to store data in the form of objects within buckets. Additionally, S3 can be used to host static websites.

## What is CloudFront?

CloudFront is a well-known content delivery service, often referred to as a CDN. Its primary function is to efficiently distribute your content to end-users, ensuring fast delivery. One of the main advantages of using CloudFront is that it serves content from edge locations. This means that if your content is already available at an edge location, it can be delivered to users with minimal latency. If the content is not present at an edge location, CloudFront retrieves it from the origin, which in this case would be S3.

## Let's start creating CloudFront and S3 by Terraform!

There are 5 steps to deploy your application on Kubernetes with GitHub Actions and ArgoCD.

1. Setup AWS Provider
2. Create the S3 bucket
3. Create the CloudFront
4. Setup CI/CD

NOTE:
You need to set your aws credential in `(~/.aws/credentials)`

## 1. Setup AWS Provider

Configure to execute AWS by Terraform

</> provider.tf

```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
  required_version = ">= 1.0"
}

provider "aws" {
  region = "ap-northeast-1"
}
```

## 2. Create the S3 bucket

</> s3.tf

```

resource "aws_s3_bucket" "bucket" {
  bucket = test-bucket
}

resource "aws_s3_bucket_cors_configuration" "cors_config" {
  bucket = aws_s3_bucket.bucket.id

  cors_rule {
    allowed_headers = ["*"]
    allowed_methods = ["GET", "POST", "PUT", "DELETE", "HEAD"]
    allowed_origins = ["https://set-origin"]
    expose_headers  = ["ETag"]
    max_age_seconds = 3000
  }
}

resource "aws_s3_bucket_ownership_controls" "example" {
  bucket = aws_s3_bucket.bucket.id
  rule {
    object_ownership = "ObjectWriter"
  }
}

resource "aws_s3_bucket_acl" "acl" {
  depends_on = [aws_s3_bucket_ownership_controls.example]

  bucket = aws_s3_bucket.bucket.id
  acl    = "private"
}

data "aws_iam_policy_document" "policy" {
  statement {
    actions = [
      "s3:GetObject",
    ]

    resources = [
      "${aws_s3_bucket.bucket.arn}/*",
    ]

    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }
    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_cloudfront_distribution.this.arn]
    }
  }
}

resource "aws_s3_bucket_policy" "bucket" {
  bucket = aws_s3_bucket.bucket.id
  policy = data.aws_iam_policy_document.policy.json
}
```

- aws_s3_bucket_cors_configuration
  - To enable Cross-Origin Resource Sharing (CORS) and allow loading resources from one domain to interact with resources in a different domain.
- aws_s3_bucket_acl
  - Set private to disable public access.
- aws_iam_policy_document
  - To restrict access to only the `s3:GetObject` action from CloudFront and exclude other policies for content delivery.

## 3. Create the CloudFront

</> cloudfront.tf

```
resource "aws_cloudfront_origin_access_control" "origin" {
  name                              = "cf-origin-access-control"
  description                       = "access to s3 bucket"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

resource "aws_cloudfront_distribution" "static" {
  default_root_object = "index.html"
  enabled             = true
  price_class         = "PriceClass_200"

  origin {
    domain_name              = aws_s3_bucket.bucket.bucket_regional_domain_name
    origin_id                = "s3-bucket"
    origin_access_control_id = aws_cloudfront_origin_access_control.origin.id
  }

  default_cache_behavior {
    allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods  = ["GET", "HEAD"]

    forwarded_values {
      cookies {
        forward = "none"
      }
      query_string = "false"
    }

    max_ttl                = 86400
    min_ttl                = 0
    target_origin_id       = "s3-bucket"
    viewer_protocol_policy = "redirect-to-https"
  }

  restrictions {
    geo_restriction {
      locations        = []
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}
```

- aws_cloudfront_origin_access_control
  - Configure the origin type as S3.
- aws_cloudfront_distribution
  - Create a CloudFront distribution and set the origin to a previously created S3 bucket.
  - To use a custom domain with CloudFront, you can configure your domain by setting it in the viewer_certificate section of your CloudFront distribution settings. In this case, you will be using the CloudFront domain for your custom domain setup.

## 4. Setup CI/CD

</> deploy.yaml

Creating CI/CD using GitHub Actions allows you to automate the build process and upload files to S3.

```
on:
  push:
    branches: [release]

permissions:
  id-token: write
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18]
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: yarn
      - run: yarn
      - run: yarn build
      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: build
          path: dist/
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          name: build
          path: dist/

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1-node16
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/gha-oidc-role
          aws-region: ap-northeast-1

      - name: Publish
        uses: opspresso/action-s3-sync@master
        env:
          AWS_REGION: ap-northeast-1
          FROM_PATH: dist/
          DEST_PATH: s3://bucket

      - name: Invalidate CF
        uses: chetan/invalidate-cloudfront-action@v2
        env:
          AWS_REGION: ap-northeast-1
          DISTRIBUTION: distributionId
          PATHS: /*
```

- Upload and Download artifact
  - To share data between jobs, you can utilize the `actions/upload-artifact@v3` action.
- Configure AWS credentials
  - To configure AWS credentials based on your IAM role, you can utilize the `aws-actions/configure-aws-credentials@v1-node16` action.
- Publish
  - To publish your built file, you can utilize the `opspresso action-s3-sync@master` action.
- Invalidate CF
  - To remove a file from CloudFront edge caches, you can utilize the `chetan/invalidate-cloudfront-action@v2` action.

## Conclusion

You have learned how to host and deploy your application using Terraform and GitHub Actions, taking advantage of the managed services provided by AWS such as S3 and CloudFront. With this setup, you can easily manage and scale your application without the need for manual infrastructure management. By following the steps outlined in this article, you can confidently host your application on AWS.

Happy coding!

### Reference:

[Amazon Simple Storage Service Documentation](https://docs.aws.amazon.com/s3/index.html)

[Invalidating files - Amazon CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html)

[Amazon CloudFront Documentation](https://docs.aws.amazon.com/cloudfront/index.html)