Chapter 3: Implementing an Ansible Playbook

Implementing multiple plays

A playbook can contain as many plays as you need. Each play targets its own hosts and can use its own connection and privilege settings, so one playbook can orchestrate a whole deployment across different machines.

Writing multiple plays

A playbook is a YAML list of plays, and a play is an ordered list of tasks against hosts from the inventory. So a playbook with several plays simply has several top-level list items, each with the usual play keys.

This is handy for multi-step orchestration: configure the web tier in one play, then the database tier in the next.

---
# This is a simple playbook with two plays

- name: First play
  hosts: web.example.com
  tasks:
    - name: First task
      ansible.builtin.dnf:
        name: httpd
        state: present

    - name: Second task
      ansible.builtin.service:
        name: httpd
        enabled: true

- name: Second play
  hosts: database.example.com
  tasks:
    - name: First task
      ansible.builtin.service:
        name: mariadb
        enabled: true

The second play only starts once the first has finished on all of its hosts.

A playbook with two plays: Plays run in order. The first configures servera with root privileges; the second runs on localhost (inside the execution environment) to test the result.

Per-play users and privilege escalation

Plays can use a different remote user or privilege settings from the defaults in ansible.cfg. Set them in the play itself, at the same level as hosts and tasks.

The remote user

Tasks run over a connection to each managed host, so Ansible needs a user to log in as. With ansible-navigator run, the default is the current user inside the execution environment, which is root. You can change it in ansible.cfg with remote_user, or override it for one play:

remote_user: remoteuser

Privilege escalation

The become keyword enables or disables escalation for one play (or one task), overriding ansible.cfg. It accepts yes/true or no/false.

become: true

If escalation is on, become_method and become_user choose how and to whom:

become_method: sudo
become_user: privileged_user

Put together, a play that connects as automation, becomes root with sudo, and edits /etc/hosts looks like this:

- name: /etc/hosts is up-to-date
  hosts: datacenter-west
  remote_user: automation
  become: true

  tasks:
    - name: server.example.com in /etc/hosts
      ansible.builtin.lineinfile:
        path: /etc/hosts
        line: '192.0.2.42 server.example.com server'
        state: present

Plays that run on localhost

A play with hosts: localhost runs its tasks without SSH. With ansible-navigator, localhost is the execution environment container, not your workstation itself. That makes it a good place for testing tasks, for example requesting a URL from a managed web server:

- name: Test intranet web server
  hosts: localhost
  become: false
  tasks:
    - name: Connect to intranet web server
      ansible.builtin.uri:
        url: http://servera.lab.example.com
        return_content: true
        status_code: 200

ansible.builtin.uri fails the task unless the server answers with the expected status_code, so a green run means the site is really up.

Takeaway

Write a playbook with several plays. Use the chapter lab to check this on a real host.