Terraform plan in GitHub Actions PR check fails with 409 Conflict on existing resources

I’m trying to set up a CI/CD pipeline using GitHub Actions to manage our Genesys Cloud resources via the genesyscloud Terraform provider. The goal is straightforward: run terraform plan on every pull request to catch drift, and only run terraform apply when the PR merges to main. I’ve got the OIDC workflow configured for authentication, so no static tokens are floating around. That part works fine.

The issue pops up during the plan phase. When I push a change to a feature branch, the action triggers and runs terraform init and terraform plan. It immediately throws a 409 Conflict error on resources that already exist in the org. The error message says something like “Resource with id ‘abc-123’ already exists.” I know the resource exists. That’s the point. The plan should detect the difference between the state file and the live environment, not fail outright because the resource is there.

Here’s the relevant snippet from my ci.yml:

- name: Terraform Plan
 run: |
 terraform plan -out=tfplan

I’m using a remote backend with S3 for state storage. The state file is definitely present because terraform init pulls it down. I’ve checked the state file manually, and it contains the correct IDs for the existing routing queues. I suspect the provider is trying to create the resource instead of reading it during the plan step, or maybe the import block isn’t being respected in the plan phase.

I’ve tried adding import blocks to the code, but that feels like a hack for a CI pipeline. Should I be using terraform import in a separate step before the plan? Or is there a flag I’m missing in the plan command to handle existing resources gracefully? The docs mention terraform plan -refresh=false but that doesn’t seem to help with the ID conflict.

I’m stuck on why the plan phase is treating existing resources as if they need to be created, causing the API call to fail with a 409. Any ideas on how to structure the workflow so the plan step just compares state without trying to write to the API?

The 409 usually means Terraform thinks it needs to create something that already exists, but the API rejects it because of a duplicate constraint (like a unique name or external ID). It’s a classic state drift issue.

Check your .tfstate file. If you imported resources manually or deleted them from the UI without updating state, Terraform gets confused. Run terraform state list and compare it against what’s actually in the Genesys Cloud UI. If you see resources in state that don’t exist in the cloud, remove them with terraform state rm <address>. If they exist in the cloud but not in state, import them.

Also, make sure you’re not using static IDs in your code. Use data sources to look up existing resources by name or attribute. This prevents hardcoding IDs that might change or conflict.

Here’s a quick example for a user:

data "genesyscloud_user" "existing" {
 name = "John Doe"
}

resource "genesyscloud_user" "this" {
 name = data.genesyscloud_user.existing.name
 email = "john@example.com"
 # other config...
}

If you’re still stuck, try running terraform plan -refresh-only to see if there’s actual drift. Sometimes the provider just needs a state sync.

One more thing: check your GitHub Actions runner permissions. If the OIDC token doesn’t have the right scopes (like user:read or routing:write), you might get weird 409s instead of 403s. It’s subtle, but it happens.

Let me know if that clears it up.