Skip to content

feat: Added slack notif support for both apply and destroy #7

feat: Added slack notif support for both apply and destroy

feat: Added slack notif support for both apply and destroy #7

---
run-name: 'Terraform workflow'
on:
workflow_call:
inputs:
working_directory:
required: true
type: string
description: 'Root directory of the terraform where all resources exist.'
provider:
required: true
type: string
description: 'Cloud provider to run the workflow. e.g. azurerm, aws, gcp or digitalocean'
aws_region:
required: false
type: string
default: us-east-2
description: 'AWS region of terraform deployment.'
gcp_region:
required: false
type: string
description: 'GCP region of terraform deployment.'
var_file:
required: false
type: string
description: 'Terraform var file directory. e.g. vars/dev.tfvars'
destroy:
required: false
type: boolean
default: false
description: 'Set true to to destroy terraform infrastructure.'
approvers:
required: false
type: string
description: 'Approvals list to approve apply or destroy'
terraform_version:
type: string
default: 1.3.6
description: 'Required erraform version '
timeout:
required: false
type: number
default: 10
description: 'Timeout for approval step'
minimum-approvals:
required: false
type: number
default: 1
description: 'Minimum approvals required to accept the plan'
skip_approval:
required: false
type: boolean
default: false
description: 'Set true to skip approval step'
token_format:
required: false
type: string
default: access_token
description: 'Output format for the generated authentication token. For OAuth 2.0 access tokens, specify "access_token". For OIDC tokens, specify "id_token". To skip token generation, leave this value empty'
access_token_lifetime:
required: false
type: string
default: 300s
description: 'Desired lifetime duration of the access token, in seconds'
project_id:
required: false
type: string
description: 'ID of the default project to use for future API calls and invocations.'
create_credentials_file:
required: false
type: string
default: true
description: 'If true, the action will securely generate a credentials file which can be used for authentication via gcloud and Google Cloud SDKs.'
secrets:
AZURE_CREDENTIALS:
required: false
description: 'Azure Credentials to install Azure in github runner.'
AZURE_CLIENT_ID:
required: false
description: 'Client ID for service principal in Azure.'
AZURE_SUBSCRIPTION_ID:
required: false
description: 'Subscription ID in Azure.'
AZURE_TENANT_ID:
required: false
description: 'Tenant ID in Azure.'
AZURE_CLIENT_SECRET:
required: false
description: 'Client Secret for the Azure app registration.'
AWS_ACCESS_KEY_ID:
required: false
description: 'AWS Access Key ID to install AWS CLI.'
BUILD_ROLE:
required: false
description: 'AWS OIDC role for aws authentication.'
AWS_SECRET_ACCESS_KEY:
required: false
description: 'AWS Secret access key to install AWS CLI'
AWS_SESSION_TOKEN:
required: false
description: 'AWS Session Token to install AWS CLI'
GCP_CREDENTIALS:
required: false
description: 'The Google Cloud JSON service account key to use for authentication'
DIGITALOCEAN_ACCESS_TOKEN:
required: false
description: 'The DigitalOcean Personal Access Token for Application & API'
env-vars:
required: false
description: 'Pass required environment variables'
WORKLOAD_IDENTITY_PROVIDER:
required: false
description: 'The full identifier of the Workload Identity Provider'
SERVICE_ACCOUNT:
required: false
description: 'The service account to be used'
SLACK_WEBHOOK:
description: "Slack webhook URL"
required: true
jobs:
terraform-workflow:
runs-on: ubuntu-latest
outputs:
tfplanExitCode: ${{ steps.tf-plan.outputs.exitcode }}
steps:

Check failure on line 127 in .github/workflows/terraform_workflow.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/terraform_workflow.yml

Invalid workflow file

You have an error in your yaml syntax on line 127
- name: Checkout
uses: actions/checkout@v4
- name: Set environment variables
run: |
(
cat <<'_EOT'
${{ secrets.env-vars }}
_EOT
) >> "$GITHUB_ENV"
- name: Install AWS CLI
if: ${{ inputs.provider == 'aws' }}
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
role-to-assume: ${{ secrets.BUILD_ROLE }}
aws-region: ${{ inputs.aws_region }}
role-duration-seconds: 900
role-skip-session-tagging: true
- name: Install Azure CLI
if: ${{ inputs.provider == 'azurerm' }}
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: 'Authenticate to Google Cloud'
if: ${{ inputs.provider == 'gcp' }}
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
create_credentials_file: ${{ inputs.create_credentials_file }}
token_format: ${{ inputs.token_format }}
workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.SERVICE_ACCOUNT }}
access_token_lifetime: ${{ inputs.access_token_lifetime }}
project_id: ${{ inputs.project_id }}
- name: Install doctl
if: ${{ inputs.provider == 'digitalocean' }}
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ inputs.terraform_version }}
- name: 'Terraform Format'
if: ${{ inputs.destroy != true }}
id: fmt
uses: 'dflook/terraform-fmt-check@v1'
with:
actions_subcommand: 'fmt'
path: ${{ inputs.working_directory }}
- name: terraform init
run: |
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
cd ${{ inputs.working_directory }}
terraform init
- name: 'Terraform validate'
if: ${{ inputs.destroy != true }}
id: validate
uses: dflook/terraform-validate@v1
with:
path: ${{ inputs.working_directory }}
- name: Terraform Plan
id: tf-plan
run: |
export exitcode=0
cd ${{ inputs.working_directory }}
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
if [ "${{ inputs.destroy }}" = true ]; then
if [ -n "${{ inputs.var_file }}" ]; then
terraform plan -destroy -out tfplan --var-file=${{ inputs.var_file }}
else
terraform plan -destroy -out tfplan
fi
else
if [ -n "${{ inputs.var_file }}" ]; then
terraform plan -out tfplan --var-file=${{ inputs.var_file }}
else
terraform plan -out tfplan
fi
fi
- name: Create String Output
id: tf-plan-string
run: |
cd ${{ inputs.working_directory }}
TERRAFORM_PLAN=$(terraform show -no-color tfplan)
delimiter="$(openssl rand -hex 8)"
echo "summary<<${delimiter}" >> $GITHUB_OUTPUT
echo "## Terraform Plan Output" >> $GITHUB_OUTPUT
echo "<details><summary>Click to expand</summary>" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
echo '```terraform' >> $GITHUB_OUTPUT
echo "$TERRAFORM_PLAN" >> $GITHUB_OUTPUT
echo '```' >> $GITHUB_OUTPUT
echo "</details>" >> $GITHUB_OUTPUT
echo "${delimiter}" >> $GITHUB_OUTPUT
- name: "Accept plan or deny"
if: ${{ inputs.skip_approval == false }}
uses: trstringer/manual-approval@v1
timeout-minutes: ${{ inputs.timeout }}
with:
secret: ${{ github.TOKEN }}
approvers: ${{ inputs.approvers }}
minimum-approvals: ${{ inputs.minimum-approvals }}
issue-title: "Terraform Plan for Infrastructure Update"
- name: terraform apply
if: ${{ inputs.destroy != true }}
run: |
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
if [ -n "${{ inputs.var_file }}" ]; then
cd ${{ inputs.working_directory }}
terraform apply -var-file="${{ inputs.var_file }}" -auto-approve
else
cd ${{ inputs.working_directory }}
terraform apply -auto-approve
fi
- name: Find Errored Terraform State
if: ${{ always() }}
run: |
cd ${{ inputs.working_directory }}
if [ -f "errored.tfstate" ]; then
ls -la errored.tfstate
echo "Uploading errored.tfstate as artifact..."
else
echo "Errored Terraform state file not found."
fi
- name: Upload Errored Terraform State Artifact
if: ${{ always() }} && success() && steps.find_errored_tfstate.outputs['errored_found'] == 'true'
uses: actions/upload-artifact@v4
with:
name: errored_tfstate
path: ${{ inputs.working_directory }}/errored.tfstate
- name: Terraform destroy
if: ${{ inputs.destroy == true }}
id: destroy
run: |
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
if [ -n "${{ inputs.var_file }}" ]; then
cd ${{ inputs.working_directory }}
terraform destroy -var-file="${{ inputs.var_file }}" -auto-approve
else
cd ${{ inputs.working_directory }}
terraform destroy -auto-approve
fi
# - name: Notify Slack
# if: ${{ always() }}
# env:
# SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
# run: |
# if [ "${{ job.status }}" == "success" ]; then
# STATUS="Success ✅"
# COLOR="good"
# MESSAGE="Terraform deployment completed successfully."
# else
# STATUS="Failed ❌"
# COLOR="danger"
# MESSAGE="Terraform deployment failed. Check logs for details."
# fi
# RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
# curl -X POST -H 'Content-type: application/json' \
# --data "$(jq -n --arg color "$COLOR" --arg status "$STATUS" --arg message "$MESSAGE" \
# --arg repo "$GITHUB_REPOSITORY" --arg branch "$GITHUB_REF_NAME" --arg sha "$GITHUB_SHA" \
# --arg run_url "$RUN_URL" \
# '{attachments: [{color: $color, title: ("Terraform Deployment: " + $status), text: $message, fields: [{title: "Repository", value: $repo, short: true}, {title: "Branch", value: $branch, short: true}, {title: "Commit", value: $sha, short: true}, {title: "Run URL", value: $run_url, short: false}]}]}')" \
# $SLACK_WEBHOOK_URL
- name: Notify Slack
if: ${{ always() }}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
run: |
# Determine the operation type (apply or destroy) using a custom environment variable or flag
if [[ "${{ inputs.destroy }}" == "true" ]]; then
OPERATION="Destroy"
else
OPERATION="Apply"
fi
# Check the job status
if [ "${{ job.status }}" == "success" ]; then
STATUS="Success ✅"
COLOR="good"
MESSAGE="Terraform $OPERATION completed successfully."
else
STATUS="Failed ❌"
COLOR="danger"
MESSAGE="Terraform $OPERATION failed. Check logs for details."
fi
# Create the Run URL
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
# Send the notification to Slack
curl -X POST -H 'Content-type: application/json' \
--data "$(jq -n --arg color "$COLOR" --arg status "$STATUS" --arg message "$MESSAGE" \
--arg operation "$OPERATION" --arg repo "$GITHUB_REPOSITORY" --arg branch "$GITHUB_REF_NAME" --arg sha "$GITHUB_SHA" \
--arg run_url "$RUN_URL" \
'{attachments: [{color: $color, title: ("Terraform " + $operation + ": " + $status), text: $message, fields: [{title: "Operation", value: $operation, short: true}, {title: "Repository", value: $repo, short: true}, {title: "Branch", value: $branch, short: true}, {title: "Commit", value: $sha, short: true}, {title: "Run URL", value: $run_url, short: false}]}]}')" \
$SLACK_WEBHOOK_URL
...