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.
Create an IAM user with programmatic access in the AWS console.
Assign necessary policies like
AmazonS3FullAccess
andIAMFullAccess
(for managing policies).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.