github-actions-aws-terraform
github-actions-aws-terraform copied to clipboard
Repository to practise Infrastructure-As-Code (IAC) with Github Actions, AWS and Terraform
Github Actions + AWS + Terraform
Repository to demonstrate Infrastructur-As-Code using:
- Github Actions Pipelines
- AWS
- Terraform
Blog about this repository
Please read my blog to know about the background and purpose of this sample repository
Empowering Backend Engineering Team
AWS Cost Warning
Provisioning cloud resources in AWS will incur cost. Please teardown the cloud resources once the usage is completed.
It is advisable to setup billing alerts or billing threshold in AWS account as a reminder to teardown of cloud resources. This will avoid incurring significant bills.
Highlights / Achievements
- Isolated
Containerised
Pipelines- Application
CI+CD
Pipeline - Infrastructure
CD
Pipeline - QA
CI
Pipeline - Auto triggering
QA CI Pipeline
after everysuccessful
Deployment
- Application
- Zero downtime release (using
Immutable Rolling Deployment
) - Auto scaling of
ECS Fargate Tasks
based oncpu
usage - Remote JVM Monitoring
- High Availability AWS resources (
multi availability zones
)- Postgres RDS instance
- NAT Gateway instance with Elastic IP
- ECS Fargate Tasks
- Bastion Jumpoff instance
- Cloudwatch Dashboard
- Infrastructure Dashboard
- Application Dashboard
- Screen Shot 1
- Screen Shot 2
- Screen Shot 3
- Cloudwatch Alerts to Email
- Critical Infrastructure Metrics
- Critical Application Metrics
- Screen Shot
- Log Visualizations and Log analysis with
CloudWatch
- Provision of multiple environments with reusable
terraform modules
-
HTTPS
redirection fromHTTP
throughALB
listener rule - Swagger v2 + OpenApi v3
-
CORS
restriction
AWS Components Used
- Simple Storage Service (s3)
- Dynamo DB
- AWS Certificate Manager (ACM)
- Virtual Private Cloud (VPC)
- Public Subnets
- Private Subnets
- Internet Gateways (IG)
- NAT Gateways + Elastic IPs
- Application Load Balancer (ALB)
- Security Groups (SG)
- Relational Database Service (RDS)
- Elastic Container Registry (ECR)
- Elastic Container Service (ECS) + Fargate
- Auto Scaling Group (ASG)
- Elastic Cloud Compute (EC2) Bastion
- AWS Key Pair
- Cloudwatch Dashboard
- Cloudwatch Logs
- Cloudwatch Metrics
- Cloudwatch Alarms
- AWS Simple Notification Service
Documentation
Accounts Required
-
AWS Account
(Root or IAM) -
Github Account
(Personal or Enterprise) - Registered
Domain
with any Domain Registerar
Tools Required
-
git
cli (any version) -
aws
cli (minimum version1.18.40
) -
terraform
cli (minimum version0.12.24
) -
docer
cli (minimum version19.03.8
) -
curl
cli (any version) -
jq
cli (any version) -
psql
cli (any version) or any Postgres compatible DB client
Prerequisites
Setup
One-off AWS account setup
Setup AWS account with a IAM user
as described below:
Setup AWS account with s3 bucket
and dynamo db
for terraform
to remotely store the terraform state
file as described below:
Setup AWS SSL certificate using ACM
and validate it as described below:
One-off GitHub Actions setup
Add the following values of your AWS account to the GitHub secrets of the repository at
Secret Key | Secret Value |
---|---|
AwsAccessKeyId | Access Key of AWS IAM user |
AwsSecretAccessKey | Secret of the Key of AWS IAM user |
One-off AWS Environment setup
Create a SSH key pair per environment as described below:
One-off GitHub Actions Environment setup
Add the following valuesto the GitHub secrets of the repository at
Secret Key | Secret Value |
---|---|
DatabaseNameDevelopment | development_db |
DatabaseUserDevelopment | development_db_user |
DatabasePasswordDevelopment | development_db_password |
Teardown
One-off AWS Environment tear down
Delete SSH Key Pair generated for the environment as described below:
One-off GitHub Actions Environment tear down
Delete the following secret keys from
Keys:
- DatabaseNameDevelopment
- DatabaseUserDevelopment
- DatabasePasswordDevelopment
One-off GitHub Actions tear down
Delete the following secret keys from
Keys:
- AwsAccessKeyId
- AwsSecretAccessKey
One-off AWS account tear down
Clean up AWS account as described below:
Documentation
aws cli login & configuration
Login to aws
cli as described below:
Verify login:
aws configure list
aws sts get-caller-identity
Provisioning environment
Provision environment from Github Actions Pipeline
Trigger through UI
https://github.com/harishkannarao/github-actions-aws-terraform/actions/workflows/CI-terraform-apply-aws-from-master-development.yml
Click Run Workflow on master branch
Trigger through command line
Generate github personal access token with repo
scope at
Generate Github Personal Token
export GITHUB_PERSONAL_ACCESS_TOKEN=<<your_personal_token>>
curl -v -H "Accept: application/vnd.github.everest-preview+json" \
-H "Authorization: token $GITHUB_PERSONAL_ACCESS_TOKEN" \
--request POST \
--data '{"event_type": "do-terraform-apply-aws-from-master-development"}' \
'https://api.github.com/repos/harishkannarao/github-actions-aws-terraform/dispatches'
View the running pipeline at:
Provision environment from local machine
Provision infrastructure using terraform from local machine as described below:
Setup cname with domain registrar
Get ALB public dns domain
aws s3api get-object --bucket "github-actions-ci" --key "terraform-development.tfstate" /dev/stdout | jq -r '.outputs["alb-dns-name"].value' | grep -E '\S' | grep -v 'null'
Get Cloudfront public dns domain
aws s3api get-object --bucket "github-actions-ci" --key "terraform-development.tfstate" /dev/stdout | jq -r '.outputs["www_distribution_domain_name"].value' | grep -E '\S' | grep -v 'null'
Setup cname
with domain registrar as:
- cname:
docker-http-app-development
pointing to:ALB public dns domain
- cname:
http-web-development
pointing to:Cloudfront public dns domain
Deploy sample http API using Application Pipeline
Open Source Sample Java Spring Boot (API) Application at Github
Trigger through UI
https://github.com/harishkannarao/MySpringBoot/actions/workflows/CI-deploy-master-to-aws-development.yml
Click Run Workflow on master branch
Trigger through command line
Generate github personal access token with repo
scope at
Generate Github Personal Token
export GITHUB_PERSONAL_ACCESS_TOKEN=<<your_personal_token>>
curl -v -H "Accept: application/vnd.github.everest-preview+json" \
-H "Authorization: token $GITHUB_PERSONAL_ACCESS_TOKEN" \
--request POST \
--data '{"event_type": "do-deploy-master-to-aws-development"}' \
'https://api.github.com/repos/harishkannarao/MySpringBoot/dispatches'
View the running pipeline at:
After successful run, the application will be accessible at:
https://docker-http-app-development.harishkannarao.com/health-check
https://docker-http-app-development.harishkannarao.com/swagger-ui.html
https://docker-http-app-development.harishkannarao.com/swagger-ui/index.html?configUrl=/api-docs/swagger-config
Deploy sample frontent web application using Application Pipeline
Open Source Sample ReactJs + NextJs + NodeJs web application at Github
Trigger through UI
https://github.com/harishkannarao/react-nextjs-rest-api/actions/workflows/CI-deploy-main-to-aws-development.yml
Click Run Workflow on master branch
Trigger through command line
Generate github personal access token with repo
scope at
Generate Github Personal Token
export GITHUB_PERSONAL_ACCESS_TOKEN=<<your_personal_token>>
curl -v -H "Accept: application/vnd.github.everest-preview+json" \
-H "Authorization: token $GITHUB_PERSONAL_ACCESS_TOKEN" \
--request POST \
--data '{"event_type": "do-deploy-main-to-aws-development"}' \
'https://api.github.com/repos/harishkannarao/react-nextjs-rest-api/dispatches'
View the running pipeline at:
After successful run, the application will be accessible at:
https://http-web-development.harishkannarao.com
Destroying environment
Destroy environment from Github Actions Pipeline
Trigger through UI
https://github.com/harishkannarao/github-actions-aws-terraform/actions/workflows/CI-terraform-destroy-aws-from-master-development.yml
Click Run Workflow on master branch
Trigger through command line
Generate github personal access token with repo
scope at
Generate Github Personal Token
export GITHUB_PERSONAL_ACCESS_TOKEN=<<your_personal_token>>
curl -v -H "Accept: application/vnd.github.everest-preview+json" \
-H "Authorization: token $GITHUB_PERSONAL_ACCESS_TOKEN" \
--request POST \
--data '{"event_type": "do-terraform-destroy-aws-from-master-development"}' \
'https://api.github.com/repos/harishkannarao/github-actions-aws-terraform/dispatches'
Destroy environment from local machine
Destroy infrastructure using terraform from local machine as described below:
Operational Goodies
Monitoring Dashboard and Alarms
Subscribe an email to SNS Notifications of Alarms
Log analysis and visualization with Insights
Using Cloudwatch Logs Insights
Log analysis with Cloudwatch Logs
Using Cloudwatch Logs Analysis
Download logs to local machine
Download Cloudwatch Logs to local machine
Remote monitoring ECS Fargate JVM
Monitor remote JVM using VisualVM
VisualVM Remote JVM Monitoring
Local port forwarding to ECS Fargate Tasks
Local port forwarding to ECS sercice task
Connecting to AWS RDS instance
Connecting to remote RDS database from local
SSH into Application Instance
SSH into ECS Fargate Service Task
Create terraform graphs with GraphViz
Visualize AWS Infrastructure through Terraform
Quick roll back of deployment
Copying files to bastion
Get remote terraform state file
aws s3api get-object --bucket "github-actions-ci" --key "terraform-development.tfstate" ignored/terraform-development.tfstate
Change region and availaibility zones
Preview the changes
grep -rn 'eu-west-2' .
Replace the region as us-east-1
find . -type f -print0 | xargs -0 sed -i '' 's/eu-west-2/us-east-1/g'
Other Terraform Commands
Validate the terraform template config and syntax
terraform validate -json environments/$ENV_NAME
Adhoc notes
Cost Optimisations
The following items can be improved or optimised to reduce AWS cost per environment:
- Resue
ALB
between multipleECS
services using multipletarget groups
through:-
Host
based routing -
Path
based routing
-
- Reuse
ECS
cluster between multipleECS
services
Further things to explore
-
Internal ALB in private Subnets for internal services
-
Try to terminate the SSL at container level using nginx to ensure end to end encryption