Yaml.sh — YAML Sans Helm

It seems like everybody is templating YAML these days. Kubernetes decided that the world needed another declarative infrastructure as code deployment layer and suddenly declaring things is all the rage again!
Only this time, it’s not using a Domain-Specific Language (DSL), which would make it easier to write and validate in an IDE. Instead, it’s using a vaguely human readable Data Serialization Language (also DSL?), YAML, a format so flexible it doesn’t even natively support schemas.
Why Template?
Of course, no one really wants to write verbose Kubernetes resource definitions, designed to flexibly support a million cases. It turns out what people actually want is to template every application individually and create a bespoke interface instead… also in YAML.
It didn’t take long for Helm to come along and promise to solve everyone’s problems by trying to combine a template engine and a package manager. Now, instead of just copying deployments from Stack Overflow and curl-bashing them into production, you can have Helm pull down random code from the Internet and execute it for you. That is “The Docker Way”, after all!
Local Rendering
But then the security police came along and reminded everyone that not only was random code on the internet probably delivered by malicious third-parties and full of viruses, but Helm’s backend, Tiller, was also impossible to secure with anything other than self-signed certificates, duct tape, and prayers.
So people went back to just using Helm for client side templating, something you can now do in only a few keystrokes (used to be a much longer hack before helm template was added):
helm template --namespace “${NAMESPACE}” --name “${RELEASE_NAME}” -—values “${CONFIG_PATH}” “${CHART_PATH}”
In fact, some continuous deployment tools, like Spinnaker, do this for you. And while that might be ok for complex open source tools you found in the Cloud Native Wasteland, telling you what you should and should not configure, it’s a pretty verbose way to template your internal web applications and backend microservices. After all, if you’re actually DevOps-ing, does developer-you really need to tell operator-you what you can and can’t configure? Do you really need to learn a new templating language?
A New Standard

So, I designed a simple tool that you can use instead. It’s lightweight, just a few lines of code on top of your YAML. Its flexible, offering both imperative and declarative interfaces. It’s powerful, handling input validation right out of the box. It’s so easy to use, you probably already know it. And you can get started right away, because it’s probably already installed on your machine! It’s called bash.
Just write your template file like so:
#!/usr/bin/env bashset -o errexit -o nounset -o pipefailcat <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: ${NAMESPACE}
labels:
team: ${TEAM}
EOF
Save it to namespace.yaml.sh and then apply it:
$ bash namespace.yaml.sh | kubectl apply -f -
namespace.yaml.sh: line 5: NAMESPACE: unbound variable
error: no objects passed to apply
Notice how it errors, telling you only your first mistake? That’s because it knows you’re internet addicted and were looking at your phone and forgot what you were doing and wouldn’t be able to fix more than one issue at a time anyway. It’s just helping you focus.
Config Files
If you don’t want to inline your environment variables or pollute your shell environment, you can feed in the values from a configuration file.
Just write your config file like so:
NAMESPACE=cool-namespace
TEAM=cool-team
Save it to namespace.env and then apply it:
env $(grep -v ‘^#’ namespace.env | xargs) bash namespace.yaml.sh | kubectl apply -f -
Tada!

Even a code janitor like yourself can master it.
Now, go forth, my brethren.
Bash all the things!