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.85k stars 9.2k forks source link

[Bug]: SSL Certificate delation takes too much time #39839

Open EugenKon opened 1 month ago

EugenKon commented 1 month ago

Terraform Core Version

v1.9.7

AWS Provider Version

v5.67.0

Affected Resource(s)

Expected Behavior

Because aws_acm_certificate is used by aws_lb_listener the aws_lb_listener should be updated first. Probably tasks could be ran in parallel.

Actual Behavior

aws_acm_certificate waits infinitly until the certificate will be released.

module.private-cloud.aws_acm_certificate.ssl: Still destroying... [id=arn:aws:acm:us-west-2:315400321086:cert...e/29f561fb-3815-4b29-8a5f-64362fc85467, 1m30s elapsed]
module.private-cloud.aws_acm_certificate.ssl: Still destroying... [id=arn:aws:acm:us-west-2:315400321086:cert...e/29f561fb-3815-4b29-8a5f-64362fc85467, 1m40s elapsed]
module.private-cloud.aws_acm_certificate.ssl: Still destroying... [id=arn:aws:acm:us-west-2:315400321086:cert...e/29f561fb-3815-4b29-8a5f-64362fc85467, 1m50s elapsed]

Relevant Error/Panic Output Snippet

Original plan:

  # module.private-cloud.aws_acm_certificate.ssl will be destroyed
  # (because aws_acm_certificate.ssl is not in configuration)
  - resource "aws_acm_certificate" "ssl" {
      - arn                       = "arn:aws:acm:us-west-2:315400321086:certificate/29f561fb-3815-4b29-8a5f-64362fc85467" -> null
...
  # module.private-cloud.aws_lb_listener.https will be updated in-place
  ~ resource "aws_lb_listener" "https" {
      ~ certificate_arn   = "arn:aws:acm:us-west-2:315400321086:certificate/29f561fb-3815-4b29-8a5f-64362fc85467" -> "arn:aws:acm:us-west-2:315400321086:certificate/9dfc43e1-5f31-4732-bfaa-b5ee407fa8ae"
        id                = "arn:aws:elasticloadbalancing:us-west-2:315400321086:listener/app/nomad-public-www/07644a5a75f45170/fab30819bbf2558f"
        tags              = {}
        # (6 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

To workaround the problem I did:

terraform plan --target module.private-cloud.aws_lb_listener.https
terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # module.private-cloud.aws_lb_listener.https will be updated in-place
  ~ resource "aws_lb_listener" "https" {
      ~ certificate_arn   = "arn:aws:acm:us-west-2:315400321086:certificate/29f561fb-3815-4b29-8a5f-64362fc85467" -> "arn:aws:acm:us-west-2:315400321086:certificate/b3d88460-6998-4e98-b3d7-5971a6569523"
        id                = "arn:aws:elasticloadbalancing:us-west-2:315400321086:listener/app/nomad-public-www/07644a5a75f45170/fab30819bbf2558f"
        tags              = {}
        # (6 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
...
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
terraform plan
terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # module.private-cloud.acme_certificate.ssl will be destroyed
  # (because acme_certificate.ssl is not in configuration)
  - resource "acme_certificate" "ssl" {
...
Plan: 0 to add, 0 to change, 1 to destroy.
...
Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

This took just seconds.

Terraform Configuration Files

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.public.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-Ext-2018-06"
  certificate_arn   = "arn:aws:acm:us-west-2:315400321086:certificate/b3d88460-6998-4e98-b3d7-5971a6569523"
  ....
}
...
resource "acme_certificate" "ssl" {
  account_key_pem           = acme_registration.certbot.account_key_pem
  key_type                  = "P384"
  common_name               = var.domain_name
  subject_alternative_names = local.san

  dns_challenge {
    provider = "route53"
  }

  depends_on = [acme_registration.certbot]
}

Steps to Reproduce

  1. Create certificate
  2. Create LB
  3. Link LB(2) to use certificate(1)
  4. Create second certificate
  5. Update TF config to use certificate(4)
  6. TF plan/apply

Debug Output

No response

Panic Output

No response

Important Factoids

No response

References

No response

Would you like to implement a fix?

None

github-actions[bot] commented 1 month ago

Community Note

Voting for Prioritization

Volunteering to Work on This Issue

justinretzolk commented 3 weeks ago

Hey @EugenKon 👋 Thank you for taking the time to raise this! I noticed that in the sample configuration that you provided, you've mentioned the aws_lb_listener and a acme_certificate resource, but the aws_acm_certificate resource isn't included. It would be really helpful if you could provide how that resource fits into the picture too, so we can get a better idea of dependencies, etc.

EugenKon commented 3 weeks ago

If you are interested, here is the full part related to ssl:

terraform {
  required_providers {
    # 3rd party provider should be defined inside module
    acme = {
      source  = "vancluever/acme"
      version = "~> 2.26"
    }
  }
}

provider "acme" {
  server_url = "https://acme-staging-v02.api.letsencrypt.org/directory"
  # server_url = "https://acme-v02.api.letsencrypt.org/directory"
}

locals {
  san = coalescelist(var.san_domains, [var.domain_name, "*.${var.domain_name}"])
}

resource "tls_private_key" "acme" {
  algorithm   = "ECDSA"
  ecdsa_curve = "P384"
}

resource "acme_registration" "certbot" {
  account_key_pem = tls_private_key.acme.private_key_pem
  email_address   = "saas-ops-infra+${var.project_name}xxxxxx"
}

resource "acme_certificate" "ssl" {
  account_key_pem           = acme_registration.certbot.account_key_pem
  key_type                  = "P384"
  common_name               = var.domain_name
  subject_alternative_names = local.san

  dns_challenge {
    provider = "route53"
  }

  depends_on = [acme_registration.certbot]
}

resource "aws_acm_certificate" "ssl" {
  certificate_body  = acme_certificate.ssl.certificate_pem
  private_key       = acme_certificate.ssl.private_key_pem
  certificate_chain = acme_certificate.ssl.issuer_pem
  depends_on        = [acme_certificate.ssl]
}

resource "local_file" "ssl_cert" {
  content = acme_certificate.ssl.certificate_pem
  # Root dir is at derived-src/aws
  filename        = "${path.root}/../../cert.pem"
  file_permission = "0400"
}

resource "local_file" "ssl_chain" {
  content = acme_certificate.ssl.issuer_pem
  # Root dir is at derived-src/aws
  filename        = "${path.root}/../../chain.pem"
  file_permission = "0400"
}

resource "local_file" "ssl_fullchain" {
  content = format("%s%s", acme_certificate.ssl.certificate_pem, acme_certificate.ssl.issuer_pem)
  # Root dir is at derived-src/aws
  filename        = "${path.root}/../../fullchain.pem"
  file_permission = "0400"
}

resource "local_file" "ssl_privkey" {
  content = acme_certificate.ssl.private_key_pem
  # Root dir is at derived-src/aws
  filename        = "${path.root}/../../privkey.pem"
  file_permission = "0400"
}