Saltstack statefiles

From wikinotes

States are where you define a set of instructions to apply to a machine.



For a state to be callable, it must exist on your salt-master configuration's file_roots. States are written in YAML.

Statefiles are structured similarly to the pillar, but work a slightly differently. Unlike the pillar, all hosts have access to all of it’s saltmaster’s statefiles. You can manually run any state file using the command state.apply (ex: sudo salt-call state.apply progs.editors.vim).

Adding a state-file to states/top.sls makes that state a part of that hostname’s highstate. A system’s highstate is the default configuration for that system. You can apply a system’s highstate (installing/updating all programs, and configurations) using the command: sudo salt-call state.highstate .

Finally, inspired by python’s package-structure, statefiles called init.sls located within a directory are referred to as the directory’s name. This is useful if you want to bundle extra configuration files alongside it’s statefile.

states/progs/mystate.sls         #  both considered equivalent, and called using:
states/progs/mystate/init.sls    #     state.apply  progs.mystate

name description location
state-files .sls files under the states subdirectory are YAML files that let you define variables, and their values. {project}/states/*.sls
states/top.sls A special sls file that contains hostnames, and under each the path to a state-file. Adding a state-file to a hostname makes it a part of that hostname's highstate. Unlike the pillar, all statefiles are accessible to all machines using state.apply. {project}/pillar/top.sls


    'dev-* or beta-*':
        - match: compound
        - progs.editors.vim

        - match: grain_pcre
        - progs.editors.vim

    '* not os_family(MacOS)':
        - match: grain_pcre

    # regex match against minion
        - match: pcre
        - hosts.{{grains['id']}}

statefile format

Every salt statefile is written using the following pattern:


.description of task:
        - param1:
        - param2:
        # ...

.description of another task:
        - param1: ...

including states

States can include other states (calling them if they haven't been called yet):


    - progs.vim
    - progs.git

templating, and dynamic logic

States can also use the jinja2 templating engine to perform simple logic like loops.


{% set personal_users = ['will', 'alex'] %}
{% for user in personal_users %}
        - source: salt://progs/shell/bash/files/bashrc
        - user:   {{user}}
        - group:  {{user}}
        - mode:   644
{% endfor %}

Salt exposes the pillar, and salt-modules this way.


# pillar
{% set password = pillar['passwd']['will']['unix'] %}

# grains
{% set fam = grains['os_family'] %}

# salt.modules.*
{% if not salt.file.file_exists( '/home/will' ) %}
# ... do something ...
{% endif %}

universal state parameters (requisites)

Some state arguments can be applied to ALL states. Here is a list of the params I use the most frequently:


.your state description:
      - name:      # usually the filepath, package-name, or line-to-be-run on commandline
      - unless:    # if this shell command succeeds, do not run the state
      - onlyif:    # if this shell command succeeds, run state, otherwise do not
      - watch:
        - file: /etc/nginx/nginx.conf  # run when this file changes

See Also:

applying states

States are applied either as a part of the system-highstate, or individually.

salt-call state.highstate                  # apply all states in highstate
salt-call state.apply    # apply only {file_roots}/progs/shell/bash/init.sls


Here are some patterns that I find myself using quite a lot

iterate homedirs


I have also encountered the use of '%h' which expands to a user's homedirectory. I need to look into the semantics of how this works, and what other types of paths this might work for.

{% set home_root = pillar['system']['home_root'] %}   # 'C:/Users', '/home', '/Users'

{% for user in salt.user.list_users() %}
    {% for homedir in [ home_root + user ] %}
        {% if salt.file.directory_exists( homedir ) %}

.your description:
      -name: blah

        {% endif %}
    {% endfor %}
{% endfor %}