Paternoster, named after the lift, enables me to quite easily create a “program” which uses the Ansible API to actually invoke and run a playbook, mostly hiding this fact from the user. I’ve known of Paternoster since at least March 2018 when I PRed one of my infamous one-letter fixes and have since wanted to blog about it. Here goes.
$ pip install paternoster ansible
The executable playbook I create has a hash bang which points to Paternoster. It can be specified in a sudoers
file, say, to give it specific privileges. Other than that, the playbook in my “program” contains two plays: the first defines Paternoster parameters which become program options, the second and subsequent are the typical Ansible plays which Paternoster invokes with variables.
The host paternoster
doesn’t actually exist in the inventory – it’s a magic word which triggers Paternoster to set up its parameters.
#!/usr/bin/env paternoster
- hosts: paternoster
vars:
description: Create a user
parameters:
- name: uname
help: Username to create
prompt: true
type: paternoster.types.restricted_str
type_params:
regex: "^[A-Z]+[0-9]$"
minlen: 2
maxlen: 5
- hosts: localhost
connection: local
tasks:
- name: Create user
debug:
msg: "Will create user "
Parameters are represented by a dictionary which I found quite self-explanatory. They are passed to Ansible prefixed with param_
, so my uname
parameter becomes the variable param_uname
in Ansible. Standard Python types can be used for describing variables (e.g. int
), but Paternoster provides a few others in order to enforce, say, string validation.
For example, paternoster.types.restricted_str
can be validated with a regex or have certain character classes, a minimum and maximum lengths. The domain
type is checked for a DNS domain, the url
type for a URL, etc.
Parameters can also be prompted for, and as with Ansible’s vars_prompt
, if the parameter’s value is passed on the CLI as an option Paternoster doesn’t prompt for it but does validate the value according to my specification.
Assuming the above script is called mkuser
, the following could occur:
$ mkuser -h
usage: mkuser [-h] [--uname UNAME] [-v]
Create a user
optional arguments:
-h, --help show this help message and exit
--uname UNAME Username to create
-v, --verbose run with a lot of debugging output
$ mkuser
Uname: jane8
usage: mkuser [-h] [--uname UNAME] [-v]
mkuser: error: argument --uname: invalid string value: 'jane8'
$ mkuser --uname JANE8
Will create user JANE8
The first invocation shows help with the descriptions I specify in the first play. The second shows how I’m prompted for a variable and how validation (regex, lengths) fails, and the third then finally works because it’s upper-case only, the right length and ends in a digit.
Paternoster’s check_user
verifies that the user running the script is the one specified, which allows me to have programs only a particular user can execute, and if I set become_user
, Paternoster uses sudo(1)
to execute the playbook as the specified user. If set, success_msg
is printed on successful exit as a confirmation to the user, untemplated.
What I like about Paternoster is how it basically “hides” that an Ansible playbook is being invoked, and that it wraps that into a utility which I can use on the command line as any other, options and all. And being Ansible means I can run it here have something actually done on remote systems. Users who wish to see what’s happening, can use -v
on the program to show the typical playbook output.
Paternoster was created by Michael “luto” Lutonsky to supplant a bunch of shell scripts at Uberspace, the hoster which also hosts this site. (I could rave about them but the simple fact that I’ve never complained about them speaks volumes!) Uberspace sponsored the development of the open code, and one day I will rave about them.