Configuration Reference¶
All infrastructure configuration lives in Pulumi.<stack-name>.yaml. See Pulumi.example.yaml for a fully documented reference with all available options.
Required Settings¶
| Config Key | Description | Example |
|---|---|---|
hawk:domain |
Internal domain for services | hawk.example.com |
hawk:publicDomain |
Public domain for DNS zones | example.com |
hawk:primarySubnetCidr |
VPC CIDR block | 10.0.0.0/16 |
Domain & DNS¶
Hawk's services live on subdomains of hawk:domain (e.g. api.hawk.example.com) and need DNS delegation working before ACM TLS certificates can validate. Four paths — pick one before deploying:
| Option | When | What to set |
|---|---|---|
| A. Route 53 Domains | New setup, simplest path. AWS handles registration, hosted zone, and delegation in a single step. | hawk:createPublicZone: "false" (the default) — Pulumi looks up the existing zone created by registration. |
| B. Existing registrar + manual delegation | You already own the domain at Namecheap/GoDaddy/etc. and want to keep it there. | hawk:createPublicZone: "true". After pulumi up creates the zone, copy the four NS records into your registrar's nameserver settings. |
| C. Cloudflare automatic delegation | Parent domain is in Cloudflare and you want a subdomain delegated to AWS automatically. | See Cloudflare below. |
| D. HTTP-only (testing only) | Smoke-testing without a real domain. | hawk:skipTlsCerts: "true". Services reachable only via the raw ALB DNS name. Not for real use. |
Cert-validation hang gotcha
With options B or C, if DNS isn't working when pulumi up runs, the wildcard ACM certificate validation will hang for ~75 minutes (default timeout) before failing. Get delegation in place first.
Don't set createPublicZone: \"true\" with Option A
If you registered the domain in Route 53 (Option A), it already created a public hosted zone and pointed the registrar's NS records at it. Setting createPublicZone: "true" makes Pulumi silently create a second zone with different NS records, put all records there, and ACM validation will hang ~75 minutes before failing because the registrar still points at the original zone. Recovery requires either manually updating the registrar's NS records (Console only — standard IAM users typically lack route53domains:UpdateDomainNameservers) or destroying the duplicate Pulumi-created zone and re-running with createPublicZone: "false".
Authentication¶
When hawk:oidcClientId is not set, Hawk provisions a Cognito user pool during pulumi up and uses it as the auth provider. Create your first user with scripts/dev/create-cognito-user.sh <stack> <email> after the deploy finishes.
To use your own OIDC provider (Okta, Auth0, etc.), set all three of these — Hawk will skip the Cognito setup:
| Config Key | Description | Example |
|---|---|---|
hawk:oidcClientId |
OIDC client ID | your-client-id |
hawk:oidcAudience |
OIDC audience for access tokens | https://api.example.com |
hawk:oidcIssuer |
OIDC issuer URL | https://login.example.com/oauth2/default |
Infrastructure Options¶
| Config Key | Default | Description |
|---|---|---|
hawk:eksK8sVersion |
1.33 |
Kubernetes version for EKS |
hawk:albIdleTimeout |
3600 |
ALB idle timeout in seconds |
hawk:albInternal |
false |
Set to true to make the ALB internal (requires VPN) |
hawk:cloudwatchLogsRetentionDays |
14 |
CloudWatch log retention |
Optional Integrations¶
These are all disabled by default. Enable them in your stack config when needed.
Datadog¶
Monitoring, APM, and log forwarding:
Requires a <env>/platform/datadog-api-key secret in AWS Secrets Manager.
Cloudflare¶
Option C from the Domain & DNS table above. Hawk creates NS records in your Cloudflare parent zone pointing to the Route 53 hosted zone, so you don't have to move nameservers manually.
hawk:publicDomain must be a subdomain of hawk:cloudflareParentDomain (Pulumi raises a ValueError otherwise).
hawk:createPublicZone: "true"
hawk:cloudflareZoneId: "your-zone-id"
hawk:cloudflareParentDomain: "example.com"
hawk:publicDomain: "hawk.example.com"
Before pulumi up, create a Cloudflare API token (Zone:DNS:Edit on the parent zone) and store it in AWS Secrets Manager:
aws secretsmanager create-secret \
--name "<env>/platform/cloudflare-api-token" \
--secret-string "<token>"
<env> defaults to your Pulumi stack name. The deploy will fail with a "secret not found" error if this isn't set up first.
Tailscale¶
VPN overlay for private service access:
Set hawk:albInternal: "true" and store a Tailscale auth key in AWS Secrets Manager. This makes all services accessible only through your Tailscale network.
CrowdStrike Falcon¶
Endpoint protection for EKS nodes and the Tailscale subnet router:
Requires a <env>/platform/crowdstrike secret in AWS Secrets Manager with:
{
"cid": "YOUR-CUSTOMER-ID",
"client_id": "YOUR-API-CLIENT-ID",
"client_secret": "YOUR-API-CLIENT-SECRET",
"base_url": "https://api.us-2.crowdstrike.com"
}
Setup:
- In the CrowdStrike Falcon console, go to Support and resources > API clients and keys and create a client with Sensor Download: Read scope.
- Copy your CID from Host setup and management > Deploy > Sensor downloads.
- Your
base_urlmatches your Falcon console URL (e.g.falcon.us-2.crowdstrike.com→https://api.us-2.crowdstrike.com).
When enabled, this installs the Falcon sensor on:
- GPU nodes (AL2023) — via the Karpenter EC2NodeClass userData at instance boot
- Tailscale subnet router (AL2023 ARM64) — via cloud-init at instance boot
The falcon-sensor DaemonSet for all EKS nodes (including Bottlerocket) requires the Falcon Images Download API scope, which is part of the Falcon Cloud Security with Containers add-on.
Budget Alerts¶
When integrations are disabled, services fall back to simpler alternatives (CloudWatch instead of Datadog, no DNS delegation, etc.).