Ansible playbooks
Gardience runs existing Ansible playbooks over the Salt connection it already has to every node. You do not need WinRM on Windows nodes, SSH keys for Linux nodes, or an inventory file.
Start here: Automation > Ansible
Automation > Ansible is the home for everything Ansible, in three steps:
- Bring your playbooks. Connect a Git repository, start a local project, or import from AWX or Ansible Automation Platform. Each opens the right part of Projects.
- Choose a playbook and its machines. Pick a playbook and the group it runs on, then Make it runnable. Each variable the playbook declares becomes a field on its run form. Passwords it prompts for come from the secrets vault by name.
- Run, schedule, or require approval. Under Your playbooks, Run… opens the form (with Check mode to preview the changes), Schedule… runs it on a schedule, and Workflows and approvals adds a sign-off step before it runs.
Each playbook shows when it last ran, its result, and who ran it. Recent Ansible runs shows live logs. Coming from AWX or Ansible Automation Platform? The table at the bottom of the page shows where each AWX feature lives in Gardience.
People who are not admins can run playbooks there. An admin makes them runnable.
There are two ways to run a playbook:
| Mode | Runbook | Nodes | Where ansible-playbook runs |
|---|---|---|---|
| Controller mode | Run an Ansible playbook (Windows and Linux, no WinRM) | Windows and Linux | On the Gardience server, in the separate playbook runner |
| Local mode | Run an Ansible playbook (Linux) | Linux only | On each node, against itself |
Uploads from the gdn command line use controller mode.
Controller mode
How it works
- Gardience builds the inventory from the target nodes. Windows nodes use PowerShell. Linux nodes use their own Python.
- The playbook runs in the playbook runner, a separate container that holds no Gardience data or secrets. Each run gets its own unprivileged account and private folder, and anything the run leaves behind is removed when it ends.
- Each command a task sends to a node goes as a Salt job to that node only. The command and its result are encrypted with that node's own key, so Salt's job history only ever holds encrypted data.
- The run can reach only the nodes it was approved for, and only for the length of the run (at most 4 hours).
One step of a playbook may run for up to an hour, for example a large Windows update. To change that for one playbook, set the variable gardience_salt_timeout (in seconds). If a command's message to a node or the node's answer is lost on the way, Gardience notices within about a minute and sends the command again. The node never runs the same command twice: it returns the saved result instead.
If a command gets no answer, Gardience sends it again. Each command carries an ID, so a node that already ran it returns the saved result instead of running it twice.
Who can run it
Running a playbook means running commands on the nodes. The person behind the run needs command-execution rights (cmd.run_all) on every target node, or the run is refused.
Controller runs go through the same checks as any runbook:
- Change windows, offline nodes and locks apply. See Change windows and locks.
- A locked node refuses every command, even in a run that has already started.
- If the audit log cannot be written, commands stop.
Run a playbook
- Open Automation > Runbooks.
- On Run an Ansible playbook (Windows and Linux, no WinRM), choose Run….
Fill in the form:
- Node group: the nodes to run on.
- Project: the
demoproject that ships with the server, or one of your Git projects. - Playbook (path in the project): for example
site.ymlorplaybooks/web.yml. - Limit to machines: optional. Machine names or wildcards separated by commas, with
!to leave one out, for exampleweb-*,!web-03. The limit only narrows the node group, and approvals, locks and the audit log see the narrowed list. - Only these tags: optional. Ansible tags separated by commas, like
--tags. - Extra variables: optional.
name=valuepairs separated by spaces, like-e. Names starting withansible_orgardience_are refused, so a run can never leave the Salt connection. - Vault secrets to pass: secret names, separated by commas. Leave empty for none.
- Check mode (preview only):
Trueruns Ansible with--check --diff, which shows what would change without changing it.
- Choose Run now as your name.
The run log shows each node's result with Ansible's own ok, changed and failed counts, followed by a PLAY RECAP. If ansible-playbook cannot start (for example, because of a YAML error), the log shows its error message.
Git projects
Keep your playbooks in Git, the way you did with AWX or Ansible Automation Platform, and run them from Gardience.
Add a project
Choose how Gardience reaches the repository:
- https with a token: store an access token in the Secrets vault first, either the token alone or
user:token. GitHub, GitLab, Bitbucket, Azure DevOps and Gitea tokens all work. A public repository needs no token. - SSH with a deploy key: use an SSH address such as
git@github.com:acme/infra.gitorssh://git@git.example.com:2222/srv/git/infra.git. Gardience creates a key pair for the project and shows its public key on the Projects page. Add it to the repository as a read-only deploy key (GitHub: Settings, Deploy keys; GitLab: Settings, Repository, Deploy keys; Bitbucket: Repository settings, Access keys), then choose Sync now. The private key is sealed with the server's vault key and never shown.
- https with a token: store an access token in the Secrets vault first, either the token alone or
- Open Automation > Projects.
Under Add a Git project, fill in:
- Name: lowercase letters, digits,
-and_, up to 40 characters. - Clone address: the https address, with no user name or token in it.
- Branch or tag:
mainunless you say otherwise. - Token from the vault: the secret name from step 1. Leave empty for a public repository.
- Pin to commit: optional. A full 40-character commit id. The project then stays on exactly that commit until you change or clear it.
- Sync on a timer: every hour, every 6 hours, or every day. Off by default (sync by hand, by webhook, or before a run). A sync that fails waits for the next turn.
- Update before every run: Gardience syncs the branch before each run of a playbook from this project.
- Allow choosing a branch, tag or commit at run time: see below.
- Name: lowercase letters, digits,
- Choose Save. An https project syncs right away; an SSH project syncs once you have added its deploy key.
Only admins add, sync and delete projects. A sync:
- Clones the branch with no submodules, no hooks and no symbolic links, up to 64 MB (
GARDIENCE_PROJECT_MAX_MB). - Checks the address the same way as every other address an admin configures, and connects only to the address that was checked. Redirects are not followed.
- Passes the token to Git only for that one command. It is never written to disk or shown in an error.
- Installs the collections and roles named in
requirements.yml,collections/requirements.ymlorroles/requirements.ymlinto the project. Only Galaxy names (namespace.name, with an optional version) are installed. A Git address, a tarball address or a local path in a requirements file is listed under Not installed and never fetched. - Over SSH, reads the server's host key on the first sync and pins it. A later sync to a server with a different key is refused with "Host key verification failed". If the key changed on purpose (a rebuilt Git server), an admin edits the project, ticks Forget the pinned SSH host key and saves; the next sync pins the new key. The pinned key's fingerprint is shown on the project, so you can compare it with the one your Git host publishes.
- Swaps the new copy in at once. A run that already started keeps the copy it started with. If a sync fails, the last good copy stays in use.
- Writes
project.synced(orproject.sync_failed) to the audit log with the commit.
Every run from a Git project logs the project, the commit and the time of the sync.
Your Git projects at the top of the page lists every project with what it follows, its status, its commit and how many playbooks it has. Under each project, Sync history keeps its last 20 syncs: when, who or what started it (a person, the push webhook, the timer, or a run), whether it worked, the commit it moved from and to (or "no new commits"), how long it took, and the install log. Tokens are removed from the log.
Python packages
Some collections need Python packages on the controller, for example VMware or cloud collections. List them in a requirements.txt at the top of the project (one package per line, with an optional version, such as pyvmomi>=8.0). Gardience also reads the requirements.txt of each collection the project installs. At sync, Gardience installs them into the project:
- Ready-built packages (wheels) only. Nothing is built from source, so no package code runs on the server during the install.
- Held to the versions the playbook runner already ships, so a project can never replace Ansible itself. A conflict stops the sync with the reason.
- URLs, files, editable installs and pip options are listed under Not installed and never used.
Playbooks from the project then find the packages when they run. This is what AAP's execution environments are for, without building container images.
Collections source
By default, collections come from Ansible Galaxy and Python packages from PyPI. On a private or air-gapped network, set Collections source on the Projects page to your own Galaxy server (Automation Hub, Galaxy NG or Pulp) and your own Python package index (Artifactory, Nexus or devpi). Every collection and package Gardience downloads is also kept in a mirror on the server, so a later sync still installs it when the Galaxy server cannot be reached. The playbook runner itself never needs the internet: collections and roles travel with the project.
Sync on every push
Each project has a push webhook. On the Projects page, open Push webhook under the project for the payload address and secret.
| Git host | Where the secret goes |
|---|---|
| GitHub, Gitea, Forgejo | The webhook's secret (Gardience checks the signature) |
| GitLab | The webhook's secret token |
| Anything else | An X-Gardience-Token header |
A push to another branch is ignored. A webhook with the wrong secret gets HTTP 403 and changes nothing.
Signed commits only
To make sure only code from your own developers runs, edit the project, tick Only run commits signed by an approved developer, and paste each developer's SSH public key, one per line, as GitHub shows it (ssh-ed25519 AAAA... ann@acme.com). From then on every sync, manual, by webhook, on the timer, before a run or for a branch chosen at run time, checks the commit it lands on:
- Signed by one of those keys: the sync goes ahead, and its history line says who signed it.
- Not signed, or signed by any other key: the sync fails with the reason, and the last good copy stays in use.
So a stolen Git password, or a push by someone with access to the repository but not on your list, never reaches a machine. Developers sign with the SSH key they already use:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign trueGitHub, GitLab and Bitbucket then mark their commits Verified once the same key is added there as a signing key. SSH signatures are checked; GPG signatures are not yet.
Choose a branch at run time
When a project allows it, a run can use another branch, tag or commit than the project's own, for example to try a feature branch before it is merged (AWX calls this branch override). The built-in controller runbook and runbooks made from that project's playbooks then show Branch, tag or commit. Gardience fetches that branch for the run only, logs its commit, and keeps the five most recently used branches cached. A project that does not allow it refuses such a run.
A branch that is not merged yet may hold code nobody has reviewed. Allow this only on projects where that is acceptable, and keep change windows and approvals on production groups.
Turn a playbook into a runbook
On the Projects page, each playbook has Make a runbook. Gardience reads the playbook and builds a form:
- Each variable in the play's
varsbecomes a form field, with its value as the default. - Each
vars_promptquestion becomes a field. Private questions (passwords) are not form fields: the runbook passes a vault secret with the same name instead. - Limit, Only these tags, Vault secrets and Check mode are added to every form.
People who may run runbooks then run the playbook from that form, without writing YAML or knowing Ansible. Form values may only use letters, digits, spaces and . , : @ / + = _ -, so a value can never carry Ansible template code.
Import from AWX or Ansible Automation Platform
To move projects, job templates, schedules and inventory groups from AWX or Ansible Automation Platform:
On a machine that can reach AWX or AAP, install the
awxcommand and export:pip install awxkit awx export --projects --job_templates --inventory --schedules > awx.json- In Gardience, open Automation > Projects and, under Import from AWX or Ansible Automation Platform, choose the file and Preview.
- Read the preview. Nothing is created until you choose Import.
| From AWX or AAP | Becomes in Gardience |
|---|---|
| Git project (https or SSH address, branch, update on launch, allow branch override) | Git project (an SSH project gets a new deploy key to add) |
| Job template (playbook, survey, extra variables, limit, tags, skip tags, check mode) | Runbook with a form |
| Survey text, number and choice questions | Form fields |
| Survey password questions | A vault secret of the same name, passed by the runbook |
| Inventory and its groups | Node groups of the machines Gardience manages, matched by name or short name |
| Weekly, daily and monthly (nth or last weekday) schedules | Schedules, imported turned off |
The preview lists everything that does not carry over, and why. Repository tokens are not in exports (store them in the vault and set them on the project). Machine credentials are not needed, because Gardience reaches machines over Salt. Workflow templates, notifications, teams, roles, custom execution environments and schedules other than daily, weekly or the nth weekday of the month are not imported. Imported schedules start turned off, so nothing runs until an admin turns each one on.
Projects without Git
Local projects keep playbooks, states and scripts on the Gardience server, in folders, with no Git host. On Automation > Projects, choose New local project and give it a name. It starts with playbooks, states and scripts folders. Admins work in its file browser:
- New file and New folder add to the folder you chose. A
.ymlfile starts as a short playbook, a.slsfile as a short state. Text files only (.yml,.sls,.j2,.cfg,.sh,.ps1,.py,.jsonand similar), up to 512 KB each, eight folders deep. - Save (or Cmd or Ctrl+S) publishes the change at once: runs from then on use it, and a run already going keeps the copy it started with. Add a note to say what changed.
- Rename or move takes a new path; use
/for folders. Delete removes a file or folder. - History lists every change to the project, and File history every change to one file, even across a move: when, who, and what. Changes shows the diff. Restore puts the file, or the whole project, back the way it was, as a new change, so nothing in the history is lost.
Playbooks in a local project become runbooks with Make a runbook, and states in its states folder publish under Automation > States (see States). Every change is in the audit log as project.file_saved. Under the hood each local project is a Git repository on the server, so its history is a real commit history.
You can also copy a project folder into srv/ansible/projects/ in your Gardience install directory, for example srv/ansible/projects/webapp/ with site.yml inside it. It appears on the Projects page as shipped with the server, and Make a runbook works the same way.
Ask the AI assistant for a playbook
Ask Ask Gardience for one in plain words, for example "write an Ansible playbook that runs yum update on the RHEL and Oracle machines". It shows the playbook and the node group; Dry run runs it in check mode, Run for real runs it. The playbook is saved in your own upload folder, so you can run it again with gdn or the API, and only you can run it. Running a playbook needs command-execution rights, as above. See AI assistant.
Upload a playbook with gdn
The gdn command line runs a playbook file from your own computer through Gardience. It needs Python 3 and an API key with the Operate scope.
- In Administration > Settings, under API keys, create a key with the scope Operate (request runbooks). Copy it; it is shown once.
Download
gdnand sign in. Replacegardience.example.comwith your server's address:curl -fsSLo gdn https://gardience.example.com/gardience/cli/gdn && chmod +x gdn ./gdn login https://gardience.example.comIf the server uses a certificate your computer does not trust, pass it with
--ca, for example./gdn login https://gardience.example.com --ca gardience.crt.Run the playbook on a node group:
./gdn playbook site.yml -l webservers --check ./gdn playbook site.yml -l webservers --secrets db_password
Options:
| Option | Meaning |
|---|---|
-l, --limit | The node group to run on (required). |
--check | Check mode: show what would change. |
--secrets | Vault secret names, separated by commas. |
--reason | A reason shown with the request. |
--no-wait | Start the run and return. Follow it with ./gdn status RUN_ID. |
What happens to an upload:
- Only the playbook file is uploaded. It cannot use roles, templates or other files from the folder next to it.
- The file must be a YAML list of plays, up to 200 KB.
- It is stored under the key owner's name. You can run only the playbooks you uploaded.
- The upload is recorded in the audit log as
playbook.uploaded, with a fingerprint of the file. - If the key requires approval, the run waits for a second person in Automation > Workflows and approvals.
Local mode (Linux)
In local mode, each Linux node runs the playbook against itself. Gardience copies the project to the node over the Salt connection.
Before the first run, install the Ansible runtime on the nodes by applying the Salt state ansible.runtime. You can do this with a custom runbook whose Salt function is state.apply and whose Arguments is ansible.runtime. The state installs ansible-core 2.18 and the ansible.posix collection into /opt/gardience-ansible, away from the system Python.
To run a playbook, use the Run an Ansible playbook (Linux) runbook. Its form has the same fields as controller mode, plus Login banner text, which the demo playbook uses. Projects come from the same srv/ansible/projects/ folder.
Local mode does not run on Windows nodes. Use controller mode for Windows.
Secrets in playbooks
List the secret names in the runbook's Vault secrets field, or with --secrets in gdn. In the playbook, refer to each secret as a variable with the same name:
- hosts: all
tasks:
- name: Write the database password
ansible.builtin.copy:
content: "{{ db_password }}"
dest: /etc/app/db.pass
mode: "0600"- Each secret must exist, and must be released to every target node. See Secrets vault. In controller mode, a run that breaks this rule is refused before it starts. In local mode, each node that has not received the secret fails with "secret(s) not released to this node".
- Values are passed in a variables file that only the run can read, and the file is deleted when the run ends.
- Any secret value of 4 characters or more that appears in the output is replaced with
********.