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.
- Setup AWS Provider
- Create the S3 bucket
- Create the CloudFront
- 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.
- To restrict access to only the
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.
- To share data between jobs, you can utilize the
- Configure AWS credentials
- To configure AWS credentials based on your IAM role, you can utilize the
aws-actions/configure-aws-credentials@v1-node16
action.
- To configure AWS credentials based on your IAM role, you can utilize the
- Publish
- To publish your built file, you can utilize the
opspresso action-s3-sync@master
action.
- To publish your built file, you can utilize the
- Invalidate CF
- To remove a file from CloudFront edge caches, you can utilize the
chetan/invalidate-cloudfront-action@v2
action.
- To remove a file from CloudFront edge caches, you can utilize the
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
Top comments (0)