DEV Community

terraform Fundamentals: ACM (Certificate Manager)

Managing TLS Certificates with Terraform: A Deep Dive into ACM

The relentless push for secure communication in modern infrastructure demands robust TLS certificate management. Manually handling certificate requests, renewals, and deployments is a recipe for outages and security vulnerabilities. Automating this process with Infrastructure as Code (IaC) is critical. Terraform, coupled with cloud provider Certificate Manager services (ACM in AWS, Azure Key Vault, Google Cloud Certificate Manager), provides a powerful solution. This isn’t just about automating a task; it’s about embedding security into the core of your infrastructure pipeline, enabling self-service for developers, and reducing operational overhead for SREs. This capability fits squarely within a platform engineering stack, providing a standardized, auditable, and repeatable process for securing applications.

What is "ACM (Certificate Manager)" in Terraform context?

“ACM” refers to the cloud provider’s managed certificate service. For this post, we’ll focus primarily on AWS Certificate Manager (ACM), but the principles apply broadly. In Terraform, ACM is accessed via the aws provider. The core resource is aws_acm_certificate, which manages the lifecycle of a certificate.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}
Enter fullscreen mode Exit fullscreen mode

The aws_acm_certificate resource handles certificate requests and validation. A key caveat is that ACM certificates are region-specific. Certificates issued in us-east-1 cannot be directly used with resources in us-west-2. Furthermore, ACM integration with services like Application Load Balancers (ALBs) and CloudFront distributions requires the certificate to be in the same region as the resource. Terraform’s state management becomes crucial here, as you’ll likely need separate ACM certificates per region.

Use Cases and When to Use

ACM with Terraform is essential in several scenarios:

  1. Automated TLS for Public-Facing Websites: Provisioning certificates for websites and APIs automatically as part of infrastructure deployments. This is a core DevOps/SRE responsibility, ensuring consistent security posture.
  2. Multi-Region Deployments: Managing certificates across multiple AWS regions for global applications. Requires careful planning and potentially separate Terraform modules per region.
  3. Self-Service Certificate Provisioning: Empowering developers to request certificates for their applications through a standardized Terraform module, reducing reliance on centralized security teams.
  4. Automated Certificate Renewal: ACM automatically renews certificates, but Terraform ensures the infrastructure utilizing those certificates is updated accordingly.
  5. Internal Application Security: Securing internal applications with private CA certificates managed through ACM Private CA, automating the trust chain.

Key Terraform Resources

  1. aws_acm_certificate: Requests and manages a public or private certificate.
   resource "aws_acm_certificate" "example" {
     domain_name       = "example.com"
     validation_method = "DNS"
   }
Enter fullscreen mode Exit fullscreen mode
  1. aws_acm_certificate_validation: Creates DNS records for certificate validation.
   resource "aws_acm_certificate_validation" "example" {
     certificate_arn         = aws_acm_certificate.example.arn
     validation_record_fqdns = [for record in aws_acm_certificate.example.domain_validation_options[0].resource_record_name : record]
   }
Enter fullscreen mode Exit fullscreen mode
  1. aws_route53_record: (Dependency) Creates DNS records for validation.
   resource "aws_route53_record" "validation" {
     zone_id = "YOUR_ZONE_ID"
     name    = aws_acm_certificate.example.domain_validation_options[0].resource_record_name
     type    = aws_acm_certificate.example.domain_validation_options[0].resource_record_type
     records = [aws_acm_certificate.example.domain_validation_options[0].resource_record_value]
     ttl     = 60
   }
Enter fullscreen mode Exit fullscreen mode
  1. aws_acm_certificate_policy: Defines a certificate policy for ACM Private CA.
   resource "aws_acm_certificate_policy" "example" {
     name   = "ExamplePolicy"
     policy = jsonencode({
       "Version" : "2012-10-17",
       "Statement" : [
         {
           "Effect" : "Allow",
           "Action" : "acm:IssueCertificate",
           "Resource" : "*"
         }
       ]
     })
   }
Enter fullscreen mode Exit fullscreen mode
  1. aws_acm_private_ca: Creates a private CA.
   resource "aws_acm_private_ca" "example" {
     name        = "example-ca"
     certificate_authority_type = "SELF_SIGNED"
   }
Enter fullscreen mode Exit fullscreen mode
  1. data.aws_acm_certificate: Retrieves information about an existing certificate.
   data "aws_acm_certificate" "example" {
     domain_name = "example.com"
     statuses    = ["ISSUED"]
   }
Enter fullscreen mode Exit fullscreen mode
  1. aws_cloudfront_distribution: (Integration) Associates an ACM certificate with a CloudFront distribution.
   resource "aws_cloudfront_distribution" "example" {
     domain_name = "example.cloudfront.net"
     viewer_certificate {
       acm_certificate_arn = aws_acm_certificate.example.arn
       ssl_support_method  = "sni-only"
     }
   }
Enter fullscreen mode Exit fullscreen mode
  1. aws_lb_listener: (Integration) Associates an ACM certificate with an Application Load Balancer listener.
   resource "aws_lb_listener" "example" {
     load_balancer_arn = aws_lb.example.arn
     port              = "443"
     protocol          = "HTTPS"
     ssl_policy        = "ELBSecurityPolicy-2016-08"
     certificate_arn   = aws_acm_certificate.example.arn
   }
Enter fullscreen mode Exit fullscreen mode

Common Patterns & Modules

Using for_each with aws_acm_certificate is common for managing multiple certificates with similar configurations. Dynamic blocks can handle varying validation methods.

resource "aws_acm_certificate" "certificates" {
  for_each = toset(["example1.com", "example2.com"])
  domain_name       = each.value
  validation_method = "DNS"
}
Enter fullscreen mode Exit fullscreen mode

Public modules like those found on the Terraform Registry (search for "acm") can provide a starting point, but often require customization for specific organizational needs. A layered module structure is recommended: a core module handling certificate request/validation, and wrapper modules for specific integrations (ALB, CloudFront). Monorepos are ideal for managing these interconnected modules.

Hands-On Tutorial

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

resource "aws_acm_certificate" "example" {
  domain_name       = "example.com"
  validation_method = "DNS"
}

resource "aws_route53_record" "validation" {
  zone_id = "YOUR_ZONE_ID" # Replace with your Route 53 zone ID

  name    = aws_acm_certificate.example.domain_validation_options[0].resource_record_name
  type    = aws_acm_certificate.example.domain_validation_options[0].resource_record_type
  records = [aws_acm_certificate.example.domain_validation_options[0].resource_record_value]
  ttl     = 60
}

output "certificate_arn" {
  value = aws_acm_certificate.example.arn
}
Enter fullscreen mode Exit fullscreen mode

terraform init, terraform plan, and terraform apply will request the certificate and create the necessary DNS record. The output will display the ARN of the issued certificate. terraform destroy will remove the DNS record and eventually delete the certificate (after the validation period).

This example assumes you have a Route 53 zone configured. In a CI/CD pipeline, this Terraform code would be executed after code changes trigger a pipeline run, automating certificate provisioning.

Enterprise Considerations

Large organizations leverage Terraform Cloud/Enterprise for state locking, remote operations, and collaboration. Sentinel or Open Policy Agent (OPA) are used for policy-as-code, enforcing constraints on certificate usage (e.g., allowed domains, validation methods). IAM roles are meticulously designed to grant least privilege access to ACM resources. Multi-region deployments require careful consideration of certificate costs and regional dependencies. State locking is paramount to prevent concurrent modifications.

Security and Compliance

Enforce least privilege using IAM policies:

resource "aws_iam_policy" "acm_policy" {
  name        = "ACMReadOnlyPolicy"
  description = "Allows read-only access to ACM certificates"
  policy      = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Action" : [
          "acm:GetCertificate",
          "acm:ListCertificates"
        ],
        "Resource" : "*"
      }
    ]
  })
}
Enter fullscreen mode Exit fullscreen mode

Tagging policies ensure certificates are properly labeled for cost allocation and compliance. Drift detection, implemented through Terraform Cloud/Enterprise or custom scripts, identifies unauthorized changes. Audit logs provide a record of certificate lifecycle events.

Integration with Other Services

graph LR
    A[Terraform] --> B(ACM Certificate);
    B --> C{ALB Listener};
    B --> D{CloudFront Distribution};
    B --> E{API Gateway};
    A --> F(Route53);
    F --> B;
Enter fullscreen mode Exit fullscreen mode

ACM integrates seamlessly with:

  1. Application Load Balancers (ALB): Securing traffic to applications.
  2. CloudFront: Distributing content securely via CDN.
  3. API Gateway: Protecting APIs with TLS.
  4. Elastic Beanstalk: Automating TLS configuration for applications.
  5. Route 53: Managing DNS records for certificate validation.

Module Design Best Practices

Abstract ACM functionality into reusable modules with well-defined input variables (domain name, validation method, region) and output variables (certificate ARN). Use locals for default values and complex logic. Document the module thoroughly, including examples and limitations. Consider using a backend like S3 for remote state management.

CI/CD Automation

# .github/workflows/terraform-acm.yml

name: Terraform ACM Deployment

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hashicorp/setup-terraform@v2
      - run: terraform fmt
      - run: terraform validate
      - run: terraform plan -out=tfplan
      - run: terraform apply tfplan
Enter fullscreen mode Exit fullscreen mode

This GitHub Actions workflow automates the Terraform lifecycle. Terraform Cloud/remote runs offer more advanced features like state management and collaboration.

Pitfalls & Troubleshooting

  1. DNS Validation Issues: Incorrect DNS record configuration prevents certificate issuance. Verify record names, types, and values.
  2. Region Mismatch: Attempting to use a certificate in a different region than it was issued.
  3. Rate Limiting: ACM has rate limits. Excessive requests can lead to errors.
  4. Certificate Status Stuck in "Pending Validation": DNS propagation delays or incorrect DNS configuration.
  5. Incorrect IAM Permissions: Insufficient permissions to request or manage certificates.
  6. State Corruption: Corrupted Terraform state can lead to unpredictable behavior.

Pros and Cons

Pros:

  • Automated certificate lifecycle management.
  • Reduced operational overhead.
  • Improved security posture.
  • Integration with other cloud services.
  • Scalability and reliability.

Cons:

  • Region-specific certificates.
  • Dependency on DNS configuration.
  • Potential rate limits.
  • Complexity of IAM permissions.
  • Cost associated with certificate issuance and renewal.

Conclusion

Terraform and ACM are a powerful combination for automating TLS certificate management. By embracing this approach, infrastructure engineers can significantly reduce risk, improve security, and streamline operations. Start by building a simple module for a single certificate, then expand to handle more complex scenarios. Evaluate existing modules on the Terraform Registry, and integrate this functionality into your CI/CD pipeline. The investment in automation will pay dividends in the long run.

Top comments (2)

Collapse
 
nevodavid profile image
Nevo David

Growth like this is always nice to see. Kinda makes me wonder what keeps stuff going long-term - habits or just not quitting?

Collapse
 
devops_fundamental profile image
DevOps Fundamental

Thanks, Nevo