Terraform gen_cloud_oauth_client secret in state file

Using the Genesys Cloud Terraform provider to provision OAuth clients. The client_secret is ending up in the tfstate file as plain text.

resource "gen_cloud_oauth_client" "main" {
 name = "wfm-api-client"
}

Is there a way to mark this as sensitive so it doesn’t show in the state? Or do we have to handle secrets outside of Terraform?

The provider doesn’t mask secrets in the state by default. You have to explicitly mark the attribute as sensitive in your code. It won’t hide it from the raw state file on disk, but it stops Terraform from printing it in plan/apply output and keeps it out of log files.

Add sensitive = true to the resource block.

resource "gen_cloud_oauth_client" "main" {
 name = "wfm-api-client"
 sensitive = true
}

If you’re worried about the state file itself sitting on disk unencrypted, that’s a different problem. Use remote state with encryption at rest. S3 with KMS or Azure Storage with encryption. Don’t store tfstate in git. Ever.

Thanks for the tip on sensitive = true. I added that to the resource block and it definitely stops the secret from leaking into the plan output, which is good. But I’m still seeing the actual value in the raw .tfstate JSON file if I just cat it out. That’s still a bit of a headache for our security team since they want zero plaintext secrets in any file, even state.

I ended up going a different route. Instead of letting Terraform manage the secret rotation or storage, I’m generating the OAuth client via the API directly using a short-lived script, then just importing the ID into Terraform for management. It’s a bit more manual but feels safer.

Here’s the curl command I’m using to create it. You need the admin:oauth_client scope on the token.

curl -X POST "https://api.mypurecloud.com/api/v2/oauth/clients" \
 -H "Authorization: Bearer $ACCESS_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "name": "wfm-api-client-manual",
 "description": "Created via API for security compliance",
 "allowedGrantTypes": ["client_credentials"]
 }'

The response gives you the clientId and clientSecret. I’m dumping that into HashiCorp Vault immediately and only referencing the Vault path in my Terraform variable files. It adds a step, but it keeps the state file clean. The vider is fine with an import block if you just need to track the resource lifecycle without touching the secret again.

import {
 to = gen_cloud_oauth_client.main
 id = "your-client-id-from-api"
}

resource "gen_cloud_oauth_client" "main" {
 name = "wfm-api-client-manual"
 # Don't define secret here, it's already set
}

Not ideal, but it works for now. The vider docs are pretty sparse on advanced security patterns.