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
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
$ 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
$ 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
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