An Advanced Guide to Salt
Introduction
In this post we will be talking about more advanced scenarios using Salt, specifically how to write your own execution and state modules.
Like we saw before, we configure all of the salt subsystem in files within /srv/salt/.
The location for our custom execution and state modules is in the same place we place our SLS files, however, within two specific directories: _modules and _states, for execution and state modules, respectively.
Execution Module
Let us take the example of creating a custom execution module that can manage users in a system, in a way that we can:
- see which home directories have no users associated with
- disable a user from login
- enable a user to login
- force a user to change its password
Let us call the module myuser, so it does not clash with any existing module name. For info on all of salt's existing modules check this list.
First we create the module file in the proper location:
root@master:~# tree /srv/salt
/srv/salt
└── states
└── base
│ ...
├── _modules
│ └── myuser.py
...
Now we start writing our module. The first thing we should do, after imports, is tell the salt system the proper module name to use:
|
|
Now we can implement our first function, let us call it get_orphan_home_dirs:
|
|
Before we can test this execution module we need to update the cache on the minion:
root@master:~$ salt 'minion-1' saltutil.sync_modules
minion-1:
----------
- modules.myuser
root@master:~$
Now we can test it on the salt-master:
root@master:~$ salt 'minion-1' myuser.get_orphan_home_dirs
minion-1:
----------
/home/does-not-exist:
User does-not-exist Not Found!
root@master:~$
And everything looks good, so we can move on to the next functions, disable/enable user accounts:
|
|
All of the Salt execution modules are available to each other and modules can call functions available in other execution modules. [1]
The variable __salt__ is packed into the modules after they are loaded into the Salt minion. [1]
The __salt__ variable is a Python dictionary containing all of the Salt functions. Dictionary keys are strings representing the names of the modules and the values are the functions themselves. [1]
Here we make use of the shadow module, you can see details about it here
Let us test these with the user user temp:
root@master:~# salt 'minion-1' myuser.disable_login temp
minion-1:
----------
changes:
----------
disable_login:
----------
new:
temp@minion-1
old:
comment:
None
result:
True
Check that it is disabled:
temp@local:~$ ssh minion-1
Your account has expired; please contact your system administrator
Connection to minion-1 closed by remote host.
Connection to minion-1 closed.
Now we enable it back.
root@master:~# salt 'minion-1' myuser.enable_login temp
minion-1:
----------
changes:
----------
enable_login:
----------
new:
temp@minion-1
old:
comment:
None
result:
True
And check that it works.
temp@local:~$ ssh minion-1
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-131-generic x86_64)
temp@minion-1:~$
Nice, now we just have one task left, make a function that forces a user to change its password:
|
|
Let us test it, then.
root@master:~# salt 'minion-1' myuser.expire_passwd temp
minion-1:
----------
changes:
----------
expire_passwd:
----------
new:
0
old:
-1
comment:
None
result:
True
If we did everything right, this will trigger a password change on the user's next successful login. And the user is forced to change to a different password.
temp@local:~$ ssh minion-1
You are required to change your password immediately (root enforced)
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for temp.
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:
Password unchanged
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
temp@minion-1:~$ logout
Connection to minion-1 closed.
Looks good =)
State Module
Like we saw in the previous post, to apply a state we use the state execution module, and its function apply. One key difference between using execution modules and state modules is the possibility of using the keyword test that, when True, will not effectively apply the state but rather it will show what it would do and/or which changes it would apply. We will see how to define this behaviour.
Just like for the execution module we should first create the state file in the proper location.
root@master:~# tree /srv/salt
/srv/salt
└── states
└── base
│ ...
├── _states
│ └── myuser.py
...
And we can start writing our state module. Like the execution module let us call it myuser. As the get_orphan_home_dirs function is informational only, we will be implementing the other three disable_login, enable_login and expire_passwd.
|
|
And now we can use these in SLS files, i.e:
|
|
Before testing our newly created state module we have to update the minion's cache.
root@master:~$ salt minion-1 saltutil.sync_states
minion-1:
----------
- states.myuser
root@master:~$
Now let us test it.
root@master:~$ salt minion-1 state.apply users test=True
minion-1:
----------
ID: add user alice and ensure the login is enabled
Function: user.present
Name: alice
Result: None
Comment: User alice set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: add user bob and ensure the login is enabled
Function: user.present
Name: bob
Result: None
Comment: User bob set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: add user alice and ensure the login is enabled
Function: myuser.enable_login
Name: alice
Result: None
Comment: Would enable login for user alice
Started: 14:23:48.895331
Duration: 0.473 ms
Changes:
----------
ID: add user bob and ensure the login is enabled
Function: myuser.enable_login
Name: bob
Result: None
Comment: Would enable login for user bob
Started: 14:23:48.895431
Duration: 0.473 ms
Changes:
----------
ID: force user alice to change its password
Function: myuser.expire_passwd
Name: alice
Result: None
Comment: Would force password change for user alice
Started: 14:23:48.895531
Duration: 0.473 ms
Changes:
----------
ID: add user charlie and ensure the login is enabled
Function: user.present
Name: charlie
Result: None
Comment: User charlie set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: add user lucy and ensure the login is enabled
Function: user.present
Name: lucy
Result: None
Comment: User lucy set to be added
Started: 14:23:49.860660
Duration: 0.967 ms
Changes:
----------
ID: force user bob to change its password
Function: myuser.expire_passwd
Name: bob
Result: None
Comment: Would force password change for user bob
Started: 14:23:48.896661
Duration: 0.473 ms
Changes:
----------
ID: add user charlie and disable its login
Function: myuser.disable_login
Name: charlie
Result: None
Comment: Would force password change for user charlie
Started: 14:23:49.896761
Duration: 0.473 ms
Changes:
----------
ID: add user lucy and disable its login
Function: myuser.disable_login
Name: lucy
Result: None
Comment: Would force password change for user lucy
Started: 14:23:49.896771
Duration: 0.473 ms
Changes:
----------
Summary for minion-1
--------------
Succeeded: 10 (unchanged=10)
Failed: 0
--------------
Total states run: 10
Total run time: 9.325 ms
The test looks good so now we can apply it.
root@master:~$ salt minion-1 state.apply users
minion-1:
----------
.....
.....
Summary for minion-1
--------------
Succeeded: 10 (changed=10)
Failed: 0
--------------
Total states run: 10
Total run time: 9.325 ms
root@master:~$
Looks good =)
Now we have 4 new users added to minion-1, alice and bob set with a forced password change, and charlie and lucy which exist but cannot login.
Conclusion
In this post we saw how to write both an execution module and a state module for the Saltstack Salt configuration management tool. We also saw how to test these modules and use them in a SLS file.
The files described in this post are available here:
Feel free to leave comments below, any improvement to this and other articles is always welcome.
Thanks for reading!
References
[1] | (1, 2, 3) https://docs.saltstack.com/en/latest/ref/modules/index.html |
Tags: linux python devops saltstack configuration management
Related Content
- A Comprehensive Introduction to Salt
- Slurm in Ubuntu Clusters - Part 2
- Slurm in Ubuntu Clusters - Part 1
- Using Python Virtual Environments with Slurm
- UEFI via software RAID with mdadm in Ubuntu 16.04