diff --git a/.github/workflows/tf-deploy.yml b/.github/workflows/tf-deploy.yml index c2456ba..4db6728 100644 --- a/.github/workflows/tf-deploy.yml +++ b/.github/workflows/tf-deploy.yml @@ -8,6 +8,17 @@ on: - 'infra/**' - '.github/workflows/terraform-*.yml' workflow_dispatch: + inputs: + filter: + description: "Terragrunt unit(s) to apply, space separated (e.g. aws/us-east-2/monitoring). Blank applies everything." + required: false + default: "" + # The Grafana service-account token in aws/us-east-2/monitoring has a 30-day + # TTL and only rotates when that unit is applied. Before per-unit filtering + # every push applied the whole estate, which kept it fresh; now nothing does. + # This re-applies just that unit weekly so the token cannot lapse. + schedule: + - cron: '0 6 * * 1' permissions: contents: read @@ -20,6 +31,8 @@ concurrency: env: TERRAFORM_VERSION: "1.15.8" TERRAGRUNT_VERSION: "0.99.1" + # Unit re-applied by the weekly schedule to rotate the Grafana token. + ROTATION_UNIT: "aws/us-east-2/monitoring" jobs: discover-changes: @@ -38,19 +51,42 @@ jobs: - name: Find changed directories id: find-changes shell: bash + env: + # Read via env, not template interpolation, so the value cannot be + # injected into the shell at expansion time. + DISPATCH_FILTER: ${{ github.event.inputs.filter }} run: | - # For push events, compare with previous commit - if [ "${{ github.event_name }}" = "push" ]; then - BASE_REF="${{ github.event.before }}" - HEAD_REF="${{ github.sha }}" - else - # For workflow_dispatch, we'll scan all directories - echo "Workflow dispatch - will process all directories" + # Scheduled runs exist only to re-apply the credential-rotation unit. + if [ "${{ github.event_name }}" = "schedule" ]; then + echo "Scheduled run - re-applying ${ROTATION_UNIT} to rotate the Grafana token" echo "has_changes=true" >> $GITHUB_OUTPUT - echo "changed_dirs=[]" >> $GITHUB_OUTPUT + echo "changed_dirs=$(jq -cn --arg d "$ROTATION_UNIT" '[$d]')" >> $GITHUB_OUTPUT exit 0 fi + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + if [ -n "$DISPATCH_FILTER" ]; then + # FILTER_ARGS is later interpolated into a string that is eval'd, + # so restrict the input to characters valid in a unit path. + if ! printf '%s' "$DISPATCH_FILTER" | grep -qE '^[A-Za-z0-9._/*[:space:]-]+$'; then + echo "::error::Invalid filter input. Allowed: letters, digits, . _ / * - and spaces." + exit 1 + fi + echo "Manual dispatch - targeting: $DISPATCH_FILTER" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "changed_dirs=$(jq -cn --arg d "$DISPATCH_FILTER" '$d | split(" ") | map(select(length > 0))')" >> $GITHUB_OUTPUT + else + echo "Manual dispatch - will process all directories" + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "changed_dirs=[]" >> $GITHUB_OUTPUT + fi + exit 0 + fi + + # Push events: compare with the previous commit + BASE_REF="${{ github.event.before }}" + HEAD_REF="${{ github.sha }}" + # Find all changed files in infra/ changed_files=$(git diff --name-only "$BASE_REF" "$HEAD_REF" -- infra/ || echo "")