Skip to main content
RunBook Academy

AnsibleXXXIX · Automation Platforms, RBAC and Event-DrivenAutomation platforms, RBAC and event-driven automation

Projects, inventories, credentials, job templates

Advanced⏱ ~26 minansible-playbookansible-inventory

What you'll learn

  • Map each platform object to the command-line argument or artefact it replaces
  • Express a real production run as a fully specified, checked-in invocation
  • Explain why a job template is a reviewable artefact and a shell history entry is not
  • Identify which parts of an invocation must be fixed and which must stay variable

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

The platform object model looks like a lot of new vocabulary. It is not. Every object is something you already have, given a name and a database row.

Platform objectWhat it actually is
ProjectYour git checkout
InventoryThe -i argument
CredentialYour SSH key and your vault password file
Job templateA saved ansible-playbook command line
Execution environmentYour pinned virtualenv, as a container image
OrganizationThe boundary that owns all of the above
SurveyThe --extra-vars you would have typed, as a form
ScheduleA cron entry
Workflow templateA shell script that runs several of the above in order

The useful exercise is not memorising this table. It is the reverse direction: taking a run you perform today and writing down what each of those objects would contain for it. That exercise finds ambiguities in your current practice whether or not you ever deploy a platform.

The project: a commit, not a directory

A project is a git repository, a branch, and a rule about when the checkout updates.

The rule is the interesting field. A platform can update the checkout on every launch, on a schedule, or never until told. The equivalent question on a plain controller is the one nobody asks:

When we ran that playbook at 02:00, what commit was the working tree on, and were there uncommitted changes?

On a controller where somebody has been editing files in place, the honest answer is “nobody knows”. The playbook that ran is not the playbook in main, and the diff between them is unrecoverable.

The inventory: an object with a change history

A platform inventory is a stored host list — either typed in, or synchronised from a source such as a cloud provider or a git file. The things that make it an object rather than an argument are that it has a name, permissions, and a record of who changed it.

That last one matters more than it sounds. Consider the blast-radius question from Part IV: which hosts does production_web contain? On a plain controller you answer it by reading a file, which is fine. The question you cannot answer is which hosts it contained last Tuesday, unless the file is in git.

Read-only / Safethe inventory as an object you can inspect
$ ansible-inventory -i inventory.ini --graph
@all:
|--@ungrouped:
|--@production:
|  |--@webservers:
|  |  |--web01.example.com
|  |  |--web02.example.com
|  |  |--web03.example.com
|  |--@databases:
|  |  |--db01.example.com
|  |  |--db02.example.com

The credential: the object that is not a file

A credential is a named, encrypted secret with a type — machine (SSH), vault, cloud, registry — and a set of permissions. It is attached to a job template rather than referenced by path.

This is the object with the least faithful CLI equivalent, and lesson 4 is about why. For now, note the shape:

  • A machine credential replaces --user, --private-key, --ask-become-pass.
  • A vault credential replaces --vault-password-file.
  • Multiple credentials attach to one template, which is how a run gets an SSH key and a vault password and a cloud token without any of them appearing on a command line.

The job template: a saved command line

Here is the object that does the most work.

A job template names a project, a playbook within it, an inventory, credentials, and the run options: limit, tags, skip tags, verbosity, forks, job type (run or check), and extra variables.

That is an ansible-playbook invocation with the fields separated out. To see how directly, take a real run:

Service impact possiblethe invocation a job template stores
$ ansible-playbook \
-i inventory/production/hosts.ini \
playbooks/patch-webtier.yml \
--limit webservers \
--tags patch,restart \
--forks 5 \
--vault-password-file /etc/ansible/vault/production \
-e '{"reboot_allowed": false}'
Job template fieldThe argument above
Project + playbookplaybooks/patch-webtier.yml at a known commit
Inventory-i inventory/production/hosts.ini
Limit--limit webservers
Job tags / skip tags--tags patch,restart
Forks--forks 5
Credentials--vault-password-file, plus the SSH identity
Extra variables-e '{"reboot_allowed": false}'
Job typeabsent here, so a real run rather than --check

Nothing is added. What changes is that this string has a name, an owner, a review history, and a permission on it — instead of living in one person’s shell history and one paragraph of a runbook.

Doing this without a platform

The exercise is to turn each real production run into a checked-in, reviewable artefact. A small wrapper is enough:

#!/usr/bin/env bash
# jobs/patch-webtier.sh - the "monthly web tier patch" job template.
# Reviewed change: CHG-12345. Owner: platform team.
set -euo pipefail

cd /srv/ansible
git status --porcelain | grep -q . && { echo 'working tree dirty; refusing'; exit 1; }
echo "revision: $(git rev-parse HEAD)"

exec ansible-playbook \
  -i inventory/production/hosts.ini \
  playbooks/patch-webtier.yml \
  --limit "${LIMIT:-webservers}" \
  --tags patch,restart \
  --forks 5 \
  --vault-password-file /etc/ansible/vault/production \
  -e '{"reboot_allowed": false}' \
  "$@"

Three properties this has that a shell-history invocation does not: it is in code review, it fails on a dirty tree, and it prints the revision it ran. Those are three of the things a job template gives you, and the file costs fifteen minutes.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A team stores each production run as a wiki sentence: "run the patching playbook against the web servers". What is the concrete operational cost of this compared to a checked-in wrapper script or a job template?

  2. Q2. Which properties does a small checked-in wrapper script give you that a shell-history invocation does not? Select all that apply.

  3. Q3. A platform separates the project from the job template so that one git repository at one commit can back many differently-parameterised runs, each with its own permissions.

  4. Q4. A vault credential shared by six job templates is rotated. What failure should you expect and why is it harder to diagnose than a rotated SSH key?

Passing score: 75%. Answers are checked in this browser.