States, Formulas & the SLS File Format
Move from imperative commands to declarative configuration management with Salt state files, requisites, and Jinja2 templating.
The SLS File Format
Salt state files use the .sls extension. They are YAML with optional Jinja2 templating:
nginx:
pkg.installed:
- name: nginx
service.running:
- name: nginx
- enable: True
- require:
- pkg: nginxThe state ID and the name argument don't have to match — the ID is just a label, name is what gets acted on.
The top.sls File
Maps which states apply to which minions:
base:
'*':
- common
'web-*':
- nginx
- php
'db-*':
- mysqlFile Structure
/srv/salt/
top.sls
common.sls
nginx.sls
php.sls
mysql/
init.sls <- referenced as 'mysql'
config.sls <- referenced as 'mysql.config'
replication.sls <- referenced as 'mysql.replication'Writing Your First Real State
common_packages:
pkg.installed:
- pkgs:
- vim
- curl
- wget
- htop
- git
- unzip
- ufw
ntp:
pkg.installed:
- name: chrony
service.running:
- name: chrony
- enable: True
- require:
- pkg: ntp
set_timezone:
timezone.system:
- name: UTC# Apply to all minions
sudo salt '*' state.apply common
# Or apply everything in top.sls
sudo salt '*' state.highstateCore State Modules
pkg — Package Management
install_apache:
pkg.installed:
- name: apache2
remove_telnet:
pkg.removed:
- name: telnetservice — Service Management
nginx_service:
service.running:
- name: nginx
- enable: True
- reload: Truefile — File and Directory Management
nginx_config:
file.managed:
- name: /etc/nginx/nginx.conf
- source: salt://nginx/files/nginx.conf
- user: root
- group: root
- mode: '0644'
web_root:
file.directory:
- name: /var/www/html
- user: www-data
- group: www-data
- mode: '0755'
- makedirs: Trueuser and group — Account Management
deploy_user:
user.present:
- name: deploy
- uid: 5000
- home: /home/deploy
- shell: /bin/bash
- groups:
- deploy
- www-datacmd — Running Commands (Use Sparingly)
run_migrations:
cmd.run:
- name: php artisan migrate --force
- cwd: /var/www/app
- unless: test -f /var/www/app/.migratedRequisites: Controlling Execution Order
# require: only runs if dependency succeeds
nginx_service:
service.running:
- name: nginx
- require:
- pkg: install_nginx
# watch: triggers when watched state changes
nginx_reload_on_config:
service.running:
- name: nginx
- watch:
- file: nginx_config
# onchanges: run only if the watched state made a change
run_bundle_install:
cmd.run:
- name: bundle install
- cwd: /var/www/app
- onchanges:
- file: gemfile_managedJinja2 Templating
{% set arch = grains['osarch'] %}
install_packages:
pkg.installed:
- pkgs:
- nginx
- curl
{% if arch == 'amd64' %}
- libssl3
{% endif %}
app_config:
file.managed:
- name: /etc/myapp/config.ini
- contents: |
hostname={{ grains['fqdn'] }}
ip={{ grains['ipv4'][0] }}
env=productionCustom Grains
Define custom grains in /etc/salt/grains on a minion:
roles:
- webserver
- loadbalancer
environment: production
datacenter: nyc1# Then target by custom grain
salt -G 'role:webserver' state.apply nginxThe Salt Fileserver
State files can reference files stored on the master using the salt:// URI scheme:
nginx_config:
file.managed:
- name: /etc/nginx/nginx.conf
- source: salt://nginx/files/nginx.confTesting States Before Applying
# Dry-run — shows what Salt would do without making changes
sudo salt 'web-01' state.apply nginx test=True
# Apply a single state
sudo salt 'web-01' state.apply nginx
# Apply multiple states
sudo salt 'web-01' state.apply nginx,php,common
# Show verbose output
sudo salt 'web-01' state.apply nginx -l debugCommunity Formulas
Salt has community-maintained formulas on GitHub at github.com/saltstack-formulas:
cd /srv
git clone https://github.com/saltstack-formulas/nginx-formula.gitAdd it to your master config's file_roots:
file_roots:
base:
- /srv/salt
- /srv/nginx-formulaWhat's Next
You now understand how to write, structure, and apply Salt states. In Part 3, we provision cloud VMs using Salt Cloud with OpenStack, so Salt can both create your servers and configure them in a single workflow.
