- Prerequisites
- Project Setup
- Provision the Infrastructure
- How to Deploy to ESC Cluster
- Usage
- Resources
- License
For the purpose of the project, the AdministratorAccess managed policy is acceptable.
You need the credentials that contain your aws_access_key_id and your aws_secret_access_key.
It is highly recommended to check service costs before choosing a region like eu-north-1.
# Set up your AWS CLI with your access keys and default region
aws configure# Verify your AWS credentials by showing the account and user details
aws sts get-caller-identity# List all IAM policies attached to a specific user
aws iam list-attached-user-policies --user-name <user-name>Do not forget to set the email address in ./config/notifications.json.
# Get your account id
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)aws budgets create-budget \
--account-id $ACCOUNT_ID \
--budget file://config/budget.json \
--notifications-with-subscribers file://config/notifications.json \
--region us-north-1After running the command below, you can copy ./config/configuration.s3.tfbackend.example to ./config/configuration.s3.tfbackend then update its content.
aws s3api create-bucket --bucket <unique-tf-state-bucket-name> \
--region <region> \
--create-bucket-configuration LocationConstraint=<region>Expected output
{
"Location": "http://<unique-tf-state-bucket-name>.s3.amazonaws.com/"
}aws dynamodb create-table --table-name <tf-state-lock-table> \
--region <region> \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST Expected output(partially):
{
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "LockID",
"AttributeType": "S"
}
],
"TableName": "<tf-state-lock-table>",
"KeySchema": [
{
"AttributeName": "LockID",
"KeyType": "HASH"
}
],
"TableStatus": "CREATING",
...
"BillingModeSummary": {
"BillingMode": "PAY_PER_REQUEST"
},
"DeletionProtectionEnabled": false
}
}# You can check the status after 10-30 second
aws dynamodb describe-table --table-name sif-state-lock-table --region eu-north-1 --query 'Table.TableStatus'Expected output: is "ACTIVE"
# With backend configuration
terraform init -backend-config=config/configuration.s3.tfbackendExpected output:
Successfully configured the backend "s3"! Terraform will automatically
Terraform has been successfully initialized!The command below generates a self-signed certificate used in a non-production environment. Therefore the domain name in the Common Name (CN) field does not have to match a real domain.
openssl req -x509 -newkey rsa:2048 -nodes -keyout private.key -out certificate.crt \
-days 365 \
-subj "/CN=your-alb-name.eu-north-1.elb.amazonaws.com"terraform plan -var-file="terraform.tfvars" -out=planterraform apply "plan"Be aware that access-token will get expired after sometime.
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.comBuild the Docker image for the Linux arm64 platform and this is a must.
docker buildx build --platform linux/arm64 -t ha-nginx-app . docker tag ha-nginx-app <account-id>.dkr.ecr.<region>.amazonaws.com<registry>:latestdocker push <account-id>.dkr.ecr.<region>.amazonaws.com<registry>:latestAfter image is pushed, you can force new deployment on ECS Service.
aws ecs update-service \
--cluster <cluster-name> \
--service <service-name> \
--force-new-deploymentMake sure that trust-policy.sh is executable: chmod +x ./scripts/trust-policy.sh
# Create trust-policy.json for the role
./scripts/trust-policy.sh <account-id> <github-username> <github-repo-name>aws iam create-role \
--role-name GitHubActionsRole \
--assume-role-policy-document file://config/trust-policy.json# Attach ECR policy for image operations
aws iam attach-role-policy \
--role-name GitHubActionsRole \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess# Attach ECS policy for deployment
aws iam attach-role-policy \
--role-name GitHubActionsRole \
--policy-arn arn:aws:iam::aws:policy/AmazonECS_FullAccessTHUMBPRINT=$(echo | openssl s_client -servername token.actions.githubusercontent.com \
-connect token.actions.githubusercontent.com:443 2>/dev/null \
| openssl x509 -fingerprint -noout \
| sed 's/://g' | awk -F= '{print tolower($2)}')aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list $THUMBPRINTExpected output:
{
"OpenIDConnectProviderArn": "arn:aws:iam::<account-id>:oidc-provider/token.actions.githubusercontent.com"
}The table below lists all secrets required for the workflow in GitHub.
| Name | Last updated |
|---|---|
| AWS_ACCOUNT_ID | Unique identifier for the AWS account |
| AWS_REGION | 1 The AWS region where resources are deployed (e.g., eu-north-1) |
| AWS_ROLE_ARN | IAM Role ARN assumed by the service for resource access |
| CLUSTER_NAME | The name of the target ECS or EKS cluster |
| ECR_REPOSITORY | The name of the Amazon ECR container image repository |
| SERVICE_NAME | The name of the target ECS service within the cluster |
Assuming that there is only the Application Load Balancer.
# Get the DNS name of the Application Load Balancer
ALB_DNS=$( aws elbv2 describe-load-balancers --query 'LoadBalancers[*].[DNSName]' --output text)# Check the health endpoint
curl --location "http://${ALB_DNS}/health"
curl -k --location "https://${ALB_DNS}/health"Expected output: is healthy
# Check the phrase endpoint
curl --location "http://${ALB_DNS}/phrase"
curl -k --location "https://${ALB_DNS}/phrase"Expected output: is OK
π§± Terraform & Infrastructure as Code
- Terraform Backends Explained β Spacelift Blog
- Terraform and AWS Application Load Balancers β Medium
- Build a Production-Grade AWS ECS Fargate Cluster with Terraform (Modular & Scalable, CI/CD Ready) β Medium
π³ AWS ECS / Fargate / ECR
- AWS Knowledge Center: ECS Task Exec Format Error β AWS Repost
- Exec Format Error (ECS) β Traffine
- Essential Container Error ECS β Theodo
- Exec Format Error β Macs, Docker Images & AWS ECS/EKS β Medium
- Configuring ECS Fargate and ECR with Private Subnets β Tinfoil Cipher
- Setting Up Cron Tasks on ECS Fargate with VPC Endpoints β Medium
- Setting Up AWS ALB with ECS Fargate Cluster β Medium
- Connecting to a Private ECR Repository Using VPC Endpoints β Edd Grant Blog
- How to Optimize VPC Endpoint for Amazon ECR Images β Cloudkeeper
- Amazon ECS Hidden Costs β FourTheorem
π NGINX, EC2 & Load Balancing
- NGINX on AWS β High Availability with Network Load Balancer β NGINX Docs
- Master AWS Load Balancing β Launch & Load Balance 3 EC2 NGINX Servers β DEV.to
- AWS Scalability & Availability in EC2 using NGINX Web Server β Medium
- Deploy NGINX on EC2 without Public Access using Ansible β Medium
- Deploying a Web Server with AWS: A Hands-on Approach β DEV.to
- What I Learned While Deploying a Resilient NGINX Web Server on AWS β AWS Builder
βοΈ Troubleshooting & AWS Networking
- ELB Target Group Cannot Make Private Subnet EC2 Healthy β AWS Repost
π₯ Video Tutorials
- Terraform State Backends Explained
- AWS ECS Fargate Introduction
- Deploying NGINX on AWS with Load Balancer
- AWS ECS Fargate with Terraform
- AWS ECS Fargate Load Balancer Setup
- ECS Fargate + Terraform Deep Dive
- AWS VPC, ECS, and ECR Setup Tutorial
- Elastic Load Balancer & ECS Target Group Troubleshooting
- Optimizing AWS Networking for ECS
