#Dev Log#AWS#Infrastructure#GitHub Actions
Two Gotchas from Migrating a Shopify Domain to CloudFront
An ACM certificate that refused to validate because of Shopify's CAA policy, and a GitHub OIDC subject claim that didn't look like the docs said it would.
We recently moved one of our sites (hire-veai.com, our client-work page) off Shopify onto the same stack as the rest of the VEAI LAB. sites: S3 + CloudFront, deployed by GitHub Actions via OIDC. The migration was staged for zero downtime — Route 53 zone pre-populated, nameserver switch last. Two things still bit us, and both are the kind of error you stare at for a while. Writing them down for the next person who searches the error string.
Gotcha 1: ACM CAA_ERROR caused by a keep-alive CNAME
To avoid downtime during the nameserver switch, our staged Route 53 zone temporarily kept the old records alive — including www → shops.myshopify.com.
When we requested the ACM certificate for example.com + www.example.com, the apex validated fine and www failed with CAA_ERROR. The reason: CAA lookups follow CNAMEs. ACM chased www to Shopify’s domain, found Shopify’s CAA policy — which, reasonably, does not authorize Amazon to issue certificates — and refused.
The fix:
- Delete the temporary
www → shopifyCNAME (the apex A record was untouched — A records don’t redirect CAA lookups). - Add an explicit
CAA 0 issue "amazon.com"(plusissuewild) record to the zone. - Request a fresh certificate.
If you ever stage a migration with keep-alive CNAMEs pointing at the old provider, remember that ACM validation will follow them. And once you add the CAA record, keep it — future re-issuance needs it too.
Gotcha 2: the GitHub OIDC sub claim had numeric IDs in it
The deploy role’s trust policy matched the documented subject format:
repo:owner/repo:ref:refs/heads/main
The deploy failed to assume the role anyway. Decoding the actual OIDC token showed a subject we hadn’t seen documented:
repo:owner@84943164/repo@1301704300:ref:refs/heads/main
— owner and repository database IDs embedded after @. The slug-only pattern in the trust policy never matched.
The fix was to make the trust policy accept both shapes:
"StringLike": {
"token.actions.githubusercontent.com:sub": [
"repo:owner/repo:*",
"repo:owner@*/repo@*:*"
]
}
ID-embedded subjects are a real GitHub OIDC variant (it protects against slug-reuse attacks after renames), but most tutorials only show the slug form. If your AssumeRoleWithWebIdentity fails and every slug looks right, decode the token and look at the sub with your own eyes before trusting any example policy.
Why we bother
hire-veai.com is a one-page static site; nobody would notice if it deployed by hand. But the point of running our own sites on the same serverless discipline — OIDC-only credentials, least-privilege roles, staged DNS — is that it is the same discipline behind our care apps, where the data actually matters. If you want to see what that looks like in product form, start at our apps — CareQuest is live, free, and keeps its records on your device.

Thanks for reading — built with care, for caregivers.