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
233 changes: 233 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
name: Terraform CD

on:
push:
branches:
- main
paths:
- 'terraform/**'
- '.github/workflows/cd.yml'

workflow_dispatch:
inputs:
plan_only:
description: 'Run plan only (no apply)'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'

destroy:
description: 'Destroy all infrastructure (DANGEROUS)'
required: false
default: 'false'
type: choice
options:
- 'true'
- 'false'

permissions:
contents: read
id-token: write
issues: write

env:
AWS_REGION: us-west-2
TF_WORKING_DIR: terraform
TERRAFORM_VERSION: 1.6.0

jobs:
terraform-plan:
name: Plan Infrastructure Changes
runs-on: ubuntu-latest

environment:
name: production

defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}

outputs:
has_changes: ${{ steps.plan.outputs.exitcode == 2 }}

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_SECRET_ACCESS_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TERRAFORM_VERSION }}
terraform_wrapper: false

- name: Terraform Format Check
run: terraform fmt -check -recursive

- name: Terraform Init
run: terraform init -upgrade

- name: Terraform Validate
run: terraform validate

- name: Terraform Plan
id: plan
run: |
terraform plan -detailed-exitcode -out=tfplan -input=false || exit_code=$?
echo "exitcode=$exit_code" >> $GITHUB_OUTPUT
terraform show tfplan -no-color
continue-on-error: true

- name: Upload Plan Artifact
if: steps.plan.outputs.exitcode == 2
uses: actions/upload-artifact@v4
with:
name: terraform-plan-${{ github.sha }}
path: ${{ env.TF_WORKING_DIR }}/tfplan
retention-days: 1

- name: Create Deployment Summary
if: always()
run: |
echo "## Terraform Plan Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Workflow:** ${{ github.workflow }}" >> $GITHUB_STEP_SUMMARY
echo "- **Triggered by:** ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **Plan Status:** ${{ steps.plan.outcome }}" >> $GITHUB_STEP_SUMMARY
echo "- **Has Changes:** ${{ steps.plan.outputs.exitcode == 2 }}" >> $GITHUB_STEP_SUMMARY

terraform-apply:
name: Apply Infrastructure Changes
runs-on: ubuntu-latest

needs: terraform-plan
if: |
needs.terraform-plan.result == 'success' &&
needs.terraform-plan.outputs.has_changes == 'true' &&
github.event.inputs.plan_only != 'true'

defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_SECRET_ACCESS_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TERRAFORM_VERSION }}

- name: Terraform Init
run: terraform init -upgrade

- name: Download Plan Artifact
uses: actions/download-artifact@v4
with:
name: terraform-plan-${{ github.sha }}
path: ${{ env.TF_WORKING_DIR }}

- name: Terraform Apply
id: apply
run: |
echo "Applying Terraform changes..."
terraform apply -auto-approve -input=false tfplan

- name: Get Terraform Outputs
id: outputs
if: steps.apply.outcome == 'success'
run: |
echo "instance_id=$(terraform output -raw instance_id)" >> $GITHUB_OUTPUT
echo "instance_public_ip=$(terraform output -raw instance_public_ip)" >> $GITHUB_OUTPUT
echo "instance_private_ip=$(terraform output -raw instance_private_ip)" >> $GITHUB_OUTPUT

- name: Create Deployment Summary
if: always()
run: |
echo "## Terraform Apply Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Workflow:** ${{ github.workflow }}" >> $GITHUB_STEP_SUMMARY
echo "- **Triggered by:** ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **Apply Status:** ${{ steps.apply.outcome }}" >> $GITHUB_STEP_SUMMARY

if [ "${{ steps.apply.outcome }}" == "success" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "### EC2 Instance Information" >> $GITHUB_STEP_SUMMARY
echo "- **Instance ID:** ${{ steps.outputs.outputs.instance_id }}" >> $GITHUB_STEP_SUMMARY
echo "- **Public IP:** ${{ steps.outputs.outputs.instance_public_ip }}" >> $GITHUB_STEP_SUMMARY
echo "- **Private IP:** ${{ steps.outputs.outputs.instance_private_ip }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Connect to Instance" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "ssh -i ~/.ssh/ashish-test-kp.pem ec2-user@${{ steps.outputs.outputs.instance_public_ip }}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
fi

- name: Notify on Failure
if: failure()
run: |
echo "::error::Terraform apply failed. Please check the logs and fix the issues."

terraform-destroy:
name: Destroy Infrastructure
runs-on: ubuntu-latest

if: github.event_name == 'workflow_dispatch' && github.event.inputs.destroy == 'true'

environment:
name: destroy-production

defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_SECRET_ACCESS_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TERRAFORM_VERSION }}

- name: Terraform Init
run: terraform init -upgrade

- name: Terraform Destroy
run: |
echo "Destroying all infrastructure..."
terraform destroy -auto-approve -input=false

- name: Confirm Destruction
run: |
echo "## Infrastructure Destroyed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All resources have been destroyed." >> $GITHUB_STEP_SUMMARY
echo "- **Triggered by:** ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
echo "- **Timestamp:** $(date)" >> $GITHUB_STEP_SUMMARY
128 changes: 128 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
name: Terraform CI

on:
pull_request:
branches:
- '**'
paths:
- 'terraform/**'
- '.github/workflows/ci.yml'

push:
branches:
- '**'
- '!main'
paths:
- 'terraform/**'
- '.github/workflows/ci.yml'

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

env:
AWS_REGION: us-west-2
TF_WORKING_DIR: terraform
TERRAFORM_VERSION: 1.6.0

jobs:
terraform-validate:
name: Validate Terraform Configuration
runs-on: ubuntu-latest

defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_SECRET_ACCESS_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TERRAFORM_VERSION }}

- name: Terraform Format Check
id: fmt
run: terraform fmt -check -recursive
continue-on-error: true

- name: Terraform Init
id: init
run: terraform init -upgrade -backend=false

- name: Terraform Validate
id: validate
run: terraform validate -no-color

- name: Terraform Plan
id: plan
run: |
terraform init -upgrade
terraform plan -no-color -input=false -out=tfplan
continue-on-error: true

- name: Post Plan to PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
env:
PLAN_OUTPUT: ${{ steps.plan.outputs.stdout }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`

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

\`\`\`terraform
${{ steps.plan.outputs.stdout }}
\`\`\`

</details>

*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Workflow: \`${{ github.workflow }}\`*`;

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

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

- name: Upload Plan Artifact
if: steps.plan.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: terraform-plan
path: ${{ env.TF_WORKING_DIR }}/tfplan
retention-days: 5

terraform-security-scan:
name: Security Scan
runs-on: ubuntu-latest

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Run tfsec
uses: aquasecurity/tfsec-action@v1.0.3
with:
working_directory: ${{ env.TF_WORKING_DIR }}
soft_fail: true
Loading
Loading