Usage#
dxaws-acm manages DNS-validated ACM certificates and can optionally converge the required Route53 validation records.
The primary public entry point is the manager:
AcmManager.execute(desired, options=...)
Basic example#
Request (or converge) a DNS-validated certificate for a single domain.
from dxaws_acm.manager import AcmManager, ExecuteOptions, ApplyOptions
from dxaws_acm.models import AcmDesired
# AWS is the default provider.
mgr = AcmManager()
desired = AcmDesired(
domain_name="example.test.dxaws.com",
subject_alternative_names=[],
# Prefer zone_name; the manager will resolve the hosted zone id.
zone_name="test.dxaws.com",
# CloudFront certificates must be in us-east-1.
region="us-east-1",
tags={"dxaws:module": "acm"},
)
opts = ExecuteOptions(
apply_options=ApplyOptions(
# waiting controls for DNS publication + issuance
max_wait_seconds=900,
poll_interval_seconds=15,
)
)
result = mgr.execute(desired, options=opts)
print(result.outputs.certificate_arn)Notes:
- The manager waits for ACM to publish DNS validation records, converges the validation records via the DNS integration, then waits for
ISSUED. - Re-running the same call is idempotent.
With Subject Alternative Names (SANs)#
desired = AcmDesired(
domain_name="example.test.dxaws.com",
subject_alternative_names=[
"www.example.test.dxaws.com",
"static.example.test.dxaws.com",
],
zone_name="test.dxaws.com",
# CloudFront certificates must be in us-east-1.
region="us-east-1",
tags={"dxaws:module": "acm"},
)
result = mgr.execute(desired, options=opts)
print(result.outputs.certificate_arn)Overriding providers#
Most callers should use AcmManager() directly and treat providers as an internal implementation detail.
Providers exist primarily for dependency injection (testing, recording providers, alternate backends). If you need to override providers explicitly:
from dxaws_core import AwsSession
from dxaws_acm.manager import AcmManager
from dxaws_acm.models import AcmDesired
from dxaws_acm.providers.aws import AwsProvider
from dxaws_acm.providers.route53 import Route53RecordProvider
aws = AwsSession(region_name="us-east-1")
acm = AwsProvider(aws=aws)
dns = Route53RecordProvider(aws=aws)
mgr = AcmManager(provider=acm, dns_provider=dns)
desired = AcmDesired(
domain_name="example.test.dxaws.com",
subject_alternative_names=[],
zone_name="test.dxaws.com",
region="us-east-1",
tags={"dxaws:module": "acm"},
)
result = mgr.execute(desired)
print(result.outputs.certificate_arn)If you find yourself needing direct access to a provider method from application or composition code, prefer exposing that capability via the manager interface instead.
Recommended acceptance workflow (real AWS)#
Acceptance tests are opt-in and only run when DXAWS_AWS_TESTS=1.
Recommended environment variables (export once in your shell):
DXAWS_ACCEPTANCE_ACCOUNT– dedicated test account idDXAWS_ACCEPTANCE_ZONE_NAME– hosted zone used for validation (e.g.test.dxaws.com)DXAWS_TEST_REGION– AWS region for the run (e.g.ca-central-1,us-east-1for cloudfront)
Run:
DXAWS_AWS_TESTS=1 make accept-acmTo keep resources for debugging:
DXAWS_AWS_TESTS=1 DXAWS_ACCEPTANCE_CLEANUP=0 make accept-acmTroubleshooting#
Certificate is stuck in PENDING_VALIDATION#
Common causes:
- The hosted zone is wrong (or not delegated correctly)
- Route53 validation records were not created
- You are requesting in the wrong AWS region
What to check:
- Confirm the test is running in the expected AWS account (the acceptance output prints STS identity)
- Confirm
DXAWS_ACCEPTANCE_ZONE_NAMEresolves to the correct public hosted zone - Confirm the validation CNAME exists in the hosted zone
ResourceNotFoundException during drift tests#
ACM is eventually consistent: certificates can appear in list results but fail describe calls briefly.
dxaws-acm hardens this by skipping certificates that disappear between list/describe when enriching list results.
Patch for tests/acceptance/test_website_sequential_acceptance.py#
assert bucket_name_out_2 == bucket_name_out, (
f"Expected same bucket name on idempotent apply; first={bucket_name_out!r} second={bucket_name_out_2!r}"
)
# Step gate: default to stopping after S3 while we build this up incrementally.
# Set DXAWS_ACCEPTANCE_STEP=acm (or later steps) to continue.
step = (os.getenv("DXAWS_ACCEPTANCE_STEP") or "s3").strip().lower()
if step in {"s3", "bucket"}:
return