Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions .github/workflows/terraform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
name: "Terraform CICD - AWS ECS Instance"

on:
pull_request:
branches:
- main
push:
branches:
- main

permissions:
contents: read
issues: write
pull-requests: write

env:
# Verbosity setting for Terraform logs
TF_LOG: ERROR
# Credentials for deployment to AWS
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# S3 bucket for the Terraform state
BUCKET_TF_STATE: ${{ secrets.BUCKET_TF_STATE}}

jobs:
# Terraform Dev CICD
terraform-dev:
name: "Terraform Infra CICD Dev"
runs-on: ubuntu-latest
defaults:
run:
shell: bash
working-directory: infra
environment: dev

steps:
- name: Checkout the repository to the runner
uses: actions/checkout@v4

- name: Setup Terraform with specified version on the runner
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.11.3

- name: Terraform init dev
id: init-dev
run: terraform init -reconfigure -backend-config=bucket=$BUCKET_TF_STATE

# Quality checks DEV
- name: Terraform format
id: fmt-dev
if: github.event_name == 'pull_request'
run: terraform fmt -check

- name: Terraform validate
id: validate-dev
if: github.event_name == 'pull_request'
run: terraform validate

- name: Terraform plan - dev
id: plan-dev
if: github.event_name == 'pull_request'
run: terraform plan -var-file=envs/dev.tfvars -no-color -input=false
continue-on-error: true

- uses: actions/github-script@v7
if: github.event_name == 'pull_request'
env:
PLAN: "terraform\n${{ steps.plan-dev.outputs.stdout }}"
with:
script: |
const output = `#### Terraform Format and Style - DEV 🖌\`${{ steps.fmt-dev.outcome }}\`
#### Terraform Initialization - DEV ⚙️\`${{ steps.init-dev.outcome }}\`
#### Terraform Validation - DEV 🤖\`${{ steps.validate-dev.outcome }}\`
#### Terraform Plan - DEV 📖\`${{ steps.plan-dev.outcome }}\`

<details><summary>Show Plan</summary>

\`\`\`\n
${process.env.PLAN}
\`\`\`

</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;

github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})

- name: Terraform Plan Status
if: steps.plan-dev.outcome == 'failure'
run: exit 1

- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve -input=false

# # Terraform Prod CI
# terraform-prod-ci:
# name: "Terraform Infra CI Prod"
# runs-on: ubuntu-latest
# defaults:
# run:
# shell: bash
# environment: prod

# steps:
# - name: Checkout the repository to the runner
# uses: actions/checkout@v4

# - name: Setup Terraform with specified version on the runner
# uses: hashicorp/setup-terraform@v3
# with:
# terraform_version: 1.11.3

# - name: Terraform init prod
# id: init-prod
# run: terraform init -reconfigure -backend-config=bucket=$BUCKET_TF_STATE #Create new bucket for prod

# # Quality checks PROD
# - name: Terraform format
# id: fmt-prod
# if: github.event_name == 'pull_request'
# run: terraform fmt -check

# - name: Terraform validate
# id: validate-prod
# if: github.event_name == 'pull_request'
# run: terraform validate

# - name: Terraform plan - prod
# id: plan-prod
# if: github.event_name == 'pull_request'
# run: terraform plan -var-file=envs/prod.tfvars -no-color -input=false
# continue-on-error: true

# - uses: actions/github-script@v6
# if: github.event_name == 'pull_request'
# env:
# PLAN: "terraform\n${{ steps.plan-prod.outputs.stdout }}"
# with:
# script: |
# const output = `#### Terraform Format and Style - PROD 🖌\`${{ steps.fmt-prod.outcome }}\`
# #### Terraform Initialization - PROD ⚙️\`${{ steps.init-prod.outcome }}\`
# #### Terraform Validation - PROD 🤖\`${{ steps.validate-prod.outcome }}\`
# #### Terraform Plan - PROD 📖\`${{ steps.plan-prod.outcome }}\`

# <details><summary>Show Plan</summary>

# \`\`\`\n
# ${process.env.PLAN}
# \`\`\`

# </details>
# *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;

# github.rest.issues.createComment({
# issue_number: context.issue.number,
# owner: context.repo.owner,
# repo: context.repo.repo,
# body: output
# })

# - name: Terraform Plan Status
# if: steps.plan-prod.outcome == 'failure'
# run: exit 1

# # Terraform PROD CD
# terraform-prod-cd:
# name: "Terraform Infra CD Prod"
# needs: [terraform-dev, terraform-prod-ci]
# runs-on: ubuntu-latest
# defaults:
# run:
# shell: bash
# environment: prod

# steps:
# - name: Checkout the repository to the runner
# uses: actions/checkout@v3

# - name: Setup Terraform with specified version on the runner
# uses: hashicorp/setup-terraform@v2
# with:
# terraform_version: 1.3.9

# - name: Terraform init prod
# id: init-prod
# run: terraform init -reconfigure -backend-config=bucket=$BUCKET_TF_STATE

# - name: Terraform Apply
# if: github.ref == 'refs/heads/main' && github.event_name == 'push' # only on push/merge to main
# run: terraform apply -auto-approve -input=false
32 changes: 6 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,38 @@ The VPC, public subnets, Internet Gateway, and Terraform remote-state bucket (S3

---

## Repo structure

```
infra/
├─ _backend.tf # remote state (S3 + DynamoDB)
├─ _providers.tf # AWS provider / default tags
├─ _variables.tf # all inputs
├─ cluster.tf # ECS cluster (awsvpc)
├─ asg_capacity.tf # ASG + capacity provider
├─ task_definition.tf # image, ports, health check, logs
├─ ecs_service.tf # service + load balancer attachment
├─ cloudwatch_logs.tf
└─ envs/
├─ dev.tfvars
└─ prod.tfvars
```

---

## 1 · Initialise Terraform (one‑time per env)

```bash
cd infra
terraform init -backend-config="bucket=node-app-infra-tfstate-dev" -backend-config="profile=node-app-terraform-dev"
terraform init -reconfigure -backend-config=bucket=node-app-infra-tfstate-dev -backend-config=profile=node-app-terraform-dev
```

---

## 2 · Build & push the container image
## 2 · Build & push the container image (Apply new version tag where appropriate)

```bash
docker buildx create --name multi --use 2>/dev/null || true
docker buildx build --platform linux/amd64 -t nrampling/demo-node-app:1.0.0 --push .
docker buildx build --platform linux/amd64 -t nrampling/demo-node-app:1.0.2 --push .
```

Update the image tag in `infra/envs/dev.tfvars`:

```hcl
docker_image = "nrampling/demo-node-app:1.0.0"
node_app_image = "nrampling/demo-node-app:1.0.2"
```

---

## 3 · Deploy with Terraform

```bash
AWS_PROFILE=node-app-terraform-dev terraform plan -var-file=envs/dev.tfvars
AWS_PROFILE=node-app-terraform-dev terraform plan -var-file=envs/dev.tfvars

AWS_PROFILE=node-app-terraform-dev terraform apply -var-file=envs/dev.tfvars
```

### Outputs (example)
### Outputs (example only - plug in aws account)

```text
alb_dns_name = dev-app-alb-123456.ap-southeast-2.elb.amazonaws.com
Expand Down
2 changes: 1 addition & 1 deletion infra/envs/dev.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ alb_public_subnet_ids = ["subnet-055583b9b74d44b56", "subnet-0e9b56625d00f6c88",

vpc_id = "vpc-0fabd74c01d8c9d4a"

node_app_image = "nrampling/demo-node-app:1.0.2"
node_app_image = "nrampling/demo-node-app:1.0.3"
2 changes: 1 addition & 1 deletion k8s/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ spec:
spec:
containers:
- name: demo-node-app
image: nrampling/demo-node-app:1.0.0
image: nrampling/demo-node-app:1.0.3
ports:
- containerPort: 3000
---
Expand Down