Is your feature request related to a problem? Please describe.
When building multi-account, multi-region infrastructure with Terramate Bundles, we need to include account and region path segments in the generated stack paths. These values come from other organizational Bundles (Account, Region) that are looked up via tm_bundle.
The problem is that tm_bundle and tm_bundles functions are not available in metadata { path } expressions inside define bundle stack blocks — they only work in component { inputs }.
This forces two workarounds:
-
Encoding compound values as split-able strings. Instead of looking up the Region Bundle directly in the path, we store the region input as account_alias/region (e.g., demo-account/us-east-1) and extract the parts with tm_split:
path = "/stacks/${bundle.environment.id}/${tm_split("/", bundle.input.region.value)[0]}/${tm_split("/", bundle.input.region.value)[1]}/fargate-clusters/${tm_slug(bundle.input.name.value)}/vpc"
This is fragile, verbose, and not self-documenting. The / separator is a convention with no validation.
-
Dependent Bundles cannot inherit values for paths. An ECS Service Bundle depends on an ECS Cluster Bundle that already selected a region. Ideally, the service should derive its account/region path segments from the cluster without asking the user again. But since tm_bundle is unavailable in metadata { path }, the service must have its own redundant region input that the user selects separately.
Describe the solution you'd like
A lets block inside define bundle stack that:
- Supports
tm_bundle and tm_bundles function calls
- Exposes intermediate variables to all other blocks within the same stack definition (
metadata, component)
- Allows chaining lookups across Bundles
Example:
define bundle stack "vpc" {
lets {
region_bundle = tm_bundle("example.com/region/v1", bundle.input.region.value, bundle.environment.id)
account_alias = lets.region_bundle.export.account_alias.value
region = lets.region_bundle.export.region.value
}
metadata {
path = "/stacks/${bundle.environment.id}/${lets.account_alias}/${lets.region}/fargate-clusters/${tm_slug(bundle.input.name.value)}/vpc"
}
component "vpc" {
source = "/components/example.com/terramate-aws-vpc/v1"
inputs = {
name = tm_join("-", [tm_slug(bundle.input.name.value), bundle.environment.id])
cidr = bundle.input.vpc_cidr.value
}
}
}
This would also enable true inheritance for dependent Bundles. The ECS Service could derive account/region from the cluster without its own input:
define bundle stack "ecs-service" {
lets {
cluster = tm_bundle("example.com/tf-aws-complete-ecs-fargate-cluster/v1", bundle.input.cluster_slug.value, bundle.environment.id)
region_bundle = tm_bundle("example.com/region/v1", lets.cluster.export.region_ref.value, bundle.environment.id)
account_alias = lets.region_bundle.export.account_alias.value
region = lets.region_bundle.export.region.value
}
metadata {
path = "/stacks/${bundle.environment.id}/${lets.account_alias}/${lets.region}/fargate-clusters/${bundle.input.cluster_slug.value}/workloads/${tm_slug(bundle.input.service_name.value)}"
}
}
Describe alternatives you've considered
- Making
tm_bundle/tm_bundles available directly in metadata { path } — This would solve the immediate problem but without intermediate variables, complex lookups would still result in very long, repeated inline expressions.
- Adding
tm_bundle/tm_bundles support to define bundle { export } blocks — This would allow Bundles to export resolved values from other Bundles, shifting the lookup from path expressions to exports. However, it doesn't eliminate repeated expressions within a single stack definition.
Additional context
- Terramate Version: v0.17.0-rc2
- This came up while adding multi-account, multi-region hierarchy to the terramate-catalyst-examples repository (PR #49)
- The
lets concept mirrors Terraform's locals block and is already familiar to the target audience
Is your feature request related to a problem? Please describe.
When building multi-account, multi-region infrastructure with Terramate Bundles, we need to include account and region path segments in the generated stack paths. These values come from other organizational Bundles (Account, Region) that are looked up via
tm_bundle.The problem is that
tm_bundleandtm_bundlesfunctions are not available inmetadata { path }expressions insidedefine bundle stackblocks — they only work incomponent { inputs }.This forces two workarounds:
Encoding compound values as split-able strings. Instead of looking up the Region Bundle directly in the path, we store the region input as
account_alias/region(e.g.,demo-account/us-east-1) and extract the parts withtm_split:This is fragile, verbose, and not self-documenting. The
/separator is a convention with no validation.Dependent Bundles cannot inherit values for paths. An ECS Service Bundle depends on an ECS Cluster Bundle that already selected a region. Ideally, the service should derive its account/region path segments from the cluster without asking the user again. But since
tm_bundleis unavailable inmetadata { path }, the service must have its own redundantregioninput that the user selects separately.Describe the solution you'd like
A
letsblock insidedefine bundle stackthat:tm_bundleandtm_bundlesfunction callsmetadata,component)Example:
This would also enable true inheritance for dependent Bundles. The ECS Service could derive account/region from the cluster without its own input:
Describe alternatives you've considered
tm_bundle/tm_bundlesavailable directly inmetadata { path }— This would solve the immediate problem but without intermediate variables, complex lookups would still result in very long, repeated inline expressions.tm_bundle/tm_bundlessupport todefine bundle { export }blocks — This would allow Bundles to export resolved values from other Bundles, shifting the lookup from path expressions to exports. However, it doesn't eliminate repeated expressions within a single stack definition.Additional context
letsconcept mirrors Terraform'slocalsblock and is already familiar to the target audience