From d8f1ceb8e916c6d428fdfc91efdeb25d38bb4343 Mon Sep 17 00:00:00 2001 From: Rohith Jayawardene Date: Sat, 28 Sep 2024 10:21:32 +0100 Subject: [PATCH 1/5] feat: adding the ability to detect drift and send slack notifications --- .github/workflows/terraform-drift.yml | 141 ++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 .github/workflows/terraform-drift.yml diff --git a/.github/workflows/terraform-drift.yml b/.github/workflows/terraform-drift.yml new file mode 100644 index 0000000..0b77c89 --- /dev/null +++ b/.github/workflows/terraform-drift.yml @@ -0,0 +1,141 @@ +--- +name: Terraform Drift Detection + +on: + # Trigger the workflow on a weekly random day at 9:00 AM + schedule: + - cron: "0 9 * * *" + workflow_dispatch: + inputs: + aws-account-id: + description: "The AWS account ID to deploy to" + required: true + type: string + + aws-role: + default: "${{ github.event.repository.name }}" + description: "The AWS role to assume" + required: false + type: string + + aws-read-role-name: + description: "Overrides the default behavior, and uses a custom role name for read-only access" + required: false + type: string + + aws-write-role-name: + description: "Overrides the default behavior, and uses a custom role name for read-write access" + required: false + type: string + + aws-region: + default: "eu-west-2" + description: "The AWS region to deploy to" + required: false + type: string + +jobs: + terraform-plan: + name: "Terraform Plan" + runs-on: ${{ inputs.runs-on }} + defaults: + run: + working-directory: ${{ inputs.working-directory }} + outputs: + result-auth: ${{ steps.auth.outcome }} + result-init: ${{ steps.init.outcome }} + result-plan: ${{ steps.plan.outcome }} + plan-stdout: ${{ steps.plan.outputs.stdout }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 16 + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: ${{ inputs.terraform-version }} + - name: Retrieve Web Identity Token for AWS Authentication + run: | + curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE + - name: Determine AWS Role + id: role + run: | + if [ "${{ inputs.use-env-as-suffix }}" == "true" ]; then + role_suffix="-${{ inputs.environment }}" + else + role_suffix="" + fi + if [[ "${GITHUB_REF##*/}" == "main" ]]; then + echo "name=${AWS_READWRITE_OVERRIDE_ROLE:-${AWS_ROLE}${role_suffix}}" >> $GITHUB_OUTPUT + else + echo "name=${AWS_READONLY_OVERRIDE_ROLE:-${AWS_ROLE}${role_suffix}-ro}" >> $GITHUB_OUTPUT + fi + - name: Authenticate with AWS + id: auth + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ inputs.aws-region }} + role-session-name: ${{ github.event.repository.name }} + role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ steps.role.outputs.name }} + mask-aws-account-id: "no" + - name: Set terraform-state-key variable + id: state-key + run: | + if [ -n "${{ inputs.terraform-state-key }}" ]; then + echo "name=${{ inputs.terraform-state-key }}" >> $GITHUB_OUTPUT + else + if [ "${{ inputs.use-env-as-suffix }}" == "true" ]; then + echo "name=${{ github.event.repository.name }}-${{ inputs.environment }}.tfstate" >> $GITHUB_OUTPUT + else + echo "name=${{ github.event.repository.name }}.tfstate" >> $GITHUB_OUTPUT + fi + fi + - name: Terraform Init + id: init + run: terraform -chdir=${{ inputs.terraform-dir }} init -backend-config="bucket=${{ inputs.aws-account-id }}-${{ inputs.aws-region }}-tfstate" -backend-config="key=${{ steps.state-key.outputs.name }}" -backend-config="encrypt=true" -backend-config="dynamodb_table=${{ inputs.aws-account-id }}-${{ inputs.aws-region }}-tflock" -backend-config="region=${{ inputs.aws-region }}" + - name: Terraform Validate + id: validate + run: terraform -chdir=${{ inputs.terraform-dir }} validate -no-color + - name: Terraform S3 Backend Check + id: s3-backend-check + run: | + if grep -E '^[^#]*backend\s+"s3"' terraform.tf; then + echo "Terraform configuration references an S3 backend." + else + echo "Terraform configuration does not reference an S3 backend." + exit 1 + fi + - name: Set terraform-values-file variable + run: | + if [ -n "${{ inputs.terraform-values-file }}" ]; then + echo "TF_VAR_FILE=${{ inputs.terraform-values-file }}" >> $GITHUB_ENV + else + echo "TF_VAR_FILE=values/${{ inputs.environment }}.tfvars" >> $GITHUB_ENV + fi + - name: Check for drift and set status + id: check-drift + run: | + if grep -q 'No changes' <(terraform -chdir=${{ inputs.terraform-dir }} plan -var-file=$TF_VAR_FILE -no-color -input=false -out=tfplan -lock-timeout=${{ inputs.terraform-lock-timeout }}); then + echo "No drift detected." + echo "::set-output name=DRIFT_STATUS::no-drift" + else + echo "Drift detected!" + echo "::set-output name=DRIFT_STATUS::drift" + exit 1 # Fail if drift is detected + fi + - name: Send Slack notification if drift is detected + if: steps.check-drift.outputs.DRIFT_STATUS == 'drift' + uses: slackapi/slack-github-action@v1.24.0 + with: + payload: | + { + "channel": "#${{ secrets.SLACK_CHANNEL }}", + "username": "GitHub Actions", + "text": "🚨 Drift Detected (${{ github.repository }})", + "icon_emoji": ":warning:" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} From acd47eceb605e8ebb4302609754d8f086e06a9f7 Mon Sep 17 00:00:00 2001 From: Rohith Jayawardene Date: Fri, 4 Oct 2024 10:43:35 +0100 Subject: [PATCH 2/5] docs: adding the documentation and the updates --- .github/workflows/terraform-drift.yml | 12 ++++--- docs/terraform-drift.md | 50 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 docs/terraform-drift.md diff --git a/.github/workflows/terraform-drift.yml b/.github/workflows/terraform-drift.yml index 0b77c89..032c24d 100644 --- a/.github/workflows/terraform-drift.yml +++ b/.github/workflows/terraform-drift.yml @@ -2,10 +2,12 @@ name: Terraform Drift Detection on: - # Trigger the workflow on a weekly random day at 9:00 AM - schedule: - - cron: "0 9 * * *" workflow_dispatch: + secrets: + slack-webhook-url: + description: "The Slack webhook URL" + required: false + inputs: aws-account-id: description: "The AWS account ID to deploy to" @@ -78,9 +80,9 @@ jobs: uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ${{ inputs.aws-region }} + mask-aws-account-id: "no" role-session-name: ${{ github.event.repository.name }} role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ steps.role.outputs.name }} - mask-aws-account-id: "no" - name: Set terraform-state-key variable id: state-key run: | @@ -138,4 +140,4 @@ jobs: "icon_emoji": ":warning:" } env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_URL: ${{ secrets.slack-webhook-url }} diff --git a/docs/terraform-drift.md b/docs/terraform-drift.md new file mode 100644 index 0000000..4c59d55 --- /dev/null +++ b/docs/terraform-drift.md @@ -0,0 +1,50 @@ +# Terraform Draft Workflow for AWS Infrastructure + +This workflow is used to run an scheduled or manually triggered drift detection on AWS infrastructure. In order to trigger the workflow, firstly the workflow must be referenced from the calling workflow flow, see below. + +## Usage + +Create a new workflow file in your Terraform repository (e.g. `.github/workflows/terraform.yml`) with the below contents: + +```yml +name: Terraform +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + terraform: + uses: appvia/appvia-cicd-workflows/.github/workflows/terraform-plan-and-apply-aws.yml@main + name: Plan and Apply + with: + aws-account: + aws-role: +``` + +And we can create another workflow file in your Terraform repository (e.g. `.github/workflows/terraform-drift.yml`) with the below contents: + +```yml +name: Terraform +on: + workflow_dispatch: + scheduled: + - cron: "0 0 * * *" + +jobs: + terraform-drift: + uses: appvia/appvia-cicd-workflows/.github/workflows/terraform-drift.yml@main + name: Drift Detection + with: + aws-account: + aws-role: +``` + +- `aws-account` is the AWS account number where the infrastructure is deployed. +- `aws-role` inputs are optional and will default to the repository name. + +**Note:** This template may change over time, so it is recommended that you point to a tagged version rather than the main branch. From 0dedd6b52b3f851ce614b97fbdb7146bfede54ed Mon Sep 17 00:00:00 2001 From: Rohith Jayawardene Date: Thu, 10 Oct 2024 18:04:32 +0100 Subject: [PATCH 3/5] chore: increasing the compression level on the upload --- .github/workflows/terraform-plan-and-apply-aws.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/terraform-plan-and-apply-aws.yml b/.github/workflows/terraform-plan-and-apply-aws.yml index 19b2416..86247f8 100644 --- a/.github/workflows/terraform-plan-and-apply-aws.yml +++ b/.github/workflows/terraform-plan-and-apply-aws.yml @@ -346,6 +346,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: tfplan-${{ inputs.environment }} + compression-level: 9 path: "tfplan*" retention-days: 14 - name: Optional Additional Directory Upload @@ -357,9 +358,10 @@ jobs: if: inputs.additional-dir uses: actions/upload-artifact@v4 with: + name: additional-dir-${{ inputs.environment }} + compression-level: 9 if-no-files-found: error include-hidden-files: true - name: additional-dir-${{ inputs.environment }} path: ${{ inputs.additional-dir }} retention-days: 14 From 1dd8da02eca10bfe8e2ff6394a88245c19a43a95 Mon Sep 17 00:00:00 2001 From: Rohith Jayawardene Date: Thu, 10 Oct 2024 18:17:41 +0100 Subject: [PATCH 4/5] chore: hacking around while we workout a fix --- .github/workflows/terraform-plan-and-apply-aws.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/terraform-plan-and-apply-aws.yml b/.github/workflows/terraform-plan-and-apply-aws.yml index 86247f8..ac8ac0e 100644 --- a/.github/workflows/terraform-plan-and-apply-aws.yml +++ b/.github/workflows/terraform-plan-and-apply-aws.yml @@ -264,7 +264,8 @@ jobs: result-validate: ${{ steps.validate.outcome }} result-s3-backend-check: ${{ steps.s3-backend-check.outcome }} result-plan: ${{ steps.plan.outcome }} - plan-stdout: ${{ steps.plan.outputs.stdout }} + #plan-stdout: ${{ steps.plan.outputs.stdout }} + plan-stdout: "" steps: - name: Checkout Repository uses: actions/checkout@v4 From bd029c54688e4aca340f4ada8180c6a753752780 Mon Sep 17 00:00:00 2001 From: Rohith Jayawardene Date: Thu, 10 Oct 2024 18:21:27 +0100 Subject: [PATCH 5/5] Revert "chore: hacking around while we workout a fix" This reverts commit 1dd8da02eca10bfe8e2ff6394a88245c19a43a95. --- .github/workflows/terraform-plan-and-apply-aws.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/terraform-plan-and-apply-aws.yml b/.github/workflows/terraform-plan-and-apply-aws.yml index ac8ac0e..86247f8 100644 --- a/.github/workflows/terraform-plan-and-apply-aws.yml +++ b/.github/workflows/terraform-plan-and-apply-aws.yml @@ -264,8 +264,7 @@ jobs: result-validate: ${{ steps.validate.outcome }} result-s3-backend-check: ${{ steps.s3-backend-check.outcome }} result-plan: ${{ steps.plan.outcome }} - #plan-stdout: ${{ steps.plan.outputs.stdout }} - plan-stdout: "" + plan-stdout: ${{ steps.plan.outputs.stdout }} steps: - name: Checkout Repository uses: actions/checkout@v4