Terraform: Masking OAuth client_secret from state file

Working with the Genesys Cloud Terraform provider to manage OAuth clients. Running into a security issue with how the genesys_cloud_oauth_client resource handles the client_secret attribute. The provider writes the secret in plain text to the .tfstate file. This is a problem because the state file is stored in an S3 bucket that’s accessible to the broader engineering team. We’ve tried using sensitive = true on the variable, but that only masks the output in the CLI. The actual value still ends up in the JSON state file.

Here’s the resource definition:

resource "genesys_cloud_oauth_client" "api_client" {
 name = "my-api-client"
 client_type = "confidential"
 redirect_uris = ["https://myapp.com/callback"]
 client_secret = var.oauth_client_secret
}

The client_secret is passed via a variable marked as sensitive. The plan shows [sensitive] for the value, which is good. But after terraform apply, checking the state file reveals the actual string.

"client_secret": "AbC123..."

I know Terraform doesn’t encrypt state files by default. We’re using AWS S3 with server-side encryption enabled. That helps at rest, but anyone with read access to the bucket can decrypt it. I want the secret to be managed outside of Terraform entirely. Ideally, we’d generate the secret in Genesys Cloud via the API and just reference the client ID. But the provider requires the secret during creation.

Is there a way to force the provider to not store the secret in state? Or should we be using a data source to read the client after creation and store the secret in AWS Secrets Manager manually? I don’t see an option in the provider docs to exclude attributes from state. This feels like a gap in the provider’s security model. We’re trying to avoid hardcoding secrets in any file that gets committed or stored remotely. Any workarounds for this?

The root cause is how Terraform handles the sensitive flag. It only masks the value in the CLI output, not in the state file itself. The state file is just JSON, and if the provider sends the secret to the API, it gets stored in the state as plain text by default for drift detection.

You can’t fully hide it from the state file without using remote state encryption or a different provider configuration, but you can stop Terraform from sending the secret on every plan/apply if it hasn’t changed.

Try adding lifecycle { ignore_changes = [client_secret] } to your resource. This tells Terraform to ignore any changes to that attribute after the initial creation. You’ll need to set the secret once, then remove it from your TF code or use a variable that’s not tracked.

resource "genesyscloud_oauth_client" "my_client" {
 name = "My OAuth Client"
 description = "Managed by Terraform"
 
 # Set this once, then remove or comment out
 # client_secret = var.oauth_secret 

 lifecycle {
 ignore_changes = [client_secret]
 }
}

Be careful. If you lose the state file or need to recreate the resource, you won’t have the secret in your code anymore. You’ll have to generate a new one via the API or UI and update the state manually if needed.

Also, make sure your S3 bucket has server-side encryption enabled. That’s the only real protection for the state file itself.