diff --git a/.github/workflows/infra_drift_detection.yml b/.github/workflows/infra_drift_detection.yml index 7f2e8031d..e7a50172d 100644 --- a/.github/workflows/infra_drift_detection.yml +++ b/.github/workflows/infra_drift_detection.yml @@ -19,7 +19,6 @@ on: required: false default: false - env: ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} @@ -47,8 +46,16 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK steps: + # Exit with error if SLACK_WEBHOOK_URL is not set + - name: Check Slack Webhook + run: | + if [ -z "$SLACK_WEBHOOK_URL" ]; then + echo "SLACK_WEBHOOK_URL is not set. Please set the secret." + exit 1 + fi + # Set the directory where the Terraform files are located - # The directory the value is then available in ${{ steps.directory.outputs.dir }} + # The directory value is then available in ${{ steps.directory.outputs.dir }} - name: Set directory id: directory env: @@ -61,17 +68,19 @@ jobs: echo "Environment must be provided." exit 1 else + # Remove trailing slash from BASE_PATH if present + BASE_PATH="${BASE_PATH%/}" # The directory is expected to be in the format # infra/resources/${{ inputs.environment }} # Example: infra/resources/prod printf "dir=%q/%q" "$BASE_PATH" "$ENVIRONMENT" >> "$GITHUB_OUTPUT" fi - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4 name: Checkout - name: Azure Login - uses: azure/login@v2 # v2.0.0 + uses: azure/login@a65d910e8af852a8061c627c456678983e180302 # pin@v2 with: client-id: ${{ env.ARM_CLIENT_ID }} tenant-id: ${{ env.ARM_TENANT_ID }} @@ -81,19 +90,24 @@ jobs: id: set-terraform-version run: | set -eu - terraform_version=$(cat .terraform-version) - printf "terraform_version=$terraform_version" >> "$GITHUB_OUTPUT" + if [ -f .terraform-version ]; then + terraform_version=$(cat .terraform-version) + else + echo "Terraform version file not found." + exit 1 + fi + echo "terraform_version=$terraform_version" >> "$GITHUB_OUTPUT" - - uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v3.0.0 + - uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # pin@v3.0.0 name: Setup Terraform with: terraform_version: ${{ steps.set-terraform-version.outputs.terraform_version }} - + - name: Terraform Init working-directory: ${{ steps.directory.outputs.dir }} run: | terraform init - + # Run Terraform Plan # The plan output is saved in a file and then processed to remove unnecessary lines # The step never fails but the result is checked in the next step @@ -102,25 +116,34 @@ jobs: id: plan working-directory: ${{ steps.directory.outputs.dir }} run: | + set -uo pipefail terraform plan -no-color -detailed-exitcode -out=plan.tfplan | grep -v "hidden-link:" - if [ $? -eq 1 ]; then + EXIT_CODE=${PIPESTATUS[0]} + if [ "$EXIT_CODE" -eq 1 ]; then echo "::error::Terraform plan exited with an error" - echo "drift_found=false" >> $GITHUB_OUTPUT exit 1 + elif [ "$EXIT_CODE" -eq 2 ]; then + echo "::notice::Terraform plan detected changes" fi - name: Drift Detection id: drift working-directory: ${{ steps.directory.outputs.dir }} run: | + set -euo pipefail + + if [ ! -f plan.tfplan ]; then + echo "::error::Terraform plan file not found." + exit 1 + fi + terraform show -no-color -json plan.tfplan > plan.json NO_CHANGES=$(jq 'if .resource_changes then [.resource_changes[] | select(.change.actions | index("create") or index("update") or index("delete"))] | length else 0 end' plan.json) if [ "$NO_CHANGES" -eq 0 ]; then echo "No drifts in this configuration" echo "drift_found=false" >> $GITHUB_OUTPUT - exit 0 else echo "Drift detected:" @@ -130,101 +153,96 @@ jobs: TO_ADD=$(jq '[.resource_changes[] | select(.change.actions | index("create"))] | length' plan.json) TO_CHANGE=$(jq '[.resource_changes[] | select(.change.actions | index("update"))] | length' plan.json) TO_DESTROY=$(jq '[.resource_changes[] | select(.change.actions | index("delete"))] | length' plan.json) - + echo " - Resources to add: $TO_ADD" echo " - Resources to change: $TO_CHANGE" echo " - Resources to destroy: $TO_DESTROY" - - # Salva le variabili nell'ambiente per l'utilizzo nei passaggi successivi + + # Save the variables in the environment for use in the next steps echo "TO_ADD=$TO_ADD" >> $GITHUB_ENV echo "TO_CHANGE=$TO_CHANGE" >> $GITHUB_ENV echo "TO_DESTROY=$TO_DESTROY" >> $GITHUB_ENV - - exit 1 fi + continue-on-error: true - - name: Get workflow data + - name: Get Workflow Data in order to send notifications + if: ${{ steps.drift.outputs.drift_found == 'true' || failure() }} id: workflow_data - run: | - workflow=$(gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - /repos/${{ github.repository }}/actions/runs/${{ github.run_id }}) - - workflow_url=$(echo $workflow | jq -r '.html_url') - workflow_name=$(echo $workflow | jq -r '.display_title') - repo_url=$(echo $workflow | jq -r '.repository.html_url') - repo_name=$(echo $workflow | jq -r '.repository.full_name') - commit_sha=$(echo $workflow | jq -r '.head_sha') - commit_message=$(echo $workflow | jq -r '.head_commit.message') - committer_name=$(echo $workflow | jq -r '.head_commit.committer.name') - - echo "workflow_url=${workflow_url}" >> $GITHUB_OUTPUT - echo "workflow_name=${workflow_name}" >> $GITHUB_OUTPUT - echo "repo_url=${repo_url}" >> $GITHUB_OUTPUT - echo "repo_name=${repo_name}" >> $GITHUB_OUTPUT - echo "commit_sha=${commit_sha}" >> $GITHUB_OUTPUT - echo "commit_url=${repo_url}/commit/${commit_sha}" >> $GITHUB_OUTPUT - echo "commit_message=${commit_message}" >> $GITHUB_OUTPUT - echo "committer_name=${committer_name}" >> $GITHUB_OUTPUT - env: - GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} - + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # pin@v7 + with: + script: | + const run = await github.rest.actions.getWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.runId + }); + const data = run.data; + // Set output for each value + core.setOutput("workflow_url", data.html_url); + core.setOutput("workflow_name", data.display_title); + core.setOutput("repo_url", data.repository.html_url); + core.setOutput("repo_name", data.repository.full_name); + core.setOutput("commit_sha", data.head_sha); + core.setOutput("commit_message", data.head_commit.message); + core.setOutput("commit_url", `${data.repository.html_url}/commit/${data.head_sha}`); + core.setOutput("committer_name", data.head_commit.committer.name); + # Reference: https://github.com/slackapi/slack-github-action - name: Drift Notification - if: ${{ always() && env.SLACK_WEBHOOK_URL && steps.drift.outputs.drift_found == 'true' }} + if: ${{ steps.drift.outputs.drift_found == 'true' }} id: drift_notify - uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e #v1.26.0 + uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # pin@v1.26.0 with: payload: | { - "text":"${{ job.status }}", - "blocks":[ + "text": "Drift Detected", + "blocks": [ { - "type":"section", - "text":{ - "type":"mrkdwn", - "text":":x: Drift detected! *${{ steps.workflow_data.outputs.workflow_name }}* action is ${{ job.status }}" - } + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":x: Drift detected by *${{ steps.workflow_data.outputs.workflow_name }}*." + } }, { - "type":"section", - "text":{ - "type":"mrkdwn", - "text":"* ${{ steps.workflow_data.outputs.workflow_name }}* results:\n:shipit: *Owner*: ${{ steps.workflow_data.outputs.committer_name }}\n:diamond_shape_with_a_dot_inside: *Commit URL*: <${{ steps.workflow_data.outputs.commit_url }}|${{ steps.workflow_data.outputs.commit_sha }}>\n:envelope: *Commit message*: ${{ steps.workflow_data.outputs.commit_message }}\n:page_with_curl: *Terraform* plan results:\n:heavy_plus_sign: Resource to add: ${{ env.TO_ADD }}\n:wavy_dash: Resource to change: ${{ env.TO_CHANGE }}\n:heavy_minus_sign: Resource to destroy: ${{ env.TO_DESTROY }}\n *Linked Repo*: <${{ steps.workflow_data.outputs.repo_url }}|${{ steps.workflow_data.outputs.repo_name }}>\n" - } + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*${{ steps.workflow_data.outputs.workflow_name }}* results:\n:shipit: *Owner*: ${{ steps.workflow_data.outputs.committer_name }}\n:diamond_shape_with_a_dot_inside: *Commit URL*: <${{ steps.workflow_data.outputs.commit_url }}|${{ steps.workflow_data.outputs.commit_sha }}>\n:envelope: *Commit message*: ${{ steps.workflow_data.outputs.commit_message }}\n:page_with_curl: *Terraform* plan results:\n:heavy_plus_sign: Resource to add: ${{ env.TO_ADD }}\n:wavy_dash: Resource to change: ${{ env.TO_CHANGE }}\n:heavy_minus_sign: Resource to destroy: ${{ env.TO_DESTROY }}\n *Linked Repo*: <${{ steps.workflow_data.outputs.repo_url }}|${{ steps.workflow_data.outputs.repo_name }}>" + } } ] } - env: - SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: ${{ env.SLACK_WEBHOOK_TYPE }} - name: Failure Notification - if: ${{ failure() && env.SLACK_WEBHOOK_URL }} + if: ${{ failure() }} id: failure_notify - uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e #v1.26.0 + uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # pin@v1.26.0 with: payload: | { - "text":"${{ job.status }}", - "blocks":[ + "text": "Workflow Failed", + "blocks": [ { - "type":"section", - "text":{ - "type":"mrkdwn", - "text":":x: Something went wrong! *${{ steps.workflow_data.outputs.workflow_name }}* action is ${{ job.status }}" + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":x: The workflow *${{ steps.workflow_data.outputs.workflow_name }}* has failed." } }, { - "type":"section", - "text":{ - "type":"mrkdwn", - "text":"Check the Workflow:\n *URL*: ${{ steps.workflow_data.outputs.workflow_url }}\n *Linked Repo*: <${{ steps.workflow_data.outputs.repo_url }}|${{ steps.workflow_data.outputs.repo_name }}>\n" + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Check the Workflow:\n *URL*: ${{ steps.workflow_data.outputs.workflow_url }}\n *Linked Repo*: <${{ steps.workflow_data.outputs.repo_url }}|${{ steps.workflow_data.outputs.repo_name }}>\n" } } ] } - env: - SLACK_WEBHOOK_URL: ${{ env.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: ${{ env.SLACK_WEBHOOK_TYPE }} \ No newline at end of file + + # Exit with an error if drift is detected, to ensure + # the failure is reflected in the README badge + - name: Exit with error + if: ${{ failure() || steps.drift.outputs.drift_found == 'true' }} + run: exit 1 +