terraform-provider-cloudflare: [3.24.0] Regression - Cannot specify both api_user_service_key and api_token

Confirmation

  • My issue isn’t already found on the issue tracker.
  • I have replicated my issue using the latest version of the provider and it is still present.

Terraform and Cloudflare provider version

Terraform v1.2.9 Cloudflare provider v3.24.0

Affected resource(s)

  • cloudflare_origin_ca_certificate

Terraform configuration files

resource "cloudflare_origin_ca_certificate" "test" {}

Link to debug output

N/A

Panic output

No response

Expected output

Resource cloudflare_origin_ca_certificate requires api_user_service_key but not other resources.

Actual output

│ Error: Invalid combination of arguments │ │ with provider[“registry.terraform.io/cloudflare/cloudflare”], │ on <empty> line 0: │ (source code not available) │ │ “api_user_service_key”: only one of │ api_key,api_token,api_user_service_key can be specified, but │ api_token,api_user_service_key were specified.

Steps to reproduce

  1. Try to use cloudflare_origin_ca_certificate and cloudflare_record in the same Terraform configuration.

Additional factoids

No response

References

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 5
  • Comments: 24 (1 by maintainers)

Most upvoted comments

Maybe I am missing some workaround, but this change has broken the use of cloudflare_origin_ca_certificate resource. If the token + api_user_service_key are specified together, the provider throws an error that only one of auth methods can be specified. If only the token is used, I get error finding OriginCACertificate "<my certificate ID>": Authentication error (10000). And if only api_user_service_key is specified the provider errors with error creating new Cloudflare client: invalid credentials: key & email must not be empty

Maybe I am missing some workaround, but this change has broken the use of cloudflare_origin_ca_certificate resource. If the token + api_user_service_key are specified together, the provider throws an error that only one of auth methods can be specified. If only the token is used, I get error finding OriginCACertificate "<my certificate ID>": Authentication error (10000). And if only api_user_service_key is specified the provider errors with error creating new Cloudflare client: invalid credentials: key & email must not be empty

IMHO, this kind of breaking change should not have been included in 3.x but anyway, this is what you need to do now::

provider "cloudflare" {
  alias  = "api_user_service_key"
  api_user_service_key = var.cloudflare_api_user_service_key
}

resource "cloudflare_origin_ca_certificate" "aks_ingress" {
  provider = cloudflare.api_user_service_key
}

Ref: https://www.terraform.io/language/providers/configuration#alias-multiple-provider-configurations

this is completely backwards

instead of pushing the complexity of creating separate provider (for just the one resource) on the user, the provider should be smart enough to choose which credentials to use for which resource

I’m sticking to 3.22 until this is properly fixed

Got it working. Won’t run with env variables, I guess.

For all the people wasting time to get this working, here is a ready example:

terraform {
  required_version = ">= 1.0.2"
  required_providers {
    cloudflare = {
      source = "cloudflare/cloudflare"
      version = "~> 3.0"
      configuration_aliases = [ cloudflare.abomination ]
    }
  }
}
provider "cloudflare" {
  api_key = "your_api_key_here"
  email = "your@email.here"
}

provider "cloudflare" {
  alias  = "abomination"
  api_user_service_key = "origin ca key here. the one from the very bottom of api tokens page. called service key nowhere on the internet"
}

(...)

resource "cloudflare_origin_ca_certificate" "origin_cert" {
  provider = cloudflare.abomination
  csr                = tls_cert_request.testreq.cert_request_pem
  hostnames          = [ "*.mydomain.com", "mydomain.com" ]
  request_type       = "origin-rsa"
  requested_validity = 365
}

Using two separate providers is not a great solution… since you’re not able to use environment variables as already mentioned. So you either have to commit your keys, which is not an option. Or have a separate .tf file for your Cloudflare provider, and add that to your .gitignore. Not great in regards to DX…

We’ll stay on 3.22 for the time being

@jacobbednarz I don’t see any workaround, you ether end up with a Authentication error (10000) or a key & email must not be empty

Yeah I ended up having to downgrade to 3.22 I believe and reverting to key and email.

Here’s my take on getting all resources available today (as of v3.26.0) working with vanilla terraform (I might add a few terragrunt examples later on):

# main.tf
terraform {
  required_version = "~> 1.0"
  required_providers {
    cloudflare = {
      source                = "cloudflare/cloudflare"
      version               = "~> 3.26"
      configuration_aliases = [cloudflare.user_service_key, cloudflare.email_api_key, cloudflare.fixed_account_id]
    }
  }
}

# providers.tf
provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

provider "cloudflare" {
  alias                = "user_service_key"
  api_user_service_key = var.cloudflare_api_user_service_key
}

provider "cloudflare" {
  alias                = "email_api_key"
  api_key              = var.cloudflare_api_key
  email                = var.cloudflare_email
}

provider "cloudflare" {
  alias                = "fixed_account_id"
  account_id           = var.cloudflare_account_id
  api_token            = var.cloudflare_api_token
}

# api_tokens.tf
data "cloudflare_api_token_permission_groups" "all" {
  provider = cloudflare.email_api_key
}

# origin_ca_certificates.tf
resource "cloudflare_origin_ca_certificate" "example" {
  provider = cloudflare.user_service_key
  …
}

# worker_scripts.tf
resource "cloudflare_worker_script" "example" {
  provider = cloudflare.fixed_account_id
  …
}

# workers_kv_namespaces.tf
resource "cloudflare_workers_kv_namespace" "example" {
  provider = cloudflare.fixed_account_id
  …
}

Then create a terraform.tfvars with:

cloudflare_account_id = ""
cloudflare_api_key = ""
cloudflare_api_token = ""
cloudflare_api_user_service_key = ""
cloudflare_email = ""

Everything else will use the default cloudflare provider configured with an API Token. Some of these resources will be fixed once v4 is released.

@mattmichal thanks for sharing this - I also did manage to configure this, but I passed the service token via module variable - I’ll try removing this part if per your suggestion 😃

I actually migrated away from env variables thanks to this issue - tfvars file work better for me since I have a lot of different CF/AWS accounts to manage.

If you’re using Terragrunt, you can automate the pain points with something like in your terragrunt.hcl parent config (using the idea shared by @pielgrzym):

generate "terraform_cloudflare_provider_fix" {
  path      = "terraform_cloudflare_provider_fix.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
## TODO: See how to fix this once upstream finds a solution.
## This is a workaround for using certain resources like cloudflare_origin_ca_certificate without issues.
## See more info: https://github.com/cloudflare/terraform-provider-cloudflare/issues/1919

variable "CF_API_TOKEN" {
  type      = string
  sensitive = true
}

variable "CF_API_USER_SERVICE_KEY" {
  type      = string
  sensitive = true
}

provider "cloudflare" {
  api_token = var.CF_API_TOKEN
}

provider "cloudflare" {
  alias                = "cf-origin-cert-workaround"
  api_user_service_key = var.CF_API_USER_SERVICE_KEY
}
EOF
}

It’s not the cleanest solution but, at least, works.

The workaround by @uncycler does not work for me. Error is "api_key": only one of 'api_key,api_token,api_user_service_key' can be specified, but 'api_key,api_user_service_key' were specified.. Using v3.25.0. I can’t wrap my head around why I can’t just pass both keys and have TF working. Can you please provide a working example how to use this monstrosity?