Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
30a2f22
feat(ecs_cluster): Ravion-managed wildcard domain (opt-in)
siddhsuresh May 27, 2026
fb4da3b
feat(ecs_service): Ravion-managed domains (auto-FQDN + custom certs)
siddhsuresh May 27, 2026
67660a5
feat(static_site): Ravion-managed domains (CloudFront, us-east-1)
siddhsuresh May 27, 2026
cddbf96
feat(ecs): Ravion-managed domains support private ALB, not just public
siddhsuresh May 27, 2026
10b1227
use my proxy
siddhsuresh May 27, 2026
e1f338a
fix(ecs_cluster): name the Ravion wildcard from the module instance g…
siddhsuresh May 27, 2026
74d0889
fix(ecs_service): auto-FQDN leaf from module instance given id
siddhsuresh May 27, 2026
0fa3e66
feat(ecs_cluster): point wildcard cert at the cluster ALB
siddhsuresh May 27, 2026
a695922
fix(ecs_service): use name_prefix for TG so attribute changes don't c…
siddhsuresh May 28, 2026
291cac2
feat(static_site): add routing="raw" for object sites
siddhsuresh May 28, 2026
2f78c63
feat: pull ravion provider from CloudFront-hosted registry
siddhsuresh May 28, 2026
638ab14
revert: keep ravion provider source on providers.siddharthsuresh.dev
siddhsuresh May 28, 2026
3a4f1f7
Revert "revert: keep ravion provider source on providers.siddharthsur…
siddhsuresh May 28, 2026
21cf2d5
Revert "feat(static_site): add routing="raw" for object sites"
siddhsuresh May 28, 2026
c315c8f
feat(domains): unify cluster ALB HTTPS listener + surface managed-dom…
siddhsuresh May 29, 2026
64eb79e
feat(domains): collapse ecs_service Mode A/B into per-entry apex clas…
siddhsuresh May 29, 2026
ce3eac1
fix(ecs_service): normalize domain entries before apex classification
siddhsuresh May 29, 2026
1a4577f
feat(ecs_service): reject in-apex non-single-label domains at plan time
siddhsuresh May 29, 2026
9c97fcd
fix(managed-domains): review findings B3/M13/M14/#39/#40
siddhsuresh May 29, 2026
6591dd8
fix(ecs_cluster): create_before_destroy on cluster wildcard cert for …
siddhsuresh May 30, 2026
bbe5d8c
feat(ecs_cluster): reject duplicate wildcard apex via ravion_dns_coll…
siddhsuresh May 30, 2026
8e85aee
feat(ecs_service): plan-time parent-apex authorization + require prov…
siddhsuresh Jun 1, 2026
ac14c22
feat(ecs_cluster): surface wildcard-apex dependents via ravion_apex_d…
siddhsuresh Jun 1, 2026
3a3cdc5
docs(ecs_service): parent-apex guard wording reflects reference-based…
siddhsuresh Jun 1, 2026
b1a5779
feat(ecs): pass module_instance_id to Ravion certs/domains
siddhsuresh Jun 1, 2026
7fffc23
naming fixes
siddhsuresh Jun 4, 2026
477e0e2
Merge branch 'main' into managed-domains
siddhsuresh Jul 29, 2026
5f8c3c0
fix: finish post-review provider reconciliation missed by the merge
siddhsuresh Jul 29, 2026
a236a84
fix(ecs_service): managed mode hands ECS its own listener rule instea…
siddhsuresh Jul 29, 2026
fec5894
feat(definitions): expose managed-domains inputs in the rvn definitio…
siddhsuresh Jul 29, 2026
344a513
feat(definitions): hide BYO certificate/alias/routing inputs in manag…
siddhsuresh Jul 29, 2026
8453fa8
fix(managed-domains): require HTTPS on the cluster ALB, and stop mask…
siddhsuresh Jul 29, 2026
609b1b1
fix(managed-domains): preserve existing terraform addresses
siddhsuresh Jul 30, 2026
73d25fe
fix(managed-domains): stickiness parity, plan-time guards, and domain…
siddhsuresh Jul 30, 2026
1a28d6a
chore(managed-domains): require provider >= 1.0.2 and bump drifted si…
siddhsuresh Jul 30, 2026
38c8966
test: repair the pre-existing red suites so the ALB/HTTPS matrix actu…
siddhsuresh Jul 30, 2026
c64b69e
feat(managed-domains): keep BYO certificates attached via SNI when ma…
siddhsuresh Jul 30, 2026
e314110
feat(static-site): surface the Domains section right after the hostin…
siddhsuresh Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/terraform-static-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
run: |
set -euo pipefail

modules=$(find . -mindepth 2 -maxdepth 2 -name versions.tf -exec dirname {} \; | sort)
modules=$(find . -mindepth 3 -maxdepth 3 -name versions.tf -exec dirname {} \; | sort)

for module in $modules; do
echo "Validating ${module}"
Expand All @@ -52,6 +52,13 @@ jobs:
tofu -chdir=cdn/cloudfront init -backend=false
tofu -chdir=cdn/cloudfront test

- name: Test ECS managed domains
run: |
tofu -chdir=compute/ecs_cluster init -backend=false
tofu -chdir=compute/ecs_cluster test -filter=tests/managed_domains.tftest.hcl
tofu -chdir=compute/ecs_service init -backend=false
tofu -chdir=compute/ecs_service test -filter=tests/managed_domains.tftest.hcl

- name: Test CloudFront redirect function
run: node --test cdn/cloudfront/functions/redirect.test.js

Expand Down
10 changes: 10 additions & 0 deletions compute/autoscaling/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,13 @@ output "region" {
description = "The AWS region where the resources are deployed."
value = local.region
}

output "mixed_instances_policy_enabled" {
description = "Whether the ASG uses a mixed-instances (spot) policy."
value = length(aws_autoscaling_group.this.mixed_instances_policy) > 0
}

output "spot_instances_distribution" {
description = "The instances_distribution of the mixed-instances policy, null when spot is disabled."
value = try(aws_autoscaling_group.this.mixed_instances_policy[0].instances_distribution[0], null)
}
49 changes: 31 additions & 18 deletions compute/ecs_cluster/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 14 additions & 4 deletions compute/ecs_cluster/load_balancers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ module "public_alb" {
subnet_ids = var.public_subnet_ids
internal_load_balancer_enabled = false

# Listener configuration
# Listener configuration. Keep listener ownership in the ALB module so
# enabling managed domains changes only the default certificate in place.
http_listener_enabled = true
https_listener_enabled = var.public_alb_https_enabled
http_to_https_redirect_enabled = var.public_alb_https_enabled

# SSL/TLS
certificate_arns = var.public_alb_certificate_arns
# Managed mode makes the Ravion wildcard the DEFAULT certificate and keeps
# every BYO certificate attached via SNI (the submodule attaches arns[1..]
# as aws_lb_listener_certificate). Toggling managed domains on therefore
# never drops TLS for hostnames served off existing BYO certificates.
certificate_arns = local.enable_ravion_domain ? concat([ravion_aws_acm_certificate.cluster[0].arn], var.public_alb_certificate_arns) : var.public_alb_certificate_arns
ssl_policy = var.public_alb_ssl_policy

# ALB settings
Expand Down Expand Up @@ -55,13 +60,18 @@ module "private_alb" {
subnet_ids = var.private_subnet_ids
internal_load_balancer_enabled = true

# Listener configuration
# Listener configuration. Keep listener ownership in the ALB module so
# enabling managed domains changes only the default certificate in place.
http_listener_enabled = true
https_listener_enabled = var.private_alb_https_enabled
http_to_https_redirect_enabled = var.private_alb_https_enabled

# SSL/TLS
certificate_arns = var.private_alb_certificate_arns
# Managed mode makes the Ravion wildcard the DEFAULT certificate and keeps
# every BYO certificate attached via SNI (the submodule attaches arns[1..]
# as aws_lb_listener_certificate). Toggling managed domains on therefore
# never drops TLS for hostnames served off existing BYO certificates.
certificate_arns = local.enable_ravion_domain ? concat([ravion_aws_acm_certificate.cluster[0].arn], var.private_alb_certificate_arns) : var.private_alb_certificate_arns
ssl_policy = var.private_alb_ssl_policy

# ALB settings
Expand Down
34 changes: 34 additions & 0 deletions compute/ecs_cluster/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,37 @@ output "region" {
description = "The AWS region where the resources are deployed."
value = local.region
}

################################################################################
# Ravion-managed domains
################################################################################

output "ravion_cluster_certificate_id" {
description = "Ravion managed-certificate id for the cluster wildcard (null unless use_ravion_managed_domains)."
value = local.enable_ravion_domain ? ravion_aws_acm_certificate.cluster[0].id : null
}

output "ravion_cluster_domain_fqdn" {
description = "Cluster wildcard apex FQDN. Pass to ecs_service as cluster_parent_fqdn."
value = local.enable_ravion_domain ? ravion_aws_acm_certificate.cluster[0].domain_name : null
}

output "ravion_cluster_cert_arn" {
description = "ACM ARN of the cluster wildcard cert."
value = local.enable_ravion_domain ? ravion_aws_acm_certificate.cluster[0].arn : null
}

output "ravion_aws_account_id" {
description = "Pass-through Ravion AwsAccount row id for ecs_service Mode B."
value = var.ravion_aws_account_id
}

output "ravion_aws_region" {
description = "Pass-through Ravion cert region for ecs_service Mode B."
value = local.enable_ravion_domain ? coalesce(var.ravion_aws_region, local.region) : null
}

output "ravion_managed_domains_enabled" {
description = "True when the cluster owns a Ravion wildcard certificate for its selected HTTPS-enabled ALB. Services read this to show/hide managed-domain fields."
value = local.enable_ravion_domain
}
69 changes: 69 additions & 0 deletions compute/ecs_cluster/ravion_domains.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
################################################################################
# Ravion-managed cluster domain (opt-in)
################################################################################
# When var.use_ravion_managed_domains = true, Ravion issues ONE wildcard cert
# `*.<name>-<hash>.<ravion-apex>` (+ apex). That cert becomes the default cert
# on the selected cluster ALB's existing HTTPS listener. The cert also publishes
# a `*.<apex>` ALIAS to that ALB so service auto-FQDNs (<svc>.<apex>) resolve.
# When the flag is off, this resource is absent and the listener keeps using the
# customer-supplied certificate ARNs.

locals {
enable_ravion_domain = var.use_ravion_managed_domains
ravion_alb_count = (var.public_alb_enabled ? 1 : 0) + (var.private_alb_enabled ? 1 : 0)
}

# Plan-time guards (wildcard-apex collision against another cluster, and
# dependents on teardown) run automatically inside the provider's ModifyPlan —
# no data source + precondition wiring needed. The allocator enforces the same
# rules server-side as an apply-time backstop.
resource "ravion_aws_acm_certificate" "cluster" {
count = local.enable_ravion_domain ? 1 : 0

wildcard = true
name = coalesce(var.ravion_cluster_name, var.module_instance_given_id, var.name)
module_instance_id = var.module_instance_id
aws_account_id = var.ravion_aws_account_id
aws_region = coalesce(var.ravion_aws_region, local.region)

# Ravion publishes a *.<apex> ALIAS to this ALB so service auto-FQDNs
# (<svc>.<apex>) resolve under the cluster wildcard. Managed mode intentionally
# supports exactly one ALB because a wildcard DNS record can target only one
# load balancer.
target_dns_name = var.public_alb_enabled ? module.public_alb[0].alb_dns_name : try(module.private_alb[0].alb_dns_name, null)
target_zone_id = var.public_alb_enabled ? module.public_alb[0].alb_zone_id : try(module.private_alb[0].alb_zone_id, null)

lifecycle {
# Rotating the cluster wildcard cert (any RequiresReplace change, e.g. a
# renamed apex) must issue the new cert and swap it onto the HTTPS
# listener(s) BEFORE the old one is torn down. Without this, terraform
# destroys the old cert first while it is still the listener's default —
# ACM returns ResourceInUse and the rotation deadlocks. create_before_destroy
# makes it new -> listener in-place swap -> delete old (now detached).
create_before_destroy = true

# A wildcard DNS record can target only one ALB. Supporting simultaneous
# public/private managed domains requires distinct apexes and certificates.
precondition {
condition = !var.use_ravion_managed_domains || local.ravion_alb_count == 1
error_message = "use_ravion_managed_domains requires exactly one ALB. Enable either the public ALB or the private ALB, not both. Supporting both requires separate managed apexes and certificates."
}
# HTTPS is not optional for the selected managed ALB: its existing listener
# serves the wildcard certificate as the default certificate.
precondition {
condition = (
!var.use_ravion_managed_domains
|| (var.public_alb_enabled ? var.public_alb_https_enabled : var.private_alb_https_enabled)
)
error_message = "use_ravion_managed_domains requires HTTPS on the selected ALB. Enable public_alb_https_enabled or private_alb_https_enabled to serve the wildcard certificate."
}
precondition {
condition = !var.use_ravion_managed_domains || (var.ravion_aws_account_id != null && var.ravion_aws_account_id != "")
error_message = "ravion_aws_account_id (aws_*) is required when use_ravion_managed_domains = true."
}
precondition {
condition = !var.use_ravion_managed_domains || (var.module_instance_id != null && var.module_instance_id != "")
error_message = "module_instance_id (minst_*) is required when use_ravion_managed_domains = true. Inside a stack run the runner injects TF_VAR_module_instance_id; set it explicitly for external runs."
}
}
}
29 changes: 27 additions & 2 deletions compute/ecs_cluster/rvn-ecs-cluster-definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ definition:
name: ECS Cluster
description: Production-ready AWS ECS cluster with Fargate, Fargate Spot, optional EC2 capacity, and shared load balancers.
release:
version: 0.5.0
description: "BREAKING: Rename ec2_managed_termination_protection to ec2_managed_termination_protection_enabled and ec2_managed_scaling_status to ec2_managed_scaling_enabled; both inputs now accept booleans instead of ENABLED or DISABLED strings."
version: 0.6.2
description: "Managed domains keeps every BYO certificate attached via SNI when the Ravion wildcard takes over as the default certificate, so enabling it never drops TLS for existing custom hostnames."
module:
inputs:
- id: network
Expand Down Expand Up @@ -51,6 +51,25 @@ module:
value: enabled
- label: Disabled
value: disabled
- id: section_managed_domains
label: Managed domains
type: section
- default: false
description: Have Ravion issue and manage a shared wildcard TLS certificate and DNS for this cluster. The selected ALB HTTPS listener serves the wildcard certificate, and services get automatic domains under the cluster apex. Requires exactly one HTTPS-enabled ALB.
id: use_ravion_managed_domains
label: Ravion-managed domains
type: boolean
- description: Subdomain for the cluster's wildcard domain. Defaults to the module given ID when empty.
id: ravion_cluster_name
label: Cluster subdomain
patterns:
- message: The subdomain must contain only lowercase letters, numbers, and hyphens, and start and end with a letter or number.
pattern: ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$
required: false
show_when:
use_ravion_managed_domains: true
suffix: ".<< platform.apex >>"
type: string
- id: section_capacity
label: Capacity providers
type: section
Expand Down Expand Up @@ -245,6 +264,7 @@ module:
show_when:
public_alb_enabled: true
public_alb_https_enabled: true
use_ravion_managed_domains: false
- collapsible: true
description: SSL policy for public ALB HTTPS.
id: public_alb_ssl_policy
Expand Down Expand Up @@ -315,6 +335,7 @@ module:
show_when:
private_alb_enabled: true
private_alb_https_enabled: true
use_ravion_managed_domains: false
- collapsible: true
description: SSL policy for private ALB HTTPS.
id: private_alb_ssl_policy
Expand Down Expand Up @@ -696,6 +717,10 @@ module:
public_nlb_security_group_ids: << module.input.public_nlb_security_group_ids >>
public_subnet_ids: << module.input.public_subnet_ids || [] >>
region: << module.input.aws_region >>
use_ravion_managed_domains: << module.input.use_ravion_managed_domains >>
ravion_cluster_name: << module.input.ravion_cluster_name >>
ravion_aws_account_id: << module.input.aws_account_id >>
ravion_aws_region: << module.input.aws_region >>
tags:
$include: ../stack/ravion-tags.yml
vpc_id: << module.input.vpc_id >>
Expand Down
Loading
Loading