Git, CI/CD & GitOpsCX · Ansible Delivery PipelinePipelineShape
The Ansible delivery pipeline — an end-to-end view
What you'll learn
- Draw the end-to-end Ansible delivery pipeline and identify the role of each stage
- Distinguish the three credential boundaries: no-target, ephemeral-target, and apply-time
- Identify which artefact each stage produces and which stages consume them
- Recognise why the pipeline is monotonic: each stage is a gate the next stage depends on
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A production Ansible delivery pipeline is not one job that runs ansible-playbook. It is a chain of deterministic gates, each one owning a single class of mistake and producing an artefact the next stage consumes. Treating the pipeline as a single job is the architectural mistake this Part of the course is designed to correct. The pipeline is monotonic: each stage is a prerequisite for the next, and the pipeline fails closed if any stage refuses to pass.
The end-to-end shape
The pipeline runs on every push to a feature branch and again on every merge to the protected default branch. Each stage is a job with its own runner, its own timeout, and its own set of artefacts it writes for the next stage. The pipeline differs from a Terraform pipeline in one important way: Ansible mutates systems in place rather than declaring a new state, so the idempotency stage replaces the plan-apply split.
flowchart LR
A["Commit pushed"] --> B["yamllint"]
B --> C["ansible-lint"]
C --> D["ansible-playbook --syntax-check"]
D --> E["molecule converge"]
E --> F["molecule verify"]
F --> G["Second converge (idempotency)"]
G --> H["Human approval on PR"]
H --> I["ansible-playbook against inventory"]
I --> J["Live host reports no change"]
Three properties of this diagram are worth pulling out before the rest of the Part walks through it stage by stage:
- Every stage is a gate. The pipeline does not advance past a failed stage. A lint error blocks the syntax-check. A failed Molecule verify blocks the apply. An idempotency drift blocks the merge.
- Every stage produces an artefact the next stage consumes. The syntax-check writes a parsed inventory reference; Molecule writes a converged container image; the apply reads the same inventory the verify stage read.
- Three credential boundaries. The first three stages (yamllint, ansible-lint, syntax-check) hold no managed-host credentials. The Molecule stage holds ephemeral-container credentials. The apply stage holds production SSH or WinRM credentials obtained only from the protected default branch secrets store.
What each stage owns
Each stage of the pipeline has a deliberately narrow job. The narrowness is the point: a stage that tries to do everything does nothing well, and a stage that fails to do its single job is a stage that is missing entirely.
| Stage | What it catches | What it deliberately does not catch |
|---|---|---|
| yamllint | Indentation, document structure, truthy strings | Ansible semantics, task correctness |
| ansible-lint | Best-practice rule violations, FQCN usage, deprecated patterns | Runtime behaviour, idempotency |
| —syntax-check | Playbook parseability, variable resolution, module existence | Whether any task would succeed against a real host |
| molecule converge | Whether the role converges to the expected state on a fresh ephemeral host | Whether production hosts match that state |
| molecule verify | Whether assertions about post-state hold | Whether the same assertions hold against production |
| second converge | Whether the role is idempotent (no further changes on re-run) | Whether production hosts were already in the desired state |
| approval | Author intent, business rationale, change-window fit | Anything mechanical - humans are not linters |
| apply | Mechanical execution of the approved playbook | Anything new - the playbook was already validated |
| live host check | Whether production hosts drifted during the apply | Whether the playbook itself is correct |
The shape is intentional: each row above the apply handles a class of mistake the apply step would otherwise perform blindly.
Three credential boundaries
The pipeline touches managed-host credentials in three distinct ways, and the three must never collapse into one:
- No-target stages (yamllint, ansible-lint, syntax-check) hold no SSH keys, WinRM credentials, or vault passwords. They run on the pull-request job and do not authenticate to any managed host. This is enforced by configuring the runner with no
ANSIBLE_*variables that point at an inventory and by excluding anyinventory.ymlfrom the lint scope. - Ephemeral-target stages (Molecule) hold credentials for throwaway containers or cloud VMs that are created and destroyed per run. They never use production SSH keys and never reach the production network. A Molecule scenario that uses a production credential is a scenario that has already failed its security review.
- Apply-time stages hold production credentials and obtain them only from the protected default branch secrets store. The PR job cannot reach these credentials; only the merge job, against the protected default branch, can.
The boundaries are enforced by the CI system’s environment-variable scoping, by the inventory file location per stage, and by the secrets store’s protected-branch rules. A pipeline that uses one credential set for all three has no PR-to-merge separation and is the structural risk this Part is designed to address.
Production discipline
- One job per stage. Parallelism within a stage is fine; collapsing stages into a single job is not. The boundaries between stages are the boundaries between credentials.
- No managed-host credentials on the PR job. A PR job that can SSH to a production host is a PR job that has compromised the pipeline before review.
- Molecule scenarios are artefacts, not scripts. Upload the convergence log, hash it, key it by commit SHA, and retain it until the apply completes and the live hosts report no further drift.
- The second-converge idempotency check is not optional. A role that changes state on every run is a role that cannot be safely re-applied after a partial failure.
- The pipeline fails closed. A missing or skipped gate is treated as a failure, not a default. Branch protection should require every required check before merge.
Cross-course references
- Ansible for Production Sysadmins - Part XXVI (Testing) covers the Molecule scenario format this pipeline depends on.
- Ansible for Production Sysadmins - Part XXV (CheckDiff) covers the
--checkand--diffsemantics the idempotency check layers into the pipeline. - This course, Part LI (AnsibleCI) - lessons
git-cicd-gitops-li-01throughgit-cicd-gitops-li-06cover the per-gate mechanics this Part assembles into a delivery pipeline. - This course, Part CVIII (IaCIntegration) - lesson
git-cicd-gitops-cviii-03covers how Ansible fits alongside Terraform and Kubernetes in a unified delivery architecture.
Quiz
Knowledge check · 4 questions
Q1. Which statement best describes the relationship between stages in a production Ansible delivery pipeline?
Q2. A pull-request job that runs yamllint, ansible-lint, and ansible-playbook --syntax-check must have a vault password configured so ansible-lint can decrypt role variables.
Q3. Name the three credential boundaries in a production Ansible delivery pipeline and the stage that lives in each.
Q4. Diagnose a pipeline that collapses three credential boundaries into one, and prescribe the structural correction.
A team has a single CI job that runs ansible-lint, syntax-check, Molecule, and the production apply. The job uses one SSH key that has root access to every production host. An attacker compromises a developer's GitHub PAT, pushes a branch that triggers the job, and the Molecule stage installs a backdoored package from a private collection. Because the same SSH key is mounted, the apply job can read the backdoor and propagate it.
Passing score: 75%. Answers are checked in this browser.