hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.79k stars 9.14k forks source link

aws_acm_certificate_validation on certificate that is in ValidationStatus=SUCCESS and Status=ISSUED should always succeed #8597

Open Nowaker opened 5 years ago

Nowaker commented 5 years ago

Community Note

Terraform Version

Terraform v0.11.13
+ provider.aws v2.10.0

Affected Resource(s)

Terraform Configuration Files

resource "aws_acm_certificate" "secure_newdream_net" {
  domain_name = "secure.newdream.net"
  subject_alternative_names = []
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_acm_certificate_validation" "secure_newdream_net" {
  certificate_arn = "${aws_acm_certificate.secure_newdream_net.arn}"
  validation_record_fqdns = ["${aws_acm_certificate.secure_newdream_net.domain_name}"]

  timeouts {
    create = "5m"
  }
}

Certificate is already in AWS - it was created earlier and then imported to Terraform.

image

Debug Output

% TF_LOG=debug terraform apply
...
  + aws_acm_certificate_validation.secure_newdream_net
      id:                                 <computed>
      certificate_arn:                    "arn:aws:acm:us-east-1:272624908555:certificate/5b9f10f1-59e2-4d16-a477-c6f4e10200fa"
      validation_record_fqdns.#:          "1"
      validation_record_fqdns.1167109527: "secure.newdream.net"
...
aws_acm_certificate_validation.secure_newdream_net: Creating...
  certificate_arn:                    "" => "arn:aws:acm:us-east-1:272624908555:certificate/5b9f10f1-59e2-4d16-a477-c6f4e10200fa"
  validation_record_fqdns.#:          "" => "1"
  validation_record_fqdns.1167109527: "" => "secure.newdream.net"
2019-05-09T22:30:06.160-0500 [DEBUG] plugin.terraform-provider-aws_v2.10.0_x4: 2019/05/09 22:30:06 [DEBUG] [aws-sdk-go] DEBUG: Request acm/DescribeCertificate Details:
...
2019-05-09T22:30:06.843-0500 [DEBUG] plugin.terraform-provider-aws_v2.10.0_x4: 2019/05/09 22:30:06 [DEBUG] [aws-sdk-go] {"Certificate":{"CertificateArn":"arn:aws:acm:us-east-1:272624908555:certificate/5b9f10f1-59e2-4d16-a477-c6f4e10200fa","CreatedAt":1.545151664E9,"DomainName":"secure.newdream.net","DomainValidationOptions":[{"DomainName":"secure.newdream.net","ResourceRecord":{"Name":"_1ce25b9917074ea63c6d293b9013b5c5.secure.newdream.net.","Type":"CNAME","Value":"_5c08a0d9d8a0c0fe289a91fec8bbbe98.hkvuiqjoua.acm-validations.aws."},"ValidationMethod":"DNS","ValidationStatus":"SUCCESS"}],"ExtendedKeyUsages":[{"Name":"TLS_WEB_SERVER_AUTHENTICATION","OID":"1.3.6.1.5.5.7.3.1"},{"Name":"TLS_WEB_CLIENT_AUTHENTICATION","OID":"1.3.6.1.5.5.7.3.2"}],"InUseBy":["arn:aws:cloudfront::272624908555:distribution/E33SJU5H6240E7"],"IssuedAt":1.545151949E9,"Issuer":"Amazon","KeyAlgorithm":"RSA-2048","KeyUsages":[{"Name":"DIGITAL_SIGNATURE"},{"Name":"KEY_ENCIPHERMENT"}],"NotAfter":1.5793488E9,"NotBefore":1.5450912E9,"Options":{"CertificateTransparencyLoggingPreference":"ENABLED"},"RenewalEligibility":"ELIGIBLE","Serial":"0c:af:a0:4f:09:67:46:b1:15:d6:b9:bd:d2:ba:cb:23","SignatureAlgorithm":"SHA256WITHRSA","Status":"ISSUED","Subject":"CN=secure.newdream.net","SubjectAlternativeNames":["secure.newdream.net"],"Type":"AMAZON_ISSUED"}}
2019/05/09 22:30:06 [ERROR] root: eval: *terraform.EvalApplyPost, err: 1 error(s) occurred:

* aws_acm_certificate_validation.secure_newdream_net: 1 error occurred:
        * missing secure.newdream.net DNS validation record: _1ce25b9917074ea63c6d293b9013b5c5.secure.newdream.net
...

Expected Behavior

Just work. Since Amazon considers the certificate issued and DNS validated, it should be a no-op for Terraform. Just create this virtual entity in the state and move on with life.

Actual Behavior

Error: Error applying plan:

1 error(s) occurred:

* aws_acm_certificate_validation.secure_newdream_net: 1 error(s) occurred:

* aws_acm_certificate_validation.secure_newdream_net: 1 error occurred:
        * missing secure.newdream.net DNS validation record: (SNIPPED).secure.newdream.net

Steps to Reproduce

  1. Create an ACM certificate manually using DNS validation in AWS.
  2. terraform import the ACM certificate.
  3. Create aws_acm_certificate_validation that references the certificate.
rafaelsales commented 5 years ago

News on this?

gonzgonz commented 4 years ago

I reckon there should be at least a way to also "import" the aws_acm_certificate_validation resource as otherwise this kind of creates a loophole if you're trying to import existing stuff into a working module

lqueryvg commented 4 years ago

My use case...

I'm using SSL certs with CloudFront. Both for web sites and for API gateway custom domains.

I'm creating Terraform code for existing environments which have historically been created by hand.

Not being able to import a aws_acm_certificate_validation resource is a real pain. Without it I am forced to create a new certificate, which in turn forces the CloudFront distribution to be updated. Forcing a change on a production system (that doesn't actually need to be changed) is never a good idea !

My personal preference would be to make aws_acm_certificate_validation importable. It would break the "pattern" if it was to suddenly appear in my state when I hadn't manually imported it. But maybe I can be convinced otherwise.

At any rate, whatever the solution, there should at least be some import related documentation for this type of resource.

lqueryvg commented 4 years ago

Actually - has the behaviour requested by this issue already been implemented in Terraform .12 ? I've just noticed a aws_acm_certificate_validation resource appear in my state after importing the associated route 53 validation validation record. If so, my problems are solved in Terraform .12.

michael-robbins commented 3 years ago

I just got this error, but let it apply and it was a 'no-op' and just succeeded in TF1.0

I imported the certificate and it's DNS records, then let TF do the apply for the validation resource.

AlexBurkey commented 3 years ago

@michael-robbins I'm getting this as well. To confirm, running an apply against a plan creating a new aws_acm_certificate_validation resource is a no-op on the actual infrastructure?

I've imported the R53 validation record and the ACM cert into my state but it still wants to create the validation resource in the plan.

michael-robbins commented 3 years ago

Yeah it was just a no-op for me IIRC, I don't remember it re-deploying anything (please don't blame me if it destroys your production DNS and you lose the cert for your primary domain)

lordgordon commented 1 year ago

Same issue here, there is a workaround?

lordgordon commented 1 year ago

This is happening to me after having added new hosts to an existing certificates. The new hosts are in a different zone. On AWS everything looks fine (cert is valid and working, DNS validation records are in place, ...). Yet Terraform is unable to create aws_acm_certificate_validation and there is no way to manually import it into the state.

lordgordon commented 1 year ago

In my case was a zone_id issue. Here how I solved it:

variable "alternative_names" {
  type = list(object({
    domain_name = string
    zone_id     = string
  }))
  default = []
}

resource "aws_acm_certificate" "this" {
  domain_name       = var.domain_name
  validation_method = "DNS"

  subject_alternative_names = [for record in var.alternative_names : record.domain_name]

  tags = {
    Manager    = "terraform"
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_acm_certificate_validation" "this" {
  certificate_arn         = aws_acm_certificate.this.arn
  validation_record_fqdns = [for record in aws_route53_record.validation : record.fqdn]
}

data "aws_route53_zone" "root_domain_name" {
  name         = var.domain_name
  private_zone = false
}

locals {
  alternative_names_domain_list = [for item in var.alternative_names : item.domain_name]
  alternative_names_zone_list   = [for item in var.alternative_names : item.zone_id]
}

resource "aws_route53_record" "validation" {
  for_each = {
    for dvo in aws_acm_certificate.this.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
      zone = contains(
        local.alternative_names_domain_list, dvo.domain_name) ? local.alternative_names_zone_list[
      index(local.alternative_names_domain_list, dvo.domain_name)] : ""
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = each.value.zone == "" ? data.aws_route53_zone.root_domain_name.zone_id : each.value.zone
}