Templates within Templates

We would like to request support for reusable template composition in Spacelift templates.

The goal is to allow a Spacelift template to reference other templates as reusable building blocks, while still supporting local stack definitions in the same parent template. This would extend the existing template model rather than replace it. Today, templates already define user inputs and a stacks array, each stack requires a unique key, and stack dependencies are handled through fields such as depends_on and stack_dependency_references.

The missing capability is the ability to reference another template from within a template and provide its required inputs explicitly.

For example, we may have reusable templates such as:

create_network
 stacks:
 - key: network
 - key: firewall_rules

create_vm
 stacks:
 - key: vm

add_dns_record
 stacks:
 - key: dns_record


Each of these templates may contain one or more stacks. These should be reusable in their own right, but they should also be available as building blocks inside larger templates.

An illustrative parent template could look something like this:

inputs:
 - id: environment
   name: Environment
   type: select
   options:
     - dev
     - test
     - prod

 - id: vm_name
   name: VM Name
   type: short_text

 - id: dns_zone
   name: DNS Zone
   type: short_text

templates:
 - key: network
   template: create_network
   inputs:
     environment: ${{ inputs.environment }}
     name: ${{ inputs.vm_name }}

 - key: vm
   template: create_vm
   inputs:
     environment: ${{ inputs.environment }}
     name: ${{ inputs.vm_name }}
     network_id: ${{ templates.network.network.vpc_id }}
     firewall_group_id: ${{ templates.network.firewall_rules.group_id }}

stacks:
   - key: bootstrap
   name: ${{ inputs.vm_name }}-bootstrap
   depends_on:
     - templates.vm.vm
   environment:
     stack_dependency_references:
       - name: VM_PRIVATE_IP
         from_stack: templates.vm.vm
         output: private_ip
   vcs:
     reference:
       value: main
       type: branch
     repository: bootstrap-repo
     provider: GITHUB
   vendor:
     terraform:
       manage_state: true
       version: "1.5.0"

   - key: dns_record
     name: ${{ inputs.vm_name }}-dns
     depends_on:
       - bootstrap
     environment:
       stack_dependency_references:
         - name: VM_PRIVATE_IP
           from_stack: templates.vm.vm
           output: private_ip
         - name: BOOTSTRAP_ID
           from_stack: bootstrap
           output: bootstrap_id
     vcs:
       reference:
         value: main
         type: branch
       repository: dns-repo
       provider: GITHUB
     vendor:
       terraform:
         manage_state: true
         version: "1.5.0"


The exact YAML schema could differ, but the important point is that a parent template should be able to contain both:

  1. references to other templates

  2. local stack definitions

A referenced template would need a unique key, a template reference, and an explicit input mapping. Those inputs could come from parent template inputs, static values, outputs from a specific stack inside another referenced template, or outputs from a local stack in the parent template.

For example:

network_id: ${{ templates.network.network.vpc_id }}

would refer to the vpc_id output from the network stack inside the referenced network template.

bootstrap_id: ${{ stacks.bootstrap.bootstrap_id }}

or an equivalent syntax would refer to the bootstrap_id output from a local stack in the parent template.

Likewise, dependency references may need to target a specific stack, not just a whole template. For example:

depends_on:
 - templates.vm.vm
 - bootstrap

In this example, templates.vm.vm means the vm stack inside the referenced vm template, while bootstrap means a local stack in the parent template.

This distinction matters because reusable templates will often contain more than one stack. A parent template may not always depend on the whole child template as a single unit. It may need to pass an output from, or depend on, one specific stack created by that referenced template.

This would allow organisations to build a proper catalogue of reusable templates, including both simple stack-only templates and larger templates composed from other templates and local stacks.

For example:

create_network
 - reusable template owned by the networking team
 - contains one or more network-related stacks

create_vm
 - reusable template owned by the virtualization team
 - contains one or more VM-related stacks

deploy_gitlab
 - higher-level self-service template
 - references create_network
 - references create_vm
 - defines its own local bootstrap or configuration stacks
 - maps inputs and stack outputs between all of them
Workaround
none
Problem
Today, Spacelift templates are useful for defining repeatable infrastructure patterns, but they cannot be composed from other templates. This creates a problem when multiple larger templates need to perform the same reusable action. For example, several templates may all need to create a network, create a virtual machine, or add a DNS record. Each of those actions may require one or more stacks, so it makes sense to define them as templates in their own right. The issue is that those smaller action templates cannot currently be included inside larger templates. As a result, the same stack definitions and supporting YAML have to be copied into multiple larger templates. This leads to duplicated code, inconsistent implementations, and more maintenance effort when shared actions need to change.

Please authenticate to join the conversation.

Upvoters
Status

πŸ‘€ In Review

Board

πŸ’‘ Feature Requests

Date

1 day ago

Subscribe to post

Get notified by email when there are changes.