Salt Vagrant - automatically provision one master and two slaves
Configuration management let's you control hundreds of computers. You can even configure machines that don't exist yet!
In this article, you'll use ready-made network of three virtual computers to play with Salt, a popular configuration management system.
These notes have not been extensively tested yet.
Background & Prerequisites
I cleaned up this configuration from a setup I've used for a long time on Linux. You can probably adapt it to Windows, and I think some of my students have.
These notes are pretty dense. As prerequisite, you should know Linux command line and directory structure. Here, we prepare to administer a lot of computers. It would help if you could already administer one.
Install Virtualization Environment
$ sudo apt-get update
$ sudo apt-get -y install virtualbox vagrant micro
$ mkdir saltdemo; cd saltdemo
$ micro Vagrantfile
Copy-paste the ready-made Vagrantfile in place.
Ready made Vagrantfile for three computers
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Copyright 2014-2023 Tero Karvinen http://TeroKarvinen.com
$minion = <<MINION
sudo apt-get update
sudo apt-get -qy install salt-minion
echo "master: 192.168.12.3">/etc/salt/minion
sudo service salt-minion restart
echo "See also: https://terokarvinen.com/2023/salt-vagrant/"
MINION
$master = <<MASTER
sudo apt-get update
sudo apt-get -qy install salt-master
echo "See also: https://terokarvinen.com/2023/salt-vagrant/"
MASTER
Vagrant.configure("2") do |config|
config.vm.box = "debian/bullseye64"
config.vm.define "t001" do |t001|
t001.vm.provision :shell, inline: $minion
t001.vm.network "private_network", ip: "192.168.12.100"
t001.vm.hostname = "t001"
end
config.vm.define "t002" do |t002|
t002.vm.provision :shell, inline: $minion
t002.vm.network "private_network", ip: "192.168.12.102"
t002.vm.hostname = "t002"
end
config.vm.define "tmaster", primary: true do |tmaster|
tmaster.vm.provision :shell, inline: $master
tmaster.vm.network "private_network", ip: "192.168.12.3"
tmaster.vm.hostname = "tmaster"
end
end
Run Three Computers
$ vagrant up
It takes 3-5 minutes, and the three computers boot up. The slaves t001 and t002 will automatically contact master, because the slave daemon (salt-minion) was installed and configured with a three-line script in Vagrantfile.
Accept the Slaves
Log into your master computer.
$ vagrant ssh tmaster
The salt commands below are run inside the virtual machine tmaster. It's prompt is very long, so I've replaced it with "$". But it's a different computer from your host OS.
The slave computers have already sent their keys to master. Just approve them
$ sudo salt-key -A
The following keys are going to be accepted:
Unaccepted Keys:
t001
t002
Proceed? [n/Y] y
Key for minion t001 accepted.
Key for minion t002 accepted.
And test that the connection works. This command contacts slaves using the secured channel created by Salt.
$ sudo salt '*' test.ping
t001:
True
t002:
True
Command Slaves
You can run regular shell commands
$ sudo salt '*' cmd.run 'hostname -I'
t001:
10.0.2.15 192.168.12.100
t002:
10.0.2.15 192.168.12.102
Collect Information
$ sudo salt '*' grains.items
$ sudo salt '*' grains.item osfinger ipv4
t002:
----------
ipv4:
- 10.0.2.15
- 127.0.0.1
- 192.168.12.102
osfinger:
Debian-11
t001:
----------
ipv4:
- 10.0.2.15
- 127.0.0.1
- 192.168.12.100
osfinger:
Debian-11
The Goal - Idempotent
Idempotent commands are much more powerful. They just describe the end state. Salt will only make changes if needed. These idempotent commands are the ones we'll mostly use later.
$ sudo salt '*' state.single file.managed '/tmp/see-you-at-terokarvinen-com'
...
Succeeded: 1 (changed=1)
Failed: 0
If you run it many times, you'll see that it's really idempotent. In the following rounds, the "(changed=1)" will disappear.
To make success messages shorter, you can use --state-output=terse
$ sudo salt --state-output=terse '*' state.single file.managed '/tmp/see-you-at-terokarvinen-com'
Install some software
$ sudo salt '*' state.single pkg.installed apache2
Make sure a daemon is running
$ sudo salt '*' state.single service.running apache2
Let's test that it's actually running
$ sudo apt-get -y install curl
$ curl -s 192.168.12.102|grep title
<title>Apache2 Debian Default Page: It works</title>
It works! So let's shut it down
$ sudo salt '*' state.single service.dead apache2
$ curl 192.168.12.102
curl: (7) Failed to connect to 192.168.12.102 port 80: Connection refused
We can control users
$ sudo salt '*' state.single user.present terote01
Modify users
$ sudo salt '*' state.single user.present terote01 shell="/bin/bash"
And make sure they don't exist
$ sudo salt '*' state.single user.absent terote01
If none of the others work, we can use cmd.run state. But we must make it idempotent ourselves.
$ sudo salt '*' state.single cmd.run 'touch /tmp/tero' creates="/tmp/tero"
You should prefer these to cmd.run: package, file, service, user.
Infra as Code - Your wishes as a text file
$ sudo mkdir -p /srv/salt/hello
$ sudoedit /srv/salt/hello/init.sls
Write these contents to init.sls. Note that this syntax is YAML. Indentation matters, and its two spaces. No tabs.
$ cat /srv/salt/hello/init.sls
/tmp/infra-as-code:
file.managed
$ sudo salt '*' state.apply hello
top.sls - What Slave Runs What States
Top file defines what states are run for which slaves.
$ sudo salt '*' state.apply hello^C
$ sudoedit /srv/salt/top.sls
$ cat /srv/salt/top.sls
base:
'*':
- hello
Now you don't have to name any modules on state.apply:
$ sudo salt '*' state.apply
Bye bye
We have cattle not pets. When it's time to say goodbye to this installation, you can destroy the machines and their contents. There is no undo.
$ exit
$ vagrant destroy # destroys all files in all three virtual computers