Terraform plan in GitHub Actions PR check fails with 'no changes' but apply works locally

Setting up a CI/CD pipeline for our CXone org using the nice-dcv-terraform provider. Goal is simple: run terraform plan on every pull request to catch drift, then terraform apply on merge to main.

The plan step in GitHub Actions keeps passing with “No changes. Infrastructure is up-to-date” even when I modify the script version in the PR. Local runs work fine.

Here’s the workflow:

name: Terraform Plan
on:
 pull_request:
 branches: [ main ]
 paths:
 - '**.tf'

jobs:
 plan:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v3
 - name: Setup Terraform
 uses: hashicorp/setup-terraform@v2
 - name: Init
 run: terraform init -backend-config=backends/prod.tfbackend
 - name: Plan
 run: terraform plan -out=tfplan

The backend config points to our S3 bucket. State is remote.

I’ve checked the state file in S3. It’s updated. The terraform init step succeeds. No errors in the logs. Just that empty plan output.

Tried adding TF_IN_AUTOMATION=true. Didn’t help.

Is the provider caching state differently in the runner? Or is there a specific flag needed for the nice-dcv provider to force a refresh against the CXone API during the plan phase? The API calls are authenticated via OIDC in the action, same token used locally. Token scope is correct.

Feeling like I’m missing a basic config flag. The apply works on merge because it forces the push, but we need the safety net on PR. Any ideas why the diff isn’t showing up?

Sounds like your GHA runner isn’t picking up the latest state because the backend config or credentials are cached incorrectly. Try explicitly setting the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in the terraform init step to force a fresh state pull.

The .NET SDK doesn’t help here since it’s a Terraform issue, but the caching problem is real. I’ve seen this with Azure DevOps pipelines too. The runner might be reusing the .terraform directory from a previous job if the cache isn’t busted properly.

Make sure you’re clearing the state cache or using a clean workspace in your GHA step. Also, check if the nice-dcv-terraform provider is pulling the correct org URL. Sometimes the default URL in the provider block gets cached.

Try adding this to your workflow before terraform plan:

- name: Clean Terraform cache
 run: |
 rm -rf .terraform
 terraform init -input=false

And verify your backend config isn’t pointing to a local file that’s being cached. If you’re using S3 or similar, ensure the state lock is released.