Skip to content

CloudFormation Template

The CloudFormation template (src/infra/cloudformation/template.yaml in the hq-deploy repo) provisions the HQ Deploy data plane. It’s parameterized, supports conditional SSR resources, and includes a cross-account IAM role for the control plane. It is deployed with the repo’s own scripts (scripts/bootstrap.sh); no hq-deploy CLI command deploys it.

hq-deploy init is separate: it writes a smaller starter template into your project (see Deploy CLI).

Parameters

Selected parameters (the template also takes Cognito, front-door, shared-layer (SharedDepsLayerS3Key), and throttling parameters):

ParameterRequiredDefaultDescription
OrgSlugYes—Organization identifier (used in resource naming)
CompanyUidYes—Company UID from the control-plane org record
DomainNameYes—Base domain (e.g., deploy.acme.com)
HostedZoneIdNo—Existing Route 53 hosted zone for the domain; when set, the template creates DNS records in it
VpcIdOnly with EnableSSR=true—VPC for ALB and ECS
SubnetIdsOnly with EnableSSR=true—Comma-separated subnet IDs for ALB and ECS
RegionNous-east-1AWS region
EnableSSRNofalseProvision ECS Fargate infrastructure
ControlPlaneAccountIdNo—AWS account ID for cross-account trust

Resources Created

Always Created (Static Hosting)

ResourceName PatternPurpose
S3 Buckethq-deploy-{OrgSlug}-assetsAsset storage with versioning
CloudFront Distribution—Global CDN with wildcard cert
ACM Certificate*.{DomainName}Wildcard TLS (us-east-1, DNS validation)
DynamoDB Tablehq-deploy-{OrgSlug}Single table for orgs, apps, deploys, and related records
Lambda@Edge—Subdomain router

The template does not create a hosted zone. When HostedZoneId is set, it adds a wildcard *.{DomainName} alias record for CloudFront to that zone.

Conditional (per-app functions — when SharedDepsLayerS3Key is set)

Next.js and Hono apps run on these resources.

ResourceName PatternPurpose
Lambda Layerhq-deploy-{OrgSlug}-shared-depsShared runtime dependencies for every per-app function
API Gateway HTTP API + router Lambda—Shared front door that routes {app}.{domain} to the app’s function
Route 53 Recordfront-door.{DomainName}Alias to the front door (only when HostedZoneId is set)

Conditional (dormant ECS path — when EnableSSR=true)

ResourcePurpose
ECS Fargate ClusterContainer orchestration
ALB + HTTPS ListenerLoad balancing with wildcard cert
ECR RepositoryDocker image storage
Security GroupsCloudFront prefix list for ALB ingress

IAM

RoleTrustScope
hq-deploy-{OrgSlug}-deployerThe stack’s own account, plus the control plane account if ControlPlaneAccountId is sethq-deploy-* resources only

Cross-account assumption requires the ExternalId hq-deploy-{OrgSlug} to prevent the confused deputy problem — a third party can’t assume the role even if they know the ARN.

Outputs

OutputDescription
DeployRoleArnARN of the cross-account deploy role
BucketNameS3 bucket name for assets
DistributionIdCloudFront distribution ID
TableNameDynamoDB table name
HostedZoneIdThe hosted zone passed in (only when HostedZoneId is set)

Version Tracking

The template records its version in Metadata.Version:

Metadata:
Version: '2.1.0'

There is no hq-deploy upgrade command; stack updates are applied with the repo’s deploy scripts.