Skip to main content
RunBook Academy

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

RBAC as a blast-radius control

Advanced⏱ ~28 minansible-playbook

What you'll learn

  • Separate the four permissions a run depends on and name who should hold each
  • Read a role assignment as a statement about blast radius rather than about trust
  • Build the equivalent separation on a controller with no platform
  • Recognise the permission designs that look restrictive and grant everything

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.

This course has spent thirty-eight parts on one idea: know which hosts you are about to change, and make it hard to change more of them than you meant to.

Every mechanism so far has been technical. --limit. A precise hosts: line. A guardrail that asserts on the environment. A canary before the fleet.

RBAC is the same idea applied to people. And the failure it prevents has exactly the same shape:

A playbook with no --limit targets every host it can reach.

An operator with no permission boundary targets every host they can reach.

“Everyone can run everything against production” is not a governance problem that happens to involve Ansible. It is a blast-radius defect, identical in structure to the YAML one, and it should offend you in the same way.

The four permissions

A run depends on four separable things. Most estates collapse them into one, and the collapse is the defect.

QuestionWhat it controlsHeld by
Who may edit the automation?What the run doesThe people who write and review the code
Who may run it?Whether it happens, and whenThe people responsible for the service
Against which inventory?How many hosts, and whichDeliberately narrower than the above
With which credentials?What privilege it holds on those hostsNarrower still

Read them as a sentence: this person may run this automation against these hosts with this privilege. Change any one of the four and you have a different blast radius.

The reason to separate edit from run is the one that convinces people: they are held by different populations for different reasons.

An application team knows when their service can be restarted. They should be able to run the restart. They should not be able to change what “restart” means for the whole fleet, because that change affects services they do not own and cannot test.

Conversely, the platform engineer who wrote the role may have no business initiating a restart in a payments environment on a Friday afternoon. Their expertise is the code. It is not the change window.

What the roles actually are

The AWX and automation controller role vocabulary is small. The system level has two:

  • System Administrator — manages all aspects of the system.
  • System Auditor — views all aspects of the system.

At the object level, the roles that carry the weight:

RoleGrants
AdminFull management of the object — organizations, teams, inventories, projects, job templates
ExecuteRuns an assigned job template. Not edits it
ReadViews the object
UseUses the object in a job template — credentials, inventories, projects, instance groups
UpdateUpdates a project from source control, or an inventory from its source
Ad HocRuns ad hoc commands against an inventory
MemberMembership in an organization or team

Plus organization-scoped administrative roles — Project Admin, Inventory Admin, Credential Admin, Notification Admin, Workflow Admin — each granting create, read, update and delete for one resource type within one organization.

Two of these deserve attention because they are the mechanism the whole design rests on.

Execute is the separation of edit from run. Execute on a job template lets somebody launch it and see the result. It does not let them change the playbook, the inventory, the limit, or the credentials. They can push the button; they cannot change what the button does.

Use is the separation of authenticate from read. Use on a credential lets somebody attach it to a job template without seeing its value. That is a real security property and it gets lesson 4 to itself.

Designing the matrix

The mechanical part is easy once the four questions are separated. Take a concrete estate: three environments, four teams.

TeamDev inventoryStaging inventoryProduction inventory
Platform engineeringAdmin on templates and projectAdminExecute on templates; no edit in the production organization
Application team AExecute on team A templatesExecute on team A templatesExecute on team A templates, limited to the app_a inventory
Database teamExecuteExecuteExecute on database templates only, with the database credential
Support / on-callReadReadExecute on a named set of remediation templates; Read on everything else

Three properties of this matrix are worth naming, because they are what distinguish a design from a list.

No cell grants edit in production. The production organization has no Admin except a break-glass account. Changing production automation means changing it in git, having it reviewed, and letting the project checkout pick it up. That is the point of separating the project from the template.

Application teams are bounded by inventory, not by intention. Team A cannot target databases because the inventory attached to their templates does not contain any. There is nothing to type that reaches a database host. Compare this with a runbook sentence saying “team A should not touch databases”, which is a hope.

On-call has Execute on a named set. Not on everything, and not on nothing. The named set is the remediation templates that have been reviewed for exactly this: safe to run at 04:00 by somebody who did not write them.

The same separation with no platform

Every property above has a version available on a plain controller. It is weaker in one specific way, discussed below, but it is not nothing — and the design work is identical, which is the reason to do this exercise regardless.

Separate inventories per environment and per team. This is the foundation and it is free. The blast radius of a run is the inventory it loaded; a team whose runs load only inventory/app_a/ cannot reach a database.

Separate the credentials. One SSH key per environment, one vault identity per environment, and the production key readable only by the production automation account. Part XXI covered the vault side; Part XLVII covers key custody in depth.

Use sudo to grant the invocation rather than the shell. This is the closest CLI analogue to Execute, and the one most teams miss:

# /etc/sudoers.d/ansible-jobs
%app-a ALL=(ansible-app-a) NOPASSWD: /srv/ansible/jobs/restart-app-a.sh

The app-a group may run one wrapper script as the ansible-app-a account. They may not read that account’s SSH key, edit the script, or run anything else as that account. That is Execute-without-edit, built out of sudo.

Separate the controllers where the separation must be real. Two hosts, two automation accounts, two sets of keys: one that can reach production, one that cannot. This is the only version of the separation that survives a compromised operator account, and for high-value environments it is worth the second VM.

Knowledge check

Knowledge check · 4 questions

  1. Q1. An estate gives every operator Execute rather than Admin on job templates, and attaches every production template to a single all_production inventory with a fleet-wide root credential. How should you assess this design?

  2. Q2. A sudoers rule lets the app-a group run /srv/ansible/jobs/restart-app-a.sh as the ansible-app-a account. Which conditions would turn this narrow grant into the ability to run anything as that account? Select all that apply.

  3. Q3. On a shared controller with no platform, restricting the vault password file to mode 0600 means an operator who can run the playbook still cannot read the secret.

  4. Q4. Your break-glass automation account has not been used in two years. What is the most likely explanation?

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