Managing ACM Certificate Validation with Terraform

AWS Certificate Manager provides public certificates for integrated AWS services at no additional cost, and those certificates auto-renew. Every public-facing service needs HTTPS, and ACM removes manual renewal overhead. The catch is that they need to be validated, and that validation process has a few nuances that trip people up when working with Terraform.

Validation can be performed via DNS or email. DNS validation is strongly preferred because it can be fully automated. When Terraform manages an aws_acm_certificate resource, the certificate is created in a pending validation state. Terraform alone does not wait for AWS to confirm domain ownership. That gap is filled by the aws_acm_certificate_validation resource, a Terraform construct that blocks until ACM confirms the certificate is valid. The resource does not create anything in AWS. It is a dependency and waiter that ensures subsequent resources reference a certificate that is actually issued.

Requesting a Certificate

The basic certificate request is simple.

```hcl
resource "awsacmcertificate" "main" {
domainname = "example.com"
validation
method = "DNS"
tags = {
Environment = "production"
ManagedBy = "terraform"
}

lifecycle {
createbeforedestroy = true
}
}
```

The create_before_destroy lifecycle rule is important. Without it, Terraform would destroy the existing certificate before creating the new one, causing downtime for any services using it. ACM supports two validation methods: DNS and email. DNS validation is strongly preferred because it can be fully automated.

When a wildcard is required, the domain name must reflect the wildcard pattern and a base domain. Due to the fact that both the wildcard certificate for a domain/subdomain and a site certificate requests use the same DNS validation record it is important to request both at the same time if you need both. In this case set the optional create_wildcard parameter to "true". Do not request a certificate that includes a wildcard and select create_wildcard = "true" in the same request.

To serve a site at the top of your domain, you will need to request a certificate with the same name as hosted zone.

Regional constraints affect where certificates can be used. For using this certificate with CloudFront, see requirements for US East region. CloudFront supports US East. If you're deploying ACM to use with CloudFront, a separate provider alias for us-east-1 is often configured alongside the primary region.

DNS Validation with Route 53

To complete DNS validation, you need to create specific DNS records that ACM uses to verify domain ownership. If you manage your DNS in Route 53, Terraform can handle this automatically.

First look up the hosted zone:

hcl data "aws_route53_zone" "main" { name = "example.com" private_zone = false }

Then create the validation DNS records:

```hcl
resource "awsroute53record" "certvalidation" {
for
each = {
for dvo in awsacmcertificate.main.domainvalidationoptions : dvo.domainname => {
name = dvo.resource
recordname
record = dvo.resource
recordvalue
type = dvo.resource
record_type
}
}

allowoverwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone
id = data.awsroute53zone.main.zone_id
}
```

The for_each loop builds a record per domain validation option returned by ACM. The aws_acm_certificate_validation resource then waits for the certificate to be validated:

hcl resource "aws_acm_certificate_validation" "main" { certificate_arn = aws_acm_certificate.main.arn validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] }

The aws_acm_certificate_validation resource doesn't create anything in AWS. It's a Terraform construct that blocks until ACM confirms the certificate is valid.

When the DNS zone is managed outside Route 53 or records are created externally, the validation record FQDNs can be supplied manually. The validation_record_fqdns argument accepts a list of fully qualified domain names for the validation records. This pattern is used when create_route53_records is set to false in module usage.

The awsacmcertificate_validation Resource

The validation resource is the synchronization point between ACM and Terraform.

  • It references the certificate ARN from the aws_acm_certificate resource.
  • It receives the FQDNs of the DNS validation records that were created.
  • It polls ACM until the certificate status becomes ISSUED.

Because the resource is a waiter, Terraform will not proceed to dependent resources until validation succeeds. This prevents referencing a certificate that is still pending.

Key behavior notes:

  • Validation does not represent a real-world entity in AWS, therefore changing or deleting it on its own has no immediate effect.
  • For use in an automated pipeline consider setting the waitforvalidation variable to false. If this is not set this module will cause terraform to wait until validation is complete or error after a 45 minute timeout.
  • The certificate_arn input is the ARN from the certificate resource, not the validation resource.
  • When referencing the certificate in downstream resources, use the ARN from the validation resource to guarantee it is issued.

The reference pattern for a load balancer listener is:

```hcl
resource "awslblistener" "https" {
loadbalancerarn = awslb.main.arn
port = 443
protocol = "HTTPS"
ssl
policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificatearn = awsacmcertificatevalidation.main.certificate_arn

defaultaction {
type = "forward"
target
grouparn = awslbtargetgroup.app.arn
}
}
```

Notice we reference aws_acm_certificate_validation.main.certificate_arn instead of aws_acm_certificate.main.arn. The validation resource's attribute is only known after ACM confirms issuance.

Using the Certificate

Once validated, reference the certificate ARN in your resources. For ALB, API Gateway, CloudFront distributions and other integrated services, the ARN must point to an issued certificate in the correct region.

When using with CloudFront, the certificate must exist in US East N. Virginia. This regional requirement is separate from the ALB or API Gateway certificate location.

Module Approaches

Community modules encapsulate certificate creation and validation.

Module example with Route53 validation:

```hcl
module "acm" {
source = "terraform-aws-modules/acm/aws"
version = "~> 4.0"

domainname = "my-domain.com"
zone
id = "Z2ES7B9AZ6SHAE"
validationmethod = "DNS"
subject
alternativenames = [
"*.my-domain.com",
"app.sub.my-domain.com",
]
wait
for_validation = true
tags = {
Name = "my-domain.com"
}
}
```

Module example with external validation records:

```hcl
module "acm" {
source = "terraform-aws-modules/acm/aws"
version = "~> 4.0"

domainname = "weekly.tf"
zone
id = "b7d259641bf30b89887c943ffc9d2138"
validationmethod = "DNS"
subject
alternativenames = [
"*.weekly.tf",
]
create
route53records = false
validation
recordfqdns = [
"
689571ee9a5f9ec307c512c5d851e25a.weekly.tf",
]
tags = {
Name = "weekly.tf"
}
}
```

The module creates an ACM certificate and validates it using Route53 DNS recommended or e-mail. Parameters control whether DNS records are created internally.

Module inputs summary:

Name Description Type
create_certificate Whether to create ACM certificate bool
createroute53records When validation is set to DNS, define whether to create the DNS records internally via Route53 or externally using any DNS provider bool
createroute53records_only Whether to create only Route53 records bool
validation_method DNS or EMAIL string
waitforvalidation Whether to wait for validation to complete bool
domain_name Primary domain name for certificate string
subjectalternativenames Additional SANs for certificate list

Provider requirements:

Name Version
terraform >= 1.5.7
aws >= 6.28

Resources created by the module:

Name Type
awsacmcertificate.this resource
awsacmcertificate_validation.this resource
awsroute53record.validation resource

Upgrading notes: If you're upgrading to v2.13.0 or above, you might be subject to off-by-one validation record issue. You can solve this without compromising existing validation records by issuing

terraform state rm <your_module_name>.validation[1]

where [1] can be a different index depending on the number of validation records your module creates. You can check this with terraform state list module.<your_module_name>.validation.

Provider and State Setup

To create and validate the ACM certificate with Terraform, we’ll first set up a provider and store the state file in S3.

```hcl
provider "aws" {
region = "eu-west-1"
}

provider "aws" {
alias = "us-east-1"
region = "us-east-1"
}

Terraform {
backend "s3" {
region = "eu-west-1"
bucket = "insertbucketname"
key = "domain-r53-acm.tfstate"
dynamodbtable = "insertdynamodblocktablename_"
encrypt = true
}
}
```

The hosted zone will attract a charge of $0.501 per month. At the time this post was published, the cheapest domain was .click at $31 per year, but current pricing can be checked.

Once registered, AWS will automatically create an associated hosted zone in Route53. This hosted zone is used for DNS validation.

Conclusion

The aws_acm_certificate_validation resource is the critical coordination point in Terraform ACM workflows. It does not provision infrastructure, it enforces correct ordering and waits for AWS to issue a certificate after DNS ownership proof is in place.

Best practices are to always use DNS validation for automation, enable create_before_destroy on the certificate resource to avoid downtime, reference the certificate ARN from the validation resource not the certificate resource, and plan for regional constraints when using CloudFront. Modules can simplify the pattern but understanding the underlying aws_acm_certificate, aws_route53_record, and aws_acm_certificate_validation resources remains essential for troubleshooting validation failures, wildcard requests, and multi-domain setups.

Sources

  1. OneUptime Blog
  2. Terraform AWS Modules ACM
  3. Terraform Foundation ACM Certificate
  4. Headforthe.cloud

Related Posts