Ansible First Playbook
Introduction
In this tutorial, we'll explore how to create and run your first Ansible playbook. Playbooks are at the heart of Ansible's configuration, deployment, and orchestration capabilities. While ad-hoc commands (which we covered earlier) are useful for simple tasks, playbooks allow you to organize multiple tasks, target specific groups of hosts, and create reusable automation workflows.
A playbook is written in YAML format and describes a set of steps (tasks) that should be executed on remote servers. Think of a playbook as a script, but instead of writing procedural code, you're declaring the desired state of your systems.
Prerequisites
Before creating your first playbook, make sure you have:
- Ansible installed on your control node
- SSH access to your target servers
- A basic understanding of YAML syntax
- An inventory file with your servers defined
Understanding Playbook Structure
An Ansible playbook consists of one or more "plays." Each play:
- Targets specific hosts or host groups
- Defines tasks to run on those hosts
- Can include variables, handlers, and other components
Let's look at the basic structure of a playbook:
---
- name: Play name
hosts: target_servers
become: yes # Run tasks with elevated privileges (sudo)
tasks:
- name: First task description
module_name:
parameter1: value1
parameter2: value2
- name: Second task description
another_module:
parameter: value
Creating Your First Playbook
Let's create a simple playbook that installs and starts the Nginx web server on target hosts. We'll call it install_nginx.yml
.
- First, create a new file using your favorite text editor:
nano install_nginx.yml
- Add the following content to the file:
---
- name: Install and configure Nginx
hosts: webservers
become: yes
tasks:
- name: Install Nginx package
apt:
name: nginx
state: present
update_cache: yes
- name: Ensure Nginx service is started
service:
name: nginx
state: started
enabled: yes
This playbook has one play targeting the webservers
group (which should be defined in your inventory). It contains two tasks:
- Install the Nginx package using the
apt
module - Ensure the Nginx service is running and enabled on boot using the
service
module
Running Your First Playbook
To run the playbook, use the ansible-playbook
command:
ansible-playbook -i inventory.ini install_nginx.yml
Where inventory.ini
is your inventory file.
The output will look something like this:
PLAY [Install and configure Nginx] ********************************************
TASK [Gathering Facts] ********************************************************
ok: [webserver1]
ok: [webserver2]
TASK [Install Nginx package] **************************************************
changed: [webserver1]
changed: [webserver2]
TASK [Ensure Nginx service is started] ****************************************
changed: [webserver1]
changed: [webserver2]
PLAY RECAP ********************************************************************
webserver1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
webserver2 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The "PLAY RECAP" section gives you a summary of what happened. Here, all tasks were executed successfully, and two tasks resulted in changes to the systems.
Understanding Playbook Execution Flow
When Ansible runs a playbook:
Ansible executes tasks in order, one at a time, and waits for each task to complete on all hosts before moving to the next task.
Adding More Complexity
Let's enhance our playbook by adding a custom Nginx configuration and a handler to restart Nginx only when the configuration changes:
---
- name: Install and configure Nginx
hosts: webservers
become: yes
tasks:
- name: Install Nginx package
apt:
name: nginx
state: present
update_cache: yes
- name: Deploy custom Nginx configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Ensure Nginx service is started
service:
name: nginx
state: started
enabled: yes
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
In this enhanced version:
- We've added a task to deploy a custom Nginx configuration using the
template
module - We've added a handler that will restart Nginx, but only if the configuration changes
- The
notify
parameter creates this connection between the task and the handler
Using Variables in Playbooks
Variables make playbooks more flexible and reusable. Let's modify our playbook to use variables:
---
- name: Install and configure Nginx
hosts: webservers
become: yes
vars:
nginx_port: 80
nginx_document_root: /var/www/html
tasks:
- name: Install Nginx package
apt:
name: nginx
state: present
update_cache: yes
- name: Deploy custom Nginx configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
vars:
server_port: "{{ nginx_port }}"
document_root: "{{ nginx_document_root }}"
notify: Restart Nginx
- name: Ensure Nginx service is started
service:
name: nginx
state: started
enabled: yes
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
In this version, we've introduced play-level variables (nginx_port
and nginx_document_root
) and task-level variables for the template task.
Our template file (templates/nginx.conf.j2
) might look like:
server {
listen {{ server_port }};
root {{ document_root }};
# Other configuration...
}
Verifying Playbook Success
After running your playbook, it's a good practice to verify that everything is working as expected:
- name: Verify Nginx is responding
uri:
url: "http://{{ inventory_hostname }}:{{ nginx_port }}"
return_content: yes
register: response
failed_when: "'Welcome to nginx' not in response.content"
This task uses the uri
module to make an HTTP request to each server and checks that the response contains the expected content.
Common Playbook Patterns
1. Conditional Tasks
Sometimes you want tasks to run only under certain conditions:
- name: Install Apache on Debian-based systems
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Install Apache on RedHat-based systems
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
2. Loops
For repetitive tasks, use loops:
- name: Install multiple packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- curl
- vim
- git
3. Tags
Tags let you run only specific parts of a playbook:
- name: Install Nginx
apt:
name: nginx
state: present
tags: install
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
tags: configure
To run only the install task:
ansible-playbook -i inventory.ini playbook.yml --tags install
Best Practices for Playbooks
- Use meaningful names for plays and tasks
- Keep playbooks focused on a specific purpose
- Use variables for values that might change
- Use handlers for actions that should happen only when a resource changes
- Include comments to explain complex tasks or decisions
- Version control your playbooks (Git)
- Test playbooks in a safe environment before using them in production
- Organize related tasks into separate files and include them using
include_tasks
Example: Complete Web Server Setup
Let's create a more complete example that sets up a web server with both Nginx and PHP:
---
- name: Set up web server with Nginx and PHP
hosts: webservers
become: yes
vars:
domain_name: example.com
php_version: 7.4
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- nginx
- "php{{ php_version }}-fpm"
- "php{{ php_version }}-mysql"
- "php{{ php_version }}-curl"
- "php{{ php_version }}-gd"
- "php{{ php_version }}-mbstring"
- "php{{ php_version }}-xml"
state: present
- name: Create web root directory
file:
path: "/var/www/{{ domain_name }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Deploy Nginx virtual host configuration
template:
src: templates/vhost.conf.j2
dest: "/etc/nginx/sites-available/{{ domain_name }}.conf"
owner: root
group: root
mode: '0644'
notify: Reload Nginx
- name: Enable Nginx virtual host
file:
src: "/etc/nginx/sites-available/{{ domain_name }}.conf"
dest: "/etc/nginx/sites-enabled/{{ domain_name }}.conf"
state: link
notify: Reload Nginx
- name: Deploy test PHP file
copy:
content: "<?php phpinfo(); ?>"
dest: "/var/www/{{ domain_name }}/info.php"
owner: www-data
group: www-data
mode: '0644'
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
- name: Ensure PHP-FPM is running
service:
name: "php{{ php_version }}-fpm"
state: started
enabled: yes
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloaded
This playbook:
- Updates the apt cache
- Installs Nginx and PHP-FPM with common extensions
- Creates a web root directory
- Deploys and enables a virtual host configuration
- Deploys a test PHP file
- Ensures both Nginx and PHP-FPM services are running
- Includes a handler to reload Nginx when the configuration changes
Summary
In this tutorial, you've learned how to create and run your first Ansible playbook:
- Playbooks are YAML files describing automation tasks
- Each playbook contains one or more plays targeting specific hosts
- Tasks use Ansible modules to make changes to systems
- Handlers execute only when notified by a task that makes changes
- Variables make playbooks more flexible and reusable
- Best practices ensure playbooks are maintainable and effective
You now have the foundational knowledge to start automating your infrastructure with Ansible playbooks. As you become more comfortable with playbooks, you can explore more advanced features like roles, which help organize and reuse your Ansible code at a higher level.
Exercises
- Create a playbook that installs and configures a different web server (like Apache) and compare it to the Nginx example.
- Modify the web server playbook to deploy a custom HTML welcome page.
- Create a playbook that installs and configures a database server like MySQL or PostgreSQL.
- Write a playbook that performs a system hardening task, such as configuring the firewall or disabling unnecessary services.
- Extend the Nginx+PHP playbook to include a task that creates a MySQL database for a web application.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)