Deploying an Angular App to AWS S3 using Terraform - Part 2 | CICD | Automating deployment using GitHub Actions | Overcoming ACL Challenges

Deploying an Angular App to AWS S3 using Terraform - Part 2 | CICD | Automating deployment using GitHub Actions | Overcoming ACL Challenges

Deploying an Angular app to an AWS S3 bucket is a great way to host a static website. While I initially deployed my app using Terraform commands directly from the command prompt, (Deploying an Angular App to AWS S3 using Terraform - Part 1 (hashnode.dev)) I decided to automate the process using GitHub Actions. In this blog post, I’ll walk through how I set up GitHub Actions to automate the entire deployment pipeline.

Using GitHub Actions allows you to automate the deployment process, ensuring that every time code is pushed to the repository, the app is automatically built, and the changes are deployed to the S3 bucket. This eliminates the need for manual execution of commands, making the deployment process faster and more efficient.

Creating AWS Credentials for GitHub Actions

To allow GitHub Actions to interact with your AWS resources, you need to create IAM user credentials with access to S3 and Terraform actions.

  1. Create an IAM user with programmatic access in the AWS console.

  2. Assign necessary policies like AmazonS3FullAccess and IAMFullAccess (for managing policies).

  3. Store the Access Key ID and Secret Access Key securely.

    Storing AWS Credentials in GitHub Secrets

In the GitHub repository, go to Settings > Secrets and variables > Actions, and create the following secrets:

  • AWS_ACCESS_KEY_ID: The access key ID of the IAM user you created.

  • AWS_SECRET_ACCESS_KEY: The secret access key for the same user.

This allows GitHub Actions to securely access your AWS resources.

Store as below.

Setting up GitHub Actions

Next, I created a GitHub Actions workflow that would automatically deploy the app to S3. This workflow is triggered on every push to the main branch.

Here’s the .github/workflows/deploy.yml configuration file:

reactive-form-angular/.github/workflows/deploy.yml at main · AasifaShaik029/reactive-form-angular

deploy.yml

name: Deploy Angular App to S3

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '20'

    - name: Install dependencies
      run: npm install

    - name: Build Angular project
      run: npm run build

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1

    - name: Deploy using Terraform
      run: |
        terraform init
        terraform apply -auto-approve

main.tf

main.tf is a file where you define the main infrastructure resources you want to provision. The main.tf file is typically the entry point of your Terraform configuration and is the most commonly used filename for Terraform configurations. When you run Terraform commands (like terraform init, terraform apply), Terraform looks for files with the .tf extension in the current directory, and main.tf is often where the core logic is defined.

provider "aws" {
  region = "us-east-1"
}

variable "bucket_name" {
  default = "angular-reactive-apple1"
}

variable "mime_types" {
  default = {
    htm  = "text/html"
    html = "text/html"
    css  = "text/css"
    ttf  = "font/ttf"
    js   = "application/javascript"
    map  = "application/javascript"
    json = "application/json"
    ico  = "image/x-icon"
  }
}

locals {
  upload_directory = "${path.cwd}/dist/app/browser/"
}

resource "aws_s3_bucket" "reactive_form" {
  bucket = var.bucket_name
}

# Separate website configuration
resource "aws_s3_bucket_website_configuration" "reactive_form_website" {
  bucket = aws_s3_bucket.reactive_form.bucket

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "index.html"
  }
}

resource "aws_s3_bucket_ownership_controls" "s3_bucket_acl_ownership" {
  bucket = aws_s3_bucket.reactive_form.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }
}

resource "aws_s3_bucket_public_access_block" "s3_public_block" {
  bucket = aws_s3_bucket.reactive_form.id

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

resource "aws_s3_bucket_policy" "allow_public_access" {
  bucket = aws_s3_bucket.reactive_form.id

  policy = data.aws_iam_policy_document.allow_public_access.json
}

data "aws_iam_policy_document" "allow_public_access" {
  statement {
    actions = [
      "s3:GetObject"
    ]
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
    resources = [
      "arn:aws:s3:::${var.bucket_name}",
      "arn:aws:s3:::${var.bucket_name}/*"
    ]
    effect = "Allow"
  }
}

resource "aws_s3_object" "website_files" {
  for_each      = fileset(local.upload_directory, "**/*.*")
  bucket        = aws_s3_bucket.reactive_form.bucket
  key           = replace(each.value, local.upload_directory, "")
  source        = "${local.upload_directory}${each.value}"
  content_type  = lookup(var.mime_types, split(".", each.value)[length(split(".", each.value)) - 1], "application/octet-stream")
}

output "website_domain" {
  value = aws_s3_bucket.reactive_form.website_domain
}

output "website_endpoint" {
  value = aws_s3_bucket.reactive_form.website_endpoint
}

Once there are deploy.yaml file and main.tf file available after making credential setup, we can make changes to the repo and the build gets automatically triggered.

Challenges I faced and How I Overcame Them

This is the issue I faced. For more info how to resolve this error, please go through Understanding and Resolving the "AccessControlListNotSupported" Error (hashnode.dev)

To check if the app is running or not, Go to properties tab,

In static website hosting section, place the URL in browser. You will see app running here.

Thanks for reading. For any issues. Please feel free to comment.