cloudtools / stacker

An AWS CloudFormation Stack orchestrator/manager.
http://stacker.readthedocs.io/en/stable/
BSD 2-Clause "Simplified" License
711 stars 167 forks source link

Templating language for lookups #363

Open ejholmes opened 7 years ago

ejholmes commented 7 years ago

Something we talked about briefly internally. The current syntax for specifying lookups looks something like this:

variables:
  VpcId: ${output vpc::VpcId}

It allows you to nest outputs, but with some caveats.

It could be nice to support a more sophisticated templating language. Specifically, it would be nice to be able to perform simple transformations on config/outputs before providing them as a variable (e.g. conditionals, math, etc). Not unlike terraform's interpolation: https://www.terraform.io/docs/configuration/interpolation.html#conditionals.

ejholmes commented 7 years ago

On a similar note, at times I've felt it would be easier to describe everything in Python, rather than YAML. That would easily allow for transformations, conditions, and math using regular python without having to go the full on templating route. It's a trade off, because you'd loose some static analysis that you get from YAML, but could be nice for building out more sophisticated meta stacks. Given a yaml config like this:

stacks:
  - name: vpc
    class_path: blueprints.Vpc
  - name: bastion
    class_path: blueprints.Bastion
    variables:
      VpcId: ${output vpc::VpcId}
  - name: cluster
    class_path: blueprints.Cluster
    variables:
      MinSize: ${cluster_min_size}
      VpcId: ${output vpc::VpcId}

I'd expect a simple Python API like so:

from stacker import Stack, Config
import blueprints

def config(environment):
    vpc = Stack(
        "vpc",
        blueprints.Vpc)
    bastion = Stack(
        "bastion",
        blueprints.Bastion,
        requires=[vpc],
        variables={
            "VpcId": lambda: vpc.Output("VpcId")})
    cluster = Stack(
        "cluster",
        blueprints.Cluster,
        requires=[vpc],
        variables={
            "MinSize": environment["cluster_min_size"],
            "VpcId": lambda: vpc.Output("VpcId")})

    return Config(stacks=[vpc, bastion, cluster])

So you could just point stacker at a .py file, instead of .yaml, and it'd execute the config function within it to get a compiled Config.

At the very least, even if this wasn't exposed to the stacker command, this could simplify the process of going from a yaml to a compiled Config that can be executed.