Featured

How to Create Your First Ansible Playbook

Playbooks are the entry points for any Ansible project.

 

In the simplest case, they contain an ordered set of individual steps (tasks) that describe how to achieve the desired configuration of the target hosts.

 

Although Ansible is not a programming language in the strictest sense, we want to follow the good-old tradition and create a “Hello World” playbook as our first official act. Playbooks are written in YAML, and they conventionally have the.yml or .yaml file extension. The playbook below is pretty much the simplest one possible:

 

---

- hosts: localhost

 

tasks:

     - debug: msg="Hello Ansible!"

 

To ensure that your project folder isn’t cluttered over time, I suggest that you create a playbooks/ folder for playbooks as follows:

 

$ cd ~/ansible/projects/start

$ mkdir playbooks

 

Then, you should store the playbook file in this new folder.

 

Before we start the whole thing, we’ll explain the content:

  • The three dashes are a known YAML marker that indicates the beginning of the document. Many Ansible users omit them, and that’s not a problem since having multiple YAML documents in one file is not possible with Ansible anyway. For completeness, Ansible uses the Python PyYAML module as a parser, which currently implements YAML version 1.1.
  • The hosts keyword specifies the target hosts to address. You can use any pattern instead of localhost, and very often, you’ll see the group name all to address all inventory hosts.

You can also provide a list of patterns, like this one:

- hosts:

   - debian

    - rocky

 

Or you can provide a list of patterns in the compact YAML list form:

 

- hosts: [debian, rocky]

 

Or you can even provide a list of patterns as one Ansible pattern string:

 

- hosts: debian, rocky

  • The tasks keyword indicates that the subsequent steps transform the target hosts into the desired state.
  • The call to the debug module is currently our only task; the msg parameter provides the desired output.

At first glance, it might not be entirely clear to you why the playbook begins with a list. (Technically speaking in YAML, the content of our playbook is a list with a single element!) The explanation is that a playbook can consist of several so-called plays, each of which consists of a set of hosts to which certain tasks are assigned. However, you will not need playbooks with more than one play until much later.

 

To start the whole thing (at last), you use the ansible-playbook command and pass the path to the playbook as a parameter. If your current working directory is the root folder of your project, then it looks like this:

 

$ ansible-playbook playbooks/hello-ansible.yml

 

PLAY [localhost] ************************************************************

 

TASK [Gathering Facts] ******************************************************

ok: [localhost]

TASK [debug] ****************************************************************

ok: [localhost] => {

   "msg": "Hello Ansible!"

}

 

PLAY RECAP ******************************************************************

localhost :    ok=2  changed=0  unreachable=0  failed=0

       skipped=0  rescued=0  ignored=0

 

The output requires some clarification:

  • The output begins with PLAY and the list of the corresponding target systems (in our example, this is only localhost). You can also provide a name: attribute to a play, and the value of this attribute would be displayed here. However, if the playbook contains only a single play, then you’d typically skip providing a name: attribute.
  • The tasks follow with a brief description and a corresponding result.
  • We didn’t request the Gathering Facts task, but it is Ansible’s default behavior to gather information about the involved target systems at the beginning of a play. In most real playbooks, you need these facts, but in our simple example, you could save some time by specifying gather_ facts: no or gather_facts: false to skip this step, as follows:

- hosts: localhost

gather_facts: no

  • The last line provides a summary report: in the localhost target system, two tasks were completed with the desired (successful) result (as indicated by ok=2), there were no changes (changed=0), the target system was reachable (unreachable=0), and no tasks failed (failed=0). For now, you can ignore the skipped, rescued, and ignored fields.

 

Relative Paths in Playbook Calls

In our chosen directory structure, you must live with the minor inconvenience of having to make all calls from the root folder of the project, as follows:

 

$ ansible-playbook playbooks/hello-ansible.yml

 

You must do this unless you use the direnv software or a similar clever method that gives you more flexibility. From now on, I will omit the playbooks/ path component in the examples to spare you this redundancy:

 

$ ansible-playbook hello-ansible.yml

 

Addressing the Actual Target Hosts

The fact is that localhost is not a common target for administrative tasks, so let’s address all the actual targets in the test lab:

 

---

- hosts: all

 

   tasks:

       - debug: msg="Hello Ansible!"

 

And to make things a bit more exciting, you can shut down a machine (e.g., suse) beforehand (by using vagrant halt suse in the vagrant/ folder). The result will be something like this:

 

$ ansible-playbook hello-all.yml

 

PLAY [all] ******************************************************************

 

TASK [Gathering Facts] ******************************************************

ok: [rocky]

ok: [debian]

ok: [ubuntu]

fatal: [suse]: UNREACHABLE! => {"changed": false, "msg":

"Failed to connect to the host via ssh: ssh: connect to host suse port 22:

Connection timed out”: "unreachable": true}

 

TASK [debug] ****************************************************************

ok: [debian] => {

   "msg": "Hello Ansible!"

}

ok: [rocky] => {

   "msg": "Hello Ansible!"

}

ok: [ubuntu] => {

   "msg": "Hello Ansible!"

}

 

PLAY RECAP ******************************************************************

debian :  ok=2   changed=0   unreachable=0   failed=0

rocky :   ok=2   changed=0   unreachable=0   failed=0

suse :    ok=0   changed=0   unreachable=1   failed=0

ubuntu :  ok=2   changed=0   unreachable=0   failed=0

 

First, you’ll notice that there seems to be no fixed order of target systems per task. Due to parallel processing, the order is indeed quite random.

 

You should also note that suse was completely out of the game after its inaccessibility was determined, as the second task did not even attempt to contact this host.

 

Finally, the concluding statistics should not pose any puzzles here either. (I have abbreviated the output slightly to save space.) If you have followed the example in the lab, then remember to let the powered-off machine participate again (by using vagrant up suse).

 

Editor’s note: This post has been adapted from a section of the book Ansible: The Practical Guide for Administrators and DevOps Teams by Axel Miesen. Axel is an Ansible coach. His interest in Linux systems began with his studies at the University of Kaiserslautern, where he studied mathematics and computer science. After graduating in 1998, he began working as a consultant and trainer and has passed on his Linux knowledge and experience to numerous professionals.

 

This post was originally published 10/2025.

Recommendation

Ansible
Ansible

If you want to keep your servers in order, Ansible is the tool of choice! In this practical guide, you’ll learn how to use Ansible to automate server configuration, software deployment, and more. Start by installing Ansible and setting up your initial inventory management process. Then, follow step-by-step instructions for system orchestration, from the basics of playbooks and tasks to using Ansible with Docker. With expert tips and best practices for testing, debugging, and more, this is your all-in-one guide to automating with Ansible!

Learn More
Rheinwerk Computing
by Rheinwerk Computing

Rheinwerk Computing is an imprint of Rheinwerk Publishing and publishes books by leading experts in the fields of programming, administration, security, analytics, and more.

Comments