AWS ACM module
Imports (and on renewal re-imports) the certificate into AWS Certificate Manager. AWS propagates a same-ARN re-import to every attached resource — CloudFront, ALB/ELB, API Gateway — so no CloudFront/ELB API calls are needed.
Overview
CloudFront, ALB/ELB and API Gateway don't accept a certificate directly — they reference an ACM certificate by ARN. When a certificate is re-imported into the same ARN, AWS automatically pushes the new key material to every resource using that ARN. This single module therefore covers all three services without touching their APIs.
- Type:
awsacm, credential type:aws_aksk(access key ID + secret access key), slotdefault. - Per target: resolve the ACM ARN →
acm:ImportCertificate(leaf + private key + intermediate chain) → persist the resulting ARN for the next renewal. - Rollback: not supported — a re-import overwrites the ACM certificate in place and AWS has already propagated it.
Region rules
- CloudFront requires the certificate in
us-east-1— always, regardless of where your origin runs. - ALB/ELB and API Gateway use the resource's own region — set
regionaccordingly. - CloudFront accepts only ECDSA P-256 or RSA-2048 keys. An RSA-3072/4096 or ECDSA P-384 certificate imports into ACM fine but CloudFront rejects it on association — pick the certificate's key type accordingly.
- A
certificate_arnwhose region segment disagrees with the targetregionis rejected up front (AWS_IMPORT) instead of surfacing as an opaque AWS ResourceNotFound.
Create the AWS credential
In the AWS console:
- IAM → Policies → Create policy → JSON — paste the policy below, name it (e.g.
CertAutoPilotACM). - IAM → Users → Create user (e.g.
certautopilot-acm), no console access. On the permissions step choose Attach policies directly and select the policy. - Open the user → Security credentials → Access keys → Create access key → use case Application running outside AWS → Create.
- Copy the Access key ID and Secret access key — the secret is shown only once.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CertAutoPilotACM",
"Effect": "Allow",
"Action": [
"acm:ImportCertificate",
"acm:DescribeCertificate",
"acm:ListCertificates",
"acm:ListTagsForCertificate",
"acm:AddTagsToCertificate"
],
"Resource": "*"
}
]
}
No CloudFront/ELB/API Gateway permissions are needed. Scope Resource down to specific certificate ARNs in production if you prefer.
Then in CertAutoPilot: Settings → Distribution → Credentials → Add Credential → type AWS Access Key (AK/SK) (aws_aksk) → paste both values → Save.
Create an ACM target
Settings → Distribution → Targets → New → module AWS ACM (CloudFront / ALB / API Gateway).
| Field | Required | Description |
|---|---|---|
region | yes | AWS region of the ACM certificate (us-east-1 for CloudFront). |
certificate_arn | no | Re-import into this existing ACM certificate (adopt a cert already attached to CloudFront/ALB — keeps attachments). Empty → reuse the ARN remembered from a previous run, else create a new certificate. |
certificate_name | no | Name tag applied only when a new certificate is created; ignored on re-import. Empty = auto-generated per certificate as cap-<token>-<domain>. Both the ARN and the Name can be overridden per distribution from the certificate's Distributions → Overrides drawer. |
Pick the aws_aksk credential from the previous step.
Idempotent create — no duplicates
Every certificate the module creates is tagged certautopilot:certificate-id=<id>. On a run with no known ARN, the module first searches ACM (paginated ListCertificates, pre-filtered by primary domain, then ListTagsForCertificate) for a certificate carrying that tag and re-imports into it instead of creating a duplicate. This closes the orphan window left by a cancelled or raced first run.
ARN resolution priority: operator-set certificate_arn → ARN remembered from a previous run (remote state) → tag discovery → create new.
First-time CloudFront attachment
The first import creates the ACM certificate but nothing references it yet. Once, in the CloudFront console:
- Edit the distribution → Settings.
- Add your domain under Alternate domain name (CNAME).
- Under Custom SSL certificate, select the ACM certificate the module created (its
Nametag =certificate_name). - Save and wait for the distribution to deploy (minutes).
Every subsequent renewal re-imports into the same ARN and CloudFront picks it up automatically — no console visits, no further configuration.
ACM's managed renewal applies only to certificates ACM itself issued. Imported certificates are never auto-renewed by AWS — CertAutoPilot drives every renewal and re-imports into the same ARN. Ignore the "renewal ineligible" note on imported certs in the ACM console.
Operations
| Operation | Behavior |
|---|---|
| Execute | Import (create) or re-import (in-place update) into ACM; stores the resulting ARN as target remote state. |
| DryRun | With an ARN: DescribeCertificate and report "update"; without: ListCertificates to prove credentials/region and report "create". |
| Validate | DescribeCertificate — confirms the ACM cert exists. End-to-end serving checks belong to a tls_fingerprint validation endpoint since CloudFront propagation is asynchronous. |
| Rollback | Not supported (rollback_available stays false). |
| HealthCheck | ListCertificates in the target region (10 s timeout) — credential/region reachability. |
Error codes
| Code | Meaning | Resolution |
|---|---|---|
AWS_CREDENTIAL | Missing or malformed aws_aksk credential | Provide a credential JSON with access_key_id and secret_access_key. |
AWS_AUTH | AWS rejected the credentials or denied the action | Check the access key and that the IAM policy grants the acm:* actions above. |
AWS_CONNECT | Could not reach the AWS endpoint (also used for ACM throttling, which is retried as transient) | Check outbound network access to acm.<region>.amazonaws.com. |
AWS_IMPORT | ImportCertificate failed — bad chain, key mismatch, or ARN region ≠ target region | Chain must be intermediates-only; key must match the certificate; fix the region. |
Troubleshooting
Browser says the certificate isn't trusted behind CloudFront
CloudFront serves whatever you imported — including a private-CA certificate that public browsers won't trust. Viewer-facing CloudFront certificates must come from a publicly trusted CA (e.g. your ACME issuer), not from an internal AD CS CA.
CloudFront serves the *.cloudfront.net certificate instead of mine
The Alternate domain name (CNAME) is missing on the distribution, or the custom SSL certificate isn't selected. Complete the first-time attachment steps.
Renewal succeeded but CloudFront still serves the old certificate
Propagation after a re-import takes minutes, not seconds. Wait, or configure a validation endpoint with generous retries against the CloudFront domain to get an explicit signal when the new cert is live.
A duplicate ACM certificate appeared
Should not happen for module-created certs (tag discovery prevents it). It can if the certautopilot:certificate-id tag was manually removed or the IAM policy lacks acm:ListTagsForCertificate/acm:AddTagsToCertificate. Set certificate_arn on the target to pin the correct one, and delete the orphan in the ACM console.