-
-
Notifications
You must be signed in to change notification settings - Fork 35
Closed
Copy link
Description
I'm using the op5dev/tf-via-pr@v13
action in a GitHub Actions workflow to run Terraform plans and post the output to pull requests (PRs). The workflow runs successfully for Terraform plans, but it fails to update the PR with the plan output when triggered manually via workflow_dispatch
. The error message is: [insert exact error message here]. This issue occurs consistently when using custom tf_directory
and tf_workspace
inputs. Any suggestions?
Workflow details
name: Terraform Plan on PR
on:
pull_request:
types: [opened, synchronize]
workflow_dispatch:
inputs:
# checkov:skip=CKV_GHA_7
# reason="This workflow manages team members by design and inputs are validated strictly.
tf_directory:
description: "Directory of the terraform code to plan"
required: true
default: "terraform/production"
type: string
tf_workspace:
description: "Terraform workspace to use"
required: true
default: "default"
type: string
permissions:
actions: read
checks: write
contents: read
pull-requests: write
id-token: write
jobs:
terraform_plan_on_pr:
runs-on: dcloud-runner
env:
AWS_REGION: ${{ vars.DCLOUD_MANAGE_AWS_REGION }}
TERRAFORM_IAM_ROLE_ARN: ${{ vars.DCLOUD_MANAGE_TF_IAM_ASSUME_ROLE }}
GITHUB_APP_CREDS_SECRET_ARN: ${{ vars.DCLOUD_MANAGE_GH_RUNNER_SECRET_ARN }} # GH Actions org/repo vars
steps:
- name: Checkout PR code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.TERRAFORM_IAM_ROLE_ARN }}
role-session-name: "gh-tf-plan-pr-${{ github.run_id }}"
aws-region: ${{ env.AWS_REGION }}
- name: Generate GitHub App Token (for PR commenting and TF GitHub Provider)
id: generate_app_token
uses: actions/create-github-app-token@v2.0.6
with:
app-id: ${{ secrets.GH_RUNNER_IAC_APP_ID }}
private-key: ${{ secrets.GH_RUNNER_APP_PEM }}
owner: cisco-dcloud
repositories: dcloud-vsphere-infra,tf_vsphere_vm
- name: Configure Git for Private Modules
run: |
GHTOKEN="${{ steps.generate_app_token.outputs.token }}"
git config --global \
url."https://x-access-token:'${GHTOKEN}'@github.com/".insteadOf "https://github.com/"
- name: Set Terraform Directory and Workspace
id: set-vars
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "Workflow triggered manually. Using inputs."
echo "TF_DIR=${{ inputs.tf_directory }}" >> "$GITHUB_OUTPUT"
echo "TF_WORKSPACE=${{ inputs.tf_workspace }}" >> "$GITHUB_OUTPUT"
else
echo "Workflow triggered by a Pull Request. Using defaults."
echo "TF_DIR=terraform/production" >> "$GITHUB_OUTPUT"
echo "TF_WORKSPACE=default" >> "$GITHUB_OUTPUT"
fi
- name: Find Pull Request Number
id: find-pr
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
PR_NUMBER=${{ github.event.pull_request.number }}
else
BRANCH_NAME="${{ github.ref_name }}"
PR_NUMBER=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number')
fi
echo "Found PR number: #${PR_NUMBER:-"none"}"
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.10.5"
- name: Provision TF
uses: op5dev/tf-via-pr@v13
with:
token: ${{ steps.generate_app_token.outputs.token }}
command: ${{ github.event_name == 'push' && 'apply' || 'plan' }}
# arg-lock: ${{ github.event_name == 'push' }}
arg-backend-config: manage-account.ini
arg-workspace: ${{ steps.set-vars.outputs.TF_WORKSPACE }}
working-directory: ${{ steps.set-vars.outputs.TF_DIR }}
comment-pr: on-change
Steps to Reproduce
- Trigger the workflow via workflow_dispatch with inputs tf_directory=terraform/production and tf_workspace=default.
- The Terraform plan executes successfully in the specified directory and workspace.
- The tf-via-pr action attempts to post the plan output to the PR using comment-pr: on-change.
- The PR update fails with the error: [insert exact error message].
Expected Behavior
- The tf-via-pr action should post the Terraform plan output as a comment or check run on the associated PR.
Actual Behavior
The PR is not updated, and the workflow logs show: [insert error message or describe behavior, e.g., "failed to update check run" or "no PR comment posted"].
Run # TF show.
# TF show.
terraform -chdir=dcloud_core show tfplan > tf.console.txt
# Diff of changes.
# Filter lines starting with " # " and save to tf.diff.txt, then prepend diff-specific symbols based on specific keywords.
grep '^ # ' tf.console.txt | sed \
-e 's/^ # \(.* be created\)/+ \1/' \
-e 's/^ # \(.* be destroyed\)/- \1/' \
-e 's/^ # \(.* be updated\|.* be replaced\)/! \1/' \
-e 's/^ # \(.* be read\)/~ \1/' \
-e 's/^ # \(.*\)/# \1/' > tf.diff.txt || true
shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
env:
AWS_REGION: us-east-1
TERRAFORM_IAM_ROLE_ARN: arn:aws:iam::126237642437:role/tf_gh_actions_oidc_role
GITHUB_APP_CREDS_SECRET_ARN: arn:aws:secretsmanager:us-east-1:126237642437:secret:/manage/default/gh_iac_runner_keys-WyKXsK
AWS_DEFAULT_REGION: us-east-1
AWS_ACCESS_KEY_ID: ***
AWS_SECRET_ACCESS_KEY: ***
AWS_SESSION_TOKEN: ***
TERRAFORM_CLI_PATH: /home/runner/work/_temp/203f5c35-04eb-4cde-89d0-0608ef725e7c
GH_API: X-GitHub-Api-Version:2022-11-28
GH_TOKEN: ***
TF_CLI_ARGS: -no-color
TF_IN_AUTOMATION: true
TF_INPUT: false
TF_WORKSPACE: sng-dev
Run actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: terraform--f965ee3c92a835683729bfa68b135048.tfplan
path: dcloud_core/tfplan
overwrite: true
if-no-files-found: warn
compression-level: 6
include-hidden-files: false
env:
AWS_REGION: us-east-1
TERRAFORM_IAM_ROLE_ARN: arn:aws:iam::126237642437:role/tf_gh_actions_oidc_role
GITHUB_APP_CREDS_SECRET_ARN: arn:aws:secretsmanager:us-east-1:126237642437:secret:/manage/default/gh_iac_runner_keys-WyKXsK
AWS_DEFAULT_REGION: us-east-1
AWS_ACCESS_KEY_ID: ***
AWS_SECRET_ACCESS_KEY: ***
AWS_SESSION_TOKEN: ***
TERRAFORM_CLI_PATH: /home/runner/work/_temp/203f5c35-04eb-4cde-89d0-0608ef725e7c
GH_API: X-GitHub-Api-Version:2022-11-28
GH_TOKEN: ***
TF_CLI_ARGS: -no-color
TF_IN_AUTOMATION: true
TF_INPUT: false
TF_WORKSPACE: sng-dev
With the provided path, there will be 1 file uploaded
Artifact name is valid!
Root directory input is valid!
Beginning upload of artifact content to blob storage
Uploaded bytes 36091
Finished uploading artifact content to blob storage!
SHA256 digest of uploaded artifact zip is 2909a528f8818f37cb1b6cc6ea5c1a1db97f2d3386755993e2aa176cb1a9b747
Finalizing artifact upload
Artifact terraform--f965ee3c92a835683729bfa68b135048.tfplan.zip successfully finalized. Artifact ID 3465434429
Artifact terraform--f965ee3c92a835683729bfa68b135048.tfplan has been successfully uploaded! Final size is 36091 bytes. Artifact ID is 3465434429
Artifact download URL: https://github.com/cisco-dcloud/dcloud-vsphere-infra/actions/runs/16072504626/artifacts/3465434429
Run # Post output.
# Post output.
# Parse the "tf.command.txt" file.
command=$(cat tf.command.txt)
# Remove each comma-delemited "hide-args" argument from the command.
IFS=',' read -ra hide_args <<< "detailed-exitcode,parallelism,lock,out,var="
for arg in "${hide_args[@]}"; do
command=$(echo "$command" | grep --invert-match "^ -${arg}" || true)
done
# Conversely, show each comma-delemited "show-args" argument in the command.
command_append=""
IFS=',' read -ra show_args <<< "workspace"
for arg in "${show_args[@]}"; do
command_append+=$(echo " -workspace=sng-dev -backend-config=manage-account.ini -check -detailed-exitcode -diff -recursive" | sed 's/ -/\n -/g' | grep "^ -${arg}" || true)
done
# Consolidate 'command', taking both "hide-args" and "show-args" into account.
command=$(echo "$command" | tr -d '\n')$command_append
echo "command=$command" >> "$GITHUB_OUTPUT"
# Parse the "tf.console.txt" file, truncated for character limit.
console=$(head --bytes=42000 tf.console.txt)
if [[ ${#console} -eq 42000 ]]; then console="${console}"$'\n…'; fi
echo "result<<EORESULTTFVIAPR"$'\n'"$console"$'\n'EORESULTTFVIAPR >> "$GITHUB_OUTPUT"
# Parse the "tf.console.txt" file for the summary.
summary=$(awk '/^(Error:|Plan:|Apply complete!|No changes.|Success)/ {line=$0} END {if (line) print line; else print "View output."}' tf.console.txt)
echo "summary=$summary" >> "$GITHUB_OUTPUT"
# If "steps.format.outcome" failed, set syntax highlighting to "diff", otherwise set it to "hcl".
syntax="hcl"
if [[ "skipped" == "failure" ]]; then syntax="diff"; fi
# Add summary to the job status.
check_run=$(gh api /repos/cisco-dcloud/dcloud-vsphere-infra/check-runs/45360128229 --header "$GH_API" --method PATCH --field "output[title]=${summary}" --field "output[summary]=${summary}")
# From "check_run", echo "html_url".
check_url=$(echo "$check_run" | jq --raw-output '.html_url')
echo "check_id=$(echo "$check_run" | jq --raw-output '.id')" >> "$GITHUB_OUTPUT"
run_url=$(echo ${check_url}#step:9:1)
echo "run_url=$run_url" >> "$GITHUB_OUTPUT"
# If "tf.diff.txt" exists, display it within a "diff" block, truncated for character limit.
if [[ -s tf.diff.txt ]]; then
# Get count of lines in "tf.diff.txt" which do not start with "# ".
diff_count=$(grep --invert-match '^# ' tf.diff.txt | wc --lines)
if [[ $diff_count -eq 1 ]]; then diff_change="change"; else diff_change="changes"; fi
# Parse diff of changes, truncated for character limit.
diff_truncated=$(head --bytes=24000 tf.diff.txt)
if [[ ${#diff_truncated} -eq 24000 ]]; then diff_truncated="${diff_truncated}"$'\n…'; fi
echo "diff<<EODIFFTFVIAPR"$'\n'"$diff_truncated"$'\n'EODIFFTFVIAPR >> "$GITHUB_OUTPUT"
diff="
<details><summary>Diff of ${diff_count} ${diff_change}.</summary>
\`\`\`diff
${diff_truncated}
\`\`\`
</details>"
else
diff=""
fi
# Set flags for creating PR comment and tagging actor.
create_comment=""
tag_actor=""
if [[ $exitcode -ne 0 ]]; then
if [[ "on-change" == "on-change" ]]; then create_comment="true"; fi
if [[ "always" == "on-change" ]]; then tag_actor="true"; fi
fi
if [[ "on-change" == "always" ]]; then create_comment="true"; fi
if [[ "always" == "always" ]]; then tag_actor="true"; fi
if [[ "$tag_actor" == "true" ]]; then handle="@"; else handle=""; fi
# Collate body content.
body=$(cat <<EOBODYTFVIAPR
<!-- placeholder-1 -->
\`\`\`fish
${command}
\`\`\`
<!-- placeholder-2 -->
${diff}
<!-- placeholder-3 -->
<details><summary>${summary}</br>
<!-- placeholder-4 -->
###### By ${handle}gowgopal83 at [(view log)](${run_url}).
</summary>
\`\`\`${syntax}
${console}
\`\`\`
</details>
<!-- placeholder-5 -->
<!-- terraform--f965ee3c92a835683729bfa68b135048.tfplan -->
<!-- placeholder-6 -->
EOBODYTFVIAPR
)
# Post output to job summary.
echo "$body" >> $GITHUB_STEP_SUMMARY
echo "comment_body<<EOCOMMENTTFVIAPR"$'\n'"$body"$'\n'EOCOMMENTTFVIAPR >> "$GITHUB_OUTPUT"
# Post PR comment if configured and PR exists.
if [[ "$create_comment" == "true" && "" != "0" ]]; then
# Check if the PR contains a bot comment with the same identifier.
list_comments=$(gh api /repos/cisco-dcloud/dcloud-vsphere-infra/issues//comments --header "$GH_API" --method GET --field per_page=100)
bot_comment=$(echo "$list_comments" | jq --raw-output --arg identifier "terraform--f965ee3c92a835683729bfa68b135048.tfplan" '.[] | select(.user.type == "Bot") | select(.body | contains($identifier)) | .id' | tail -n 1)
if [[ -n "$bot_comment" ]]; then
if [[ "update" == "recreate" ]]; then
# Delete previous comment before posting a new one.
gh api /repos/cisco-dcloud/dcloud-vsphere-infra/issues/comments/${bot_comment} --header "$GH_API" --method DELETE
pr_comment=$(gh api /repos/cisco-dcloud/dcloud-vsphere-infra/issues//comments --header "$GH_API" --method POST --field "body=${body}")
echo "comment_id=$(echo "$pr_comment" | jq --raw-output '.id')" >> "$GITHUB_OUTPUT"
elif [[ "update" == "update" ]]; then
# Update existing comment.
pr_comment=$(gh api /repos/cisco-dcloud/dcloud-vsphere-infra/issues/comments/${bot_comment} --header "$GH_API" --method PATCH --field "body=${body}")
echo "comment_id=$(echo "$pr_comment" | jq --raw-output '.id')" >> "$GITHUB_OUTPUT"
fi
else
# Post new comment.
pr_comment=$(gh api /repos/cisco-dcloud/dcloud-vsphere-infra/issues//comments --header "$GH_API" --method POST --field "body=${body}")
echo "comment_id=$(echo "$pr_comment" | jq --raw-output '.id')" >> "$GITHUB_OUTPUT"
fi
elif [[ "on-change" == "on-change" && "" != "0" ]]; then
# Delete previous comment due to no changes.
list_comments=$(gh api /repos/cisco-dcloud/dcloud-vsphere-infra/issues//comments --header "$GH_API" --method GET --field per_page=100)
bot_comment=$(echo "$list_comments" | jq --raw-output --arg identifier "terraform--f965ee3c92a835683729bfa68b135048.tfplan" '.[] | select(.user.type == "Bot") | select(.body | contains($identifier)) | .id' | tail -n 1)
if [[ -n "$bot_comment" ]]; then
gh api /repos/cisco-dcloud/dcloud-vsphere-infra/issues/comments/${bot_comment} --header "$GH_API" --method DELETE
fi
fi
# Optionally delete the plan file.
if [[ "$PRESERVE_PLAN" != "true" ]]; then
rm --force "dcloud_core/tfplan"
fi
# Clean up files.
rm --force tf.command.txt tf.console.txt tf.diff.txt
shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
env:
AWS_REGION: us-east-1
TERRAFORM_IAM_ROLE_ARN: arn:aws:iam::126237642437:role/tf_gh_actions_oidc_role
GITHUB_APP_CREDS_SECRET_ARN: arn:aws:secretsmanager:us-east-1:126237642437:secret:/manage/default/gh_iac_runner_keys-WyKXsK
AWS_DEFAULT_REGION: us-east-1
AWS_ACCESS_KEY_ID: ***
AWS_SECRET_ACCESS_KEY: ***
AWS_SESSION_TOKEN: ***
TERRAFORM_CLI_PATH: /home/runner/work/_temp/203f5c35-04eb-4cde-89d0-0608ef725e7c
GH_API: X-GitHub-Api-Version:2022-11-28
GH_TOKEN: ***
TF_CLI_ARGS: -no-color
TF_IN_AUTOMATION: true
TF_INPUT: false
TF_WORKSPACE: sng-dev
exitcode: 0
PRESERVE_PLAN: false
gh: Resource not accessible by integration (HTTP 403)
Error: Process completed with exit code 1.