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"
validationmethod = "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" {
foreach = {
for dvo in awsacmcertificate.main.domainvalidationoptions : dvo.domainname => {
name = dvo.resourcerecordname
record = dvo.resourcerecordvalue
type = dvo.resourcerecord_type
}
}
allowoverwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zoneid = 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_certificateresource. - 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_arninput 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"
sslpolicy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificatearn = awsacmcertificatevalidation.main.certificate_arn
defaultaction {
type = "forward"
targetgrouparn = 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"
zoneid = "Z2ES7B9AZ6SHAE"
validationmethod = "DNS"
subjectalternativenames = [
"*.my-domain.com",
"app.sub.my-domain.com",
]
waitfor_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"
zoneid = "b7d259641bf30b89887c943ffc9d2138"
validationmethod = "DNS"
subjectalternativenames = [
"*.weekly.tf",
]
createroute53records = false
validationrecordfqdns = [
"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.