terraform-provider-cloudflare icon indicating copy to clipboard operation
terraform-provider-cloudflare copied to clipboard

support cloudflare_certificate_pack data

Open mbelang opened this issue 4 years ago • 21 comments

Current Terraform and Cloudflare provider version

terraform -v Terraform v0.14.10

  • provider registry.terraform.io/cloudflare/cloudflare v2.20.0
  • provider registry.terraform.io/hashicorp/aws v3.38.0

Description

Having cloudflare_certificate_pack data would be useful to retrieve information like the certificate txt value in order to use to in other resources to automatically create the validation txt record.

Use cases

We want to get the certificate pack txt value in order to automatically create the associated txt record to allow validation of the cert pack.

Potential Terraform configuration

resource "cloudflare_zone" "example" {
    zone = "example.com"
}

resource "cloudflare_certificate_pack" "example" {
  zone_id               = cloudflare_zone.example.id
  type                  = "advanced"
  hosts                 = ["example.com", "*.example.com"]
  validation_method     = "txt"
  certificate_authority = "digicert"
}

data "cloudflare_certificate_pack" "example" {
  zone_id = cloudflare_zone.example.id
  cert_pack_id = cloudflare_certificate_pack.example.id
}
...

References

The certificate txt value is available in the api v4 Get Certificate Pack response:

{
  "result": {
    "id": "<redacted>",
    "type": "advanced",
    "hosts": [
      "*.example.com",
      "example.com"
    ],
    "primary_certificate": 0,
    "status": "pending_validation",
    "certificates": [],
    "created_on": "2021-05-05T13:00:15.714111Z",
    "validity_days": 90,
    "validation_method": "txt",
    "validation_records": [
      {
        "txt_name": "example.com",
        "txt_value": "ca3-<redacted>"
      }
    ],
    "certificate_authority": "digicert"
  },
  "success": true,
  "errors": [],
  "messages": []
}

mbelang avatar May 05 '21 13:05 mbelang

is there a reason why you've opted for a new data source over exposing these on the resource itself? i agree with the concept but not sure why we'd need this separate if you're already managing the certificate in Terraform.

jacobbednarz avatar May 06 '21 04:05 jacobbednarz

The cert_pack_resource doesn't provide the the output that I want because the API itself doesn't return that value on creation of the certificate. Only get cert pack returns the value that I want.

mbelang avatar May 06 '21 12:05 mbelang

When a resource is created, internally it also calls the Read method which is the GetCertificatePack endpoint in order to sync the state. As that is the case, I'm 👍 to adding this to the existing resource instead of a new data source.

Do you want to submit a PR for this one?

jacobbednarz avatar May 06 '21 20:05 jacobbednarz

I think the ability to read validation_records from the existing cloudflare_certificate_pack would be enough to automate the process of adding txt records automatically.

komljen avatar May 13 '21 10:05 komljen

Any updates on this ? Kinda defeats the purpose of the cloudflare_certificate_pack resource if we cannot automatically validate the certificate via the TXT record

crisp2u avatar Jan 13 '22 12:01 crisp2u

This got fixed recently but we need to update the docs before we call this closed - https://github.com/cloudflare/terraform-provider-cloudflare/blob/332bf001d606db2dd49a4118ca10efd3672744a7/cloudflare/schema_cloudflare_certificate_pack.go#L49-L54

garrettgalow avatar Mar 16 '22 16:03 garrettgalow

I am slightly confused by how this is supposed to work, so the validation records will be attributes of the cloudflare_certificate_pack resource?

If the certificate pack will only be created if domain control validation has been performed, but you can only get the values to put in the validation record after you create the certificate pack since its an attribute - unless I am missing something, is this not a bit of a chicken and egg situation?

Or perhaps wait_for_active_status needs to be false so it creates it and includes the validation records in the attributes which you can then use to create the DNS TXT record? But if that's the case doesn't that mean you will have an outage because it will delete before recreating the resource.

We are using version 3.17 of the provider and when we create certificate packs

resource "cloudflare_certificate_pack" "XXXXXXXXX" {
  provider = cloudflare
  certificate_authority  = "digicert"
  cloudflare_branding    = false
  hosts                  = ["example.com", "*.example.com"]
  type                   = "advanced"
  validation_method      = "txt"
  validity_days          = 30
  zone_id                = cloudflare_zone.XXXXXX.id
  wait_for_active_status = true
  lifecycle {
    create_before_destroy = true
  }
}

The state file just has null for validation_records:

"type": "advanced",
"validation_errors": null,
"validation_method": "txt",
"validation_records": null,
"validity_days": 30,
"wait_for_active_status": true,

Or is it the case that for Full Setup the validation is automatic because the Cloudflare is authoritative for the DNS zone? If you're using partial DNS is that scenario not supported with the Terraform provider?

Any clarifications on how this is supposed to work would be much appreciated.

vijaytdh avatar Jul 13 '22 16:07 vijaytdh

+1 here

I'm creating several cloudflare_certificate_pack for different domains using this module:

variable "zone_id" {}
variable "wildcards" {}

# Advanced certificate manager for DigiCert
resource "cloudflare_certificate_pack" "advanced_digicert" {
  zone_id               = var.zone_id
  type                  = "advanced"
  hosts                 = var.wildcards
  validation_method     = "txt"
  validity_days         = 365
  certificate_authority = "digicert"
  cloudflare_branding   = false
  
  # to make sure it is active, uncomment below
  wait_for_active_status = true
  
  lifecycle {
    ignore_changes        = [wait_for_active_status]
    create_before_destroy = true
  }
}
 
locals {
 txt = cloudflare_certificate_pack.advanced_digicert.validation_records == null ? tolist([{ "txt_value"="unknown"}]) : concat(cloudflare_certificate_pack.advanced_digicert.validation_records,[ { "txt_value"="unknown" }])

 depends_on = [ cloudflare_certificate_pack.advanced_digicert ]
}
 
output "validation-txt" {
  description = "txt data to validade certificate"
  value = local.txt[0].txt_value
}

And then i call the module and update the route53 Domain main TXT record using the validation-txt output. All looks perfect, but when i run this the first time, cloudflare_certificate_pack.advanced_digicert.validation_records is null (that is exactly why i have the above code to test null) and the TXT record is created with "unknown"

I have to run a second apply, so the cloudflare_certificate_pack.advanced_digicert.validation_records do have the correct values and finally the TXT changed to the correct value

Finally,looks like after the certificate is active, the validation record gets emptied, so i need the concat to avoid later errors of empty list

Am i missing something, or is this a bug? i would say that the provider should wait for the validation info, so the output is correctly set without requiring running apply twice. The disappearing validation data, is weird, but not so critical. Both right now require the use of locals to workaround most, but not all of this issues

danielmotaleite avatar Oct 05 '22 00:10 danielmotaleite

I might be wrong, but at least in my case when I switched from letsencrypt to digicert, I don't have to add validation records anymore. I still have a cloudflare-verify validation record for cloudflare zone, so I guess that is enough for this certificate to get validated.

Maybe validation_records is null only when using digicert?

komljen avatar Oct 05 '22 11:10 komljen

If you use cloudflare as DNS provider, it should automatically create the validation record, if i recall correctly from the documentation. In my case, i'm using route53, so that do not work

danielmotaleite avatar Oct 06 '22 04:10 danielmotaleite

I'm using partial DNS, and route53 is my primary DNS provider.

komljen avatar Oct 06 '22 09:10 komljen

@komljen are you using wildcards or plain FQDN certificates? FQDN can use http validation, while wildcard certificates do require email or txt validation

danielmotaleite avatar Oct 06 '22 16:10 danielmotaleite

I use wildcard certs. Basically, I can go to the CF web console and create a new DigiCert wildcard cert, which gets validated immediately without needing to add a validation record in route53. The important part is that this only works with DigiCert and not lets-encrypt.

The only record that I have is zone validation. Here is a TF code for that:

resource "aws_route53_record" "cloudflare_verify" {
  name    = "cloudflare-verify.${module.common_local_vars.env._root_domain}"
  ttl     = 300
  type    = "TXT"
  zone_id = data.aws_route53_zone.main.zone_id

  records = [
    cloudflare_zone.main.verification_key
  ]
}

komljen avatar Oct 06 '22 17:10 komljen

ok, it is strange... maybe it is not really being verified! :D i will try and check if the TXT is really required or not them :) thanks for the help

danielmotaleite avatar Oct 06 '22 17:10 danielmotaleite

image

nope, didn't worked for me in a domain that never had a certificate ... but maybe it works in domain that already had a CF certificate and digicert do some validation cache when the request arrives from the same CF account that was validated before

danielmotaleite avatar Oct 07 '22 01:10 danielmotaleite

Any updates on how this is supposed to work? I tried creating a google cert as we're trying to plan for the upcoming Digicert deprecation, and as @danielmotaleite demonstrated, the validation records don't appear in the state until the second apply. (I simply created a certificate_pack resource and piped the validation_records to an output).

Is the issue that the GetCertPack call to sync the state happens before the actual apply to create the cert? Would it make sense to sync the state again in the apply logic before finalizing the state?

evanrappe avatar May 26 '23 20:05 evanrappe

@jacobbednarz any thoughts here on why this resource needs to be applied twice to actually contain the validation records? Applying twice to achieve full automation with Terraform is not very graceful. If you'd like me to open a new issue, lmk, didn't want to duplicate this already-open one.

evanrappe avatar Jun 08 '23 20:06 evanrappe

Yes this would be really handy please !

bluemalkin avatar Jun 16 '23 02:06 bluemalkin

Using Cloudflare provider v4.17.0 it still requires two runs of apply to get the validation_records data, this is very counter-intuitive as a resource that creates something normally outputs useful details on the thing after it's been created.

Contrast with, for example, aws_acm_certificate which outputs the required domain validation records which can immediately be used to create DNS records, and validate the certificate, all in one run of Terraform apply.

sigmaris avatar Nov 01 '23 18:11 sigmaris

Any update please ? Same problem for me using provider v4.21

jedupuis avatar Jan 05 '24 10:01 jedupuis

The DCV delegation URL seems to be static for Cloudflare zones. Storing the DCV record somewhere and using it to build the CNAME target for TXT validation could work as a workaround:

resource "cloudflare_certificate_pack" "auth_certificate" {
  validation_method     = "txt"
  . . . 
}

locals {
  cloudflare_zones = {
    "myzone": "1234567890.dcv.cloudflare.com",
    "myotherzone": "a1234567890.dev.cloudflare.com"
  }
}

resource "your_dns_provider" "cert_validation" {
  . . . 
  name     = "_acme-challenge.${var.host}"
  type     = "CNAME"
  records  = ["${var.host}.${local.cloudflare_zones[var.zone_name]}"]
}

kwcrook avatar Jan 25 '24 01:01 kwcrook

yes, using a TFvar or a local for storing the DVC record do work, but i have around 50 domains, just manually gathering the list to add to a local var is a major pain

i quickly created this script, to use the cloudflare api and get in the output the domain name and the dvc, ready to copy&paste to a tfvar

Export the CLOUDFLARE_EMAIL, CLOUDFLARE_TOKEN and your CLOUDFLARE_ACCOUNT and have fun if you have more than 50 domains, increment the page=1 until you get all the domains

 curl -s --request GET      --header 'Content-Type: application/json'   -H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" -H "X-Auth-Key: ${CLOUDFLARE_TOKEN}" --url   "https://api.cloudflare.com/client/v4/zones?per_page=50&page=1&account.id=${CLOUDFLARE_ACCOUNT}"  | \
  jq '.result[] | (.name,.id) ' | \
  sed 'N; s/\n/ /g; s/"//g'  | \
  awk '{ print "echo \\\""$1"\\\""; print "curl --request GET      --header \"Content-Type: application/json\"   -H \"X-Auth-Email: ${CLOUDFLARE_EMAIL}\"
 -H \"X-Auth-Key: ${CLOUDFLARE_TOKEN}\" --url https://api.cloudflare.com/client/v4/zones/"$2"/dcv_delegation/uuid    -s | jq \".result.uuid\" "}' | \
  bash  | \
  sed 'N; s/\n/ = /g; s/^/  /g; s/"$/.dcv.cloudflare.com"/g'

yes, it is a bash script, so quick and dirty, as it should ;) bash, curl, sed, awk and jq required, but you can replace bash with your preferred shell

and yes, the cloudflare provider should add this to a data source, so we don't need to manage manual tfvar new ticket open for this https://github.com/cloudflare/terraform-provider-cloudflare/issues/3165 , as dcv is not really cloudflare_certificate_pack related, is per zone. This one is for the TXT values

danielmotaleite avatar Mar 06 '24 17:03 danielmotaleite

This issue has been closed as we are now tracking this internally with service teams directly. If you would like an update or to be notified when/if the product ships with this change, please reach out to Cloudflare Support or your account team who can watch the internal feature request for you.

jacobbednarz avatar Mar 25 '24 00:03 jacobbednarz