Ansible Playbooks & CI/CD Pipelines: SSH Handling, YAML Structure, and Playbook Elements Explained
Introduction
When you start automating infrastructure with Ansible inside a CI/CD pipeline, you quickly encounter two recurring topics: how to manage SSH authentication without manual prompts and how to write a valid playbook.yml file. Both are essential for reliable, repeatable deployments. This article breaks down the reasoning behind using ssh‑add (or ssh-agent) instead of simply copying a private key, clarifies the YAML syntax that defines a playbook, and explains the purpose of key sections such as name, hosts, and roles. By the end, you’ll have a solid reference you can apply to any Ansible‑based DevSecOps lab or certification exercise.
1. SSH Authentication in CI/CD Pipelines
1.1 Why not just copy the private key to ~/.ssh?
In a typical development workstation you might place a private key (id_rsa) in ~/.ssh and use it directly. In a CI/CD runner, however, the environment is ephemeral and non‑interactive. When Ansible tries to connect to a remote host, the SSH client may still ask for a passphrase or confirmation (e.g., “Are you sure you want to continue connecting (yes/no)?”). Because the pipeline cannot respond to prompts, the job would fail.
1.2 The role of ssh-agent and ssh-add
Running an SSH agent creates a background process that holds the private key in memory. The command ssh-add loads the key into that agent. Once loaded:
- The key is available to any subsequent SSH command without reading it from the filesystem each time.
- Passphrase‑protected keys can be supplied once (or stored in a CI secret) and then reused automatically.
- The agent eliminates the “first‑time host verification” prompt when you also pre‑populate
known_hosts.
In practice, the CI step looks like this:
stage: prod
image: willhallonline/ansible:2.9-ubuntu-18.04
before_script:
# 1️⃣ Create the .ssh directory
- mkdir -p ~/.ssh
# 2️⃣ Write the secret private key (provided as a CI variable) to a file
- echo "$DEPLOYMENT_SERVER_SSH_PRIVKEY" | tr -d '\r' > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
# 3️⃣ Start the SSH agent and load the key
- eval "$(ssh-agent -s)"
- ssh-add ~/.ssh/id_rsa
If you remove the ssh-agent/ssh-add lines and run the pipeline, you’ll see the job stall on an SSH prompt, confirming the necessity of the agent in automated environments.
1.3 Quick tip
Store the private key as a protected CI variable (e.g., DEPLOYMENT_SERVER_SSH_PRIVKEY) and never commit it to source control.
2. Understanding the YAML Structure of an Ansible Playbook
2.1 Lists vs. Mappings in YAML
YAML distinguishes two fundamental data structures:
| Structure | Symbol | Example |
|---|---|---|
| Mapping (key‑value pair) | : |
hosts: prod |
| List (ordered collection) | - (hyphen) |
- name: Install Terraform |
A playbook is a list of plays. Each play is a mapping that contains keys such as name, hosts, remote_user, become, roles, and tasks. Because the playbook itself is a list, the first line of every play starts with a hyphen.
- name: Deploy web tier
hosts: webservers
become: true
roles:
- nginx
- monitoring
- The hyphen before
nametells YAML “this is the first item in the outer list (the play).” - Inside the play, keys (
hosts,become, etc.) are mappings, so they do not use hyphens.
2.2 When to add a hyphen
| Context | Use a hyphen? | Reason |
|---|---|---|
| Beginning of a play (top‑level) | Yes | Starts a new item in the playbook list. |
| Defining a role or task within a play | Yes | Each role or task is an element of its own list (roles: or tasks:). |
Simple key/value pair like hosts: or remote_user: |
No | These are mappings inside the current play. |
3. Key Playbook Elements Explained
3.1 name – Human‑readable identifier
- What it is: A descriptive label for the play (or for a task/role).
- Why it matters: Makes
ansible-playbookoutput readable and helps teammates understand the intent without digging into the code.
- name: "Provision AWS EC2 instances"
hosts: localhost
...
3.2 hosts – Target inventory group
- What it is: The inventory pattern that selects which machines the play runs against.
- Why no hyphen:
hostsis a key inside the play’s mapping, not an item of a list.
hosts: prod # selects the “prod” group from your inventory
3.3 roles – Reusable collections of tasks, handlers, vars, etc.
- What it is: A list of role names (often pulled from Ansible Galaxy).
- Why hyphenated: Each role is an element in the
roleslist.
roles:
- secfigo.terraform # first role
- mycompany.firewall # second role
3.4 tasks – Individual actions
- What it is: A list of task dictionaries, each with its own
nameand module call.
tasks:
- name: Ensure firewalld is latest
apt:
name: firewalld
state: latest
4. Practical Example: Minimal Playbook for Hardening a Docker Host
---
- name: Harden Docker host (CI/CD friendly)
hosts: docker_host
remote_user: ansible
become: true
# 1️⃣ Load the SSH key via the pipeline (see Section 1)
# 2️⃣ Apply hardening role from Galaxy
roles:
- secfigo.docker_hardening
# 3️⃣ Optional custom tasks
tasks:
- name: Verify firewalld is active
service:
name: firewalld
state: started
Notice the hyphen before name (starting a new play) and before each role and task (list items).
5. Common Questions & Tips
Q1: Do I still need ssh-agent if my private key has no passphrase?
A: Not strictly, but using an agent is a best practice because it centralizes key handling and avoids accidental exposure of the key file to other processes.
Q2: Can I mix hyphenated and non‑hyphenated lines inside a play?
A: Yes. Only list items need hyphens. All other key/value pairs remain plain mappings.
Q3: What happens if I forget a hyphen before a role?
A: Ansible will treat roles: as a mapping with a single string value, causing a syntax error like “list object has no attribute 'items'”.
Tip: Validate YAML early
ansible-playbook --syntax-check playbook.yml
Running the syntax check in your CI pipeline catches indentation or hyphen mistakes before the job proceeds to actual deployment.
Conclusion
Managing SSH authentication with ssh-agent and ssh-add ensures your CI/CD pipelines stay non‑interactive and secure. Understanding YAML’s distinction between lists (hyphen‑prefixed) and mappings (colon‑separated) lets you craft clean, error‑free Ansible playbooks. By mastering the roles of name, hosts, and roles, you’ll write playbooks that are both human‑readable and machine‑ready—key skills for any DevSecOps professional.
Keywords: Ansible playbook, CI/CD SSH handling, ssh-agent, yaml list hyphen, ansible roles, devsecops automation