Skip to content

Complete Project Automation: EC2 Deployment Using Terraform & GitHub Workflow (deploy.yml) #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
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
223 changes: 223 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
name: EC2 Deploy

on:
push:
branches:
- devops/a3
tags:
- deploy-dev
- deploy-prod

workflow_dispatch:
inputs:
stage:
description: "Select stage to deploy"
required: true
default: dev
type: choice
options:
- dev
- prod

env:
AWS_REGION: ap-south-1
TF_WORKING_DIR: ./terraform

jobs:
deploy:
runs-on: ubuntu-latest

steps:
# Checkout Repository
- name: Checkout repository
uses: actions/checkout@v4

# Configure AWS Credentials
- 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: ${{ env.AWS_REGION }}

# Install Terraform
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3

# Determine Stage - dev/prod defaults to dev
- name: Determine Stage
id: set_stage
run: |
if [[ "${GITHUB_REF}" == "refs/tags/deploy-dev" ]]; then
echo "STAGE=dev" >> $GITHUB_ENV
elif [[ "${GITHUB_REF}" == "refs/tags/deploy-prod" ]]; then
echo "STAGE=prod" >> $GITHUB_ENV
elif [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
echo "STAGE=${{ github.event.inputs.stage }}" >> $GITHUB_ENV
else
echo "STAGE=dev" >> $GITHUB_ENV # fallback
fi

echo "🛠️ Deployment stage: $STAGE"

# Terraform Init & Workspace
- name: Terraform Init & Workspace
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
terraform init
terraform workspace select ${STAGE} || terraform workspace new ${STAGE}

# Terraform Apply (Full Infra)
- name: Terraform Apply
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
terraform apply -var-file="${STAGE}_config.tfvars" -auto-approve \
-var "stage=${STAGE}"

# Get Outputs: App IP, Verifier IP, S3 Bucket
- name: Get Terraform Outputs
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
INSTANCE_IP=$(terraform output -raw instance_public_ip)
S3_BUCKET=$(terraform output -raw s3_log_bucket)
echo "INSTANCE_IP=$INSTANCE_IP" >> $GITHUB_ENV
echo "S3_BUCKET=$S3_BUCKET" >> $GITHUB_ENV

echo "📦 App IP: $INSTANCE_IP"
echo "🪣 S3 Bucket: $S3_BUCKET"

# Wait for App Initialization
- name: Wait for App Initialization
run: |
echo "⏳ Waiting 90 seconds for app EC2 to initialize..."
sleep 90

# Validate App Health
- name: Validate App Health
run: |
echo -e "\n📦 Full Response from App:\n"
curl -s http://${{ env.INSTANCE_IP }}:80 || echo "❌ Failed to get response"
echo -e "\n"
echo "Checking app health at http://${{ env.INSTANCE_IP }}:80"
for i in {1..10}; do
STATUS=$(curl -o /dev/null -s -w "%{http_code}" http://${{ env.INSTANCE_IP }}:80)
if [[ "$STATUS" == "200" ]]; then
echo "✅ App is healthy (HTTP 200)"
exit 0
else
echo "Attempt $i: got HTTP $STATUS"
sleep 10
fi
done
echo "❌ App failed health check"
exit 1

# Provision Verifier EC2
- name: Terraform Apply Verifier EC2
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
terraform apply -var-file="${STAGE}_config.tfvars" \
-target=aws_instance.log_verifier -auto-approve \
-var "stage=${STAGE}"

# Get Verifier IP
- name: Get Verifier IP
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
VERIFIER_IP=$(terraform output -raw verifier_instance_public_ip)
echo "VERIFIER_IP=$VERIFIER_IP" >> $GITHUB_ENV
echo "🔑 Verifier IP: $VERIFIER_IP"

# Setup SSH Key for EC2 Access
- name: Setup SSH Key for EC2 Access
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

# Wait for Verifier EC2 Initialization
- name: Wait for Verifier EC2 Initialization
run: |
echo "⏳ Waiting 60 seconds for verifier EC2 to initialize..."
sleep 60

# SSH into Verifier EC2 and Validate Logs
- name: Validate Logs from Verifier EC2
run: |
echo "🔐 Validating logs in S3 from verifier EC2"

# Retry SSH if EC2 not yet ready
for attempt in {1..5}; do
ssh -o StrictHostKeyChecking=no ubuntu@${VERIFIER_IP} "echo '✅ SSH to verifier successful'" && break
echo "⏳ Verifier not ready, retrying SSH (attempt $attempt)..."
sleep 15
done

# Validate logs in S3
for log in system/cloud-init.log app/my-app.log; do
ssh -o StrictHostKeyChecking=no ubuntu@${VERIFIER_IP} \
"if aws s3 ls s3://${S3_BUCKET}/${STAGE}/$log > /dev/null 2>&1; then
echo '✅ Found: $log';
else
echo '❌ Missing: $log'; exit 1;
fi"
done

echo "🎉 All required logs are present in S3"

# Print Logs from Verifier EC2
- name: Print Logs from Verifier EC2
run: |
echo "📄 Fetching logs from /mylogs/${STAGE} on verifier EC2"

# Retry SSH if EC2 not yet ready
for attempt in {1..5}; do
ssh -o StrictHostKeyChecking=no ubuntu@${VERIFIER_IP} "echo '✅ SSH to verifier successful for log fetch'" && break
echo "⏳ Verifier not ready for log fetch, retrying SSH (attempt $attempt)..."
sleep 15
done

# Print system log
ssh -o StrictHostKeyChecking=no ubuntu@${VERIFIER_IP} \
"if [ -f /mylogs/${STAGE}/system/cloud-init.log ]; then
echo '📄 ====== system/cloud-init.log ======'
cat /mylogs/${STAGE}/system/cloud-init.log | tail -n 20
else
echo '❌ system/cloud-init.log not found'
fi"

# Print app log
ssh -o StrictHostKeyChecking=no ubuntu@${VERIFIER_IP} \
"if [ -f /mylogs/${STAGE}/app/my-app.log ]; then
echo '📄 ====== app/my-app.log ======'
cat /mylogs/${STAGE}/app/my-app.log | tail -n 20
else
echo '❌ app/my-app.log not found'
fi"

echo "✅ Printed last 20 lines of logs from verifier EC2"

# # Verify Logs in S3 using AWS CLI
# - name: Verify Logs in S3
# run: |
# echo "📦 Checking for logs in S3 bucket: $S3_BUCKET"
# aws s3 ls s3://$S3_BUCKET/${STAGE}/system/cloud-init.log || { echo "❌ system logs missing"; exit 1; }
# aws s3 ls s3://$S3_BUCKET/${STAGE}/app/my-app.log || { echo "❌ app logs missing"; exit 1; }
# echo "✅ Logs found in S3 bucket"


# Destroy Infrastructure
- name: Destroy Infrastructure
if: always()
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
echo "🗑️ Destroying infrastructure for stage: ${STAGE}"
terraform destroy -var-file="${STAGE}_config.tfvars" -auto-approve \
-var "stage=${STAGE}"

# Cleanup Terraform Workspace
- name: Cleanup Terraform Workspace
if: always()
working-directory: ${{ env.TF_WORKING_DIR }}
run: |
terraform workspace select default
terraform workspace delete ${STAGE}
Loading
Loading