Skip to content

writing saltstack formulas

SaltStack is a great open-source, cross-platform automation system. It lets you configure servers using declarative yaml files and python. You can create custom “states” during yaml/python, and then say “make server X have state Y”. There’s a lot of plumbing involved which I’m not going to delve into, but it’s pretty neat stuff and doesn’t require punching holes in firewalls.

SaltStack provides a lot of different mechanisms to re-use configuration, and recently I’ve been working a lot with salt formulas as a mechanism to create and re-use custom states.

From the docs:

Formulas are pre-written Salt States. They are as open-ended as Salt States themselves and can be used for tasks such as installing a package, configuring, and starting a service, setting up users or permissions, and many other common tasks.

These are implemented as a git repository with one formula in it, and then there are a variety of ways to reference a formula. It’s common to customize the configuration using salt’s pillar mechanism (yaml again)

Some thoughts on writing formulas:

  • the pillar.example file should show an in-depth example of what pillar customizations are available
  • this community has settled on restructured text over markdown
  • map.jinja files are used to set defaults and switch options based on OS
    • these defaults should match what’s in pillar.example, which is annoying to maintain by hand
  • there are a million formulas on github, but the nature of salt states lends to writing really opinionated states that are super-specific to one’s infrastructure. It’s really hard to make a formula generic enough or sufficiently customizable, unless you’re doing something so trivial that it’s likely not worth a formula. There’s just too many choices that need to be made. For example, a formula to install cacti has a ton of choices:
    • install from source, or from a package manager?
    • apache or nginx?
    • any cacti plugins?
    • which poller, spine or cmd.php?
    • install the non-free snmp MIBs, or stick with the free (as-in-everything) option?
  • if you find a repo on github, you should fork it and reference your fork, even if it’s perfect the way it is. If you reference someone else’s formula directly, then you might have problems if they make breaking changes, or security issues if someone slips a backdoor into the formula. Each formula is third-party code running (usually as root!) on your systems, so you need to be careful to read and understand every formula
  • I found myself forking an existing formula that’s close to what I want, and then thoroughly gutting it until it’s customized for my specific infrastructure. This is due to insufficient configuration options, not sharing my opinions on how to set something up, or being too specific to the creator’s infrastructure
  • after writing a formula, it seems like it should be subdivided into many smaller formulas so they each do one thing, but managing dependencies between formulas is an open issue. If you split it up in a way that feels right, your install instructions get way longer. Instead of “fork and install my formula and salt-call state.sls my-formula“, it’s “fork and install these N formulas, then salt-call
  • if you start a formula from scratch, fork the template-formula
  • jinja has some powerful abstraction mechanisms via macros and imports. It’s tempting to write a custom salt module in python, but a lot can be done just right in jinja. This is a double-edged sword
  • there are a ton of helpful examples in the official saltstack-formulas

Happy formulating!