Salt State
Setting up your Minions
Installing Apache
We can set the state of an Minion inside a YAML file that contains instructions for Salt. Start by creating a folder /srv/salt
and run git init
to version your minion state. To make sure that our minion has Apache installed we will create an apache.sls
file inside the directory on your MASTER:
install_apache:
pkg.installed:
- name: apache2
You can now execute this set of instructions on your minion server by running:
sudo salt ubuntuAsus state.sls apache
ubuntuAsus:
----------
ID: install_apache
Function: pkg.installed
Name: apache2
Result: True
Comment: The following packages were installed/updated: apache2
Started: 15:15:20.619100
Duration: 28624.3 ms
Changes:
----------
apache2:
----------
new:
2.4.41-4ubuntu3
old:
apache2-bin:
----------
new:
2.4.41-4ubuntu3
old:
apache2-data:
Summary for ubuntuAsus
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 28.624 s
This step had our Minion download a copy of apache.sls
file from our master and run it's instructions. The master does not need to know how the minion is going to download and install the required software - the OS on our minion makes that decision. Since we have Ubuntu installed on our minion it will run apt-get update && apt-get install apache2
.
Configuring Apache
We can now also make sure that Apache will be enabled and activated as a service by adding the following line to our instruction file:
install_apache:
pkg.installed:
- name: apache2
enable_apache:
service.running:
- name: apache2
- enable: True
We can also configure Apache to display a landing page on Port 80:
install_apache:
pkg.installed:
- name: apache2
enable_apache:
service.running:
- name: apache2
- enable: True
add_landing_page:
file.managed:
- name: /var/www/html/index.html
- contents: |
<!doctype html>
<body><h1>Salty Dayze, Sailor!</h1></body>
Now re-run state.sls
and the default Apache Landing Page will be overwritten by our Hello World:
sudo salt ubuntuAsus state.sls apache test=true
sudo salt ubuntuAsus state.sls apache
You can visit the website on your minions IP address and port 80. You can get the IP address by running the following command:
sudo salt ubuntuAsus network.ip_addrs
ubuntuAsus:
- 10.1.88.0
- 172.17.0.1
- 192.168.2.111
curl 192.168.2.111
<!doctype html>
<body><h1>Salty Dayze, Sailor!</h1></body>
Jinja Scripts
Conditionals
The Apache install script in this form will only work for Debian-based operating system - the Apache package is called apache2
on Ubuntu but httpd
on CentOS. We can use the Jinja script syntax to make our script more robust:
install_apache:
pkg.installed:
{% if salt.grains.get('os_family') == 'Debian' %}
- name: apache2
{% elif salt.grains.get('os_family') == 'RedHat' %}
- name: httpd
{% endif %}
enable_apache:
service.running:
{% if salt.grains.get('os_family') == 'Debian' %}
- name: apache2
{% elif salt.grains.get('os_family') == 'RedHat' %}
- name: httpd
{% endif %}
- enable: True
add_landing_page:
file.managed:
- name: /var/www/html/index.html
- contents: |
<!doctype html>
<body><h1>Salty Dayze, Sailor!</h1></body>
We can test this script with the state.show_sls
command:
sudo salt '*' state.show_sls apache | less
ubuntuAsus:
----------
enable_apache:
----------
__env__:
base
__sls__:
apache
service:
|_
----------
name:
apache2
|_
----------
enable:
True
- running
|_
----------
order:
10001
We can see that our Ubuntu Minion correctly resolved the apache2
package and not httpd
.
Looping
To configure Apache we can now create configuration files in the conf-available
directory and copy them over into the conf-enabled
directory to start using them:
mod_status:
file.managed:
- name: /etc/apache2/conf-available/mod_status.conf
- contents: |
<Location '/status'>
SetHandler server-status
</Location>
cmd.run:
- name: a2enmod status && a2enconf mod_status
- creates: /etc/apache2/conf-enabled/mod_status.conf
mod_info:
file.managed:
- name: /etc/apache2/conf-available/mod_info.conf
- contents: |
<Location '/info'>
SetHandler server-info
</Location>
cmd.run:
- name: a2enmod info && a2enconf mod_info
- creates: /etc/apache2/conf-enabled/mod_info.conf
This configuration script can be compacted by writing a for-loop:
{% for conf in ['status', 'info'] %}
mod_{{ conf }}:
file.managed:
- name: /etc/apache2/conf-available/mod_{{ conf }}.conf
- contents: |
<Location '/{{ conf }}'>
SetHandler server-{{ conf }}
</Location>
{% if salt.grains.get('os_family') == 'Debian' %}
cmd.run:
- name: a2enmod {{ conf }} && a2enconf mod_{{ conf }}
- creates: /etc/apache2/conf-enabled/mod_{{ conf }}.conf
{% endif %}
{% endfor %}
The command step is only necessary on Debian systems and can be wrapped into a conditional. We can again test our script:
sudo salt '*' state.show_sls mods | less
ubuntuAsus:
----------
mod_info:
----------
__env__:
base
__sls__:
mods
...
Scripts CleanUP
Commenting your scripts and separating logic from state:
apache.sls
# Install vanilla Apache on Debian/RedHat
{% if salt.grains.get('os_family') == 'Debian' %}
{% set apache_pkg = 'apache2' %}
{% elif salt.grains.get('os_family') == 'RedHat' %}
{% set apache_pkg = 'httpd' %}
{% endif %}
install_apache:
pkg.installed:
- name: {{ apache_pkg }}
enable_apache:
service.running:
- name: {{ apache_pkg }}
# Will be enabled automatically on Debian but has to be enabled manually on RedHat
- enable: True
# Adding a blank front page
add_landing_page:
file.managed:
- name: /var/www/html/index.html
- contents: |
<!doctype html>
<body><h1>Salty Dayze, Sailor!</h1></body>
Working with Dictionaries
Create a lookup dictionary to assign the correct package and service name for each minion:
apache.sls
# Install vanilla Apache on Debian/RedHat
{% set lookup = {
'Debian': {
'pkg': 'apache2',
'srv': 'apache2'
},
'RedHat': {
'pkg': 'httpd',
'srv': 'httpd'
}
} %}
{% set apache = lookup[salt.grains.get('os_family')] %}
install_apache:
pkg.installed:
- name: {{ apache.pkg }}
enable_apache:
service.running:
- name: {{ apache.srv }}
# Will be enabled automatically on Debian but has to be enabled manually on RedHat
- enable: True
# Adding a blank front page
add_landing_page:
file.managed:
- name: /var/www/html/index.html
- contents: |
<!doctype html>
<body><h1>Salty Dayze, Sailor!</h1></body>
making this is a little bit more compact by using grains.filter_by
:
sudo salt '*' grains.filter_by '{Debian: apache2, RedHat: httpd}'
ubuntuAsus:
apache2
apache.sls
# Install vanilla Apache on Debian/RedHat
{% set apache = salt.grains.filter_by({
'Debian': {
'pkg': 'apache2',
'srv': 'apache2'
},
'RedHat': {
'pkg': 'httpd',
'srv': 'httpd'
}
}) %}
install_apache:
pkg.installed:
- name: {{ apache.pkg }}
enable_apache:
service.running:
- name: {{ apache.srv }}
# Will be enabled automatically on Debian but has to be enabled manually on RedHat
- enable: True
# Adding a blank front page
add_landing_page:
file.managed:
- name: /var/www/html/index.html
- contents: |
<!doctype html>
<body><h1>Salty Dayze, Sailor!</h1></body>
Again, you can test your script with sudo salt '*' state.show_sls apache | less
.
Splitting up our Files
We can now break up our configuration file so that every SLS file only does one thing - if possible. We will collect the resulting files inside a subdirectory /srv/salt/apache
:
welcome.sls
# Adding a blank front page
add_landing_page:
file.managed:
- name: /var/www/html/index.html
- contents: |
<!doctype html>
<body><h1>Salty Dayze, Sailor!</h1></body>
map.sls
# Get package/service name per OS version
{% set apache = salt.grains.filter_by({
'Debian': {
'pkg': 'apache2',
'srv': 'apache2'
},
'RedHat': {
'pkg': 'httpd',
'srv': 'httpd'
}
}) %}
init.sls
# Install vanilla Apache on Debian/RedHat
{% from 'apache/map.sls' import apache with context %}
install_apache:
pkg.installed:
- name: {{ apache.pkg }}
enable_apache:
service.running:
- name: {{ apache.srv }}
# Will be enabled automatically on Debian but has to be enabled manually on RedHat
- enable: True
mods.sls
{% for conf in ['status', 'info'] %}
mod_{{ conf }}:
file.managed:
- name: /etc/apache2/conf-available/mod_{{ conf }}.conf
- contents: |
<Location '/{{ conf }}'>
SetHandler server-{{ conf }}
</Location>
{% if salt.grains.get('os_family') == 'Debian' %}
cmd.run:
- name: a2enmod {{ conf }} && a2enconf mod_{{ conf }}
- creates: /etc/apache2/conf-enabled/mod_{{ conf }}.conf
{% endif %}
{% endfor %}
Again, you can test your script with sudo salt '*' state.show_sls apache | less
Using Custom Python Scripts
You can write your own Python modules and execute them with Salt. Start by creating a folder /srv/salt/_modules
and add your Python scripts - myUtils.py
:
def getDate():
return __salt__['cmd.run']('date')
sudo salt '*' saltutil.sync_modules //sync script with all minions
sudo salt '*' myUtils.getDate
ubuntuAsus:
Tue Aug 4 09:19:49 UTC 2020