The Ultimate Ansible tutorial with Ansible Playbook Examples

Are you tired of managing multiple hosts manually? It’s time to learn and automate configuration management with Ansible with this Ultimate Ansible tutorial with Ansible Playbook Examples.

Ansible is the most popular and widely used automation tool to manage configuration changes across your on-prem and cloud resources.

In this article, you’re going to learn everything you should know about Ansible and will get a jump start on running your Ansible commands and Ansible Playbooks!

Let’s get started!

Join 53 other followers

Table of Contents

  1. What is Ansible?
  2. Ansible Architecture Diagram and Ansible components
  3. Ansible Inventory
  4. Ansible Adhoc command
  5. What is Ansible playbook and Ansible playbook examples!
  6. Executing Ansible when conditional using Ansible playbook
  7. Ansible Variables and Dictionary
  8. Ansible Error Handling
  9. Ansible Handlers
  10. Ansible Variables
  11. Ansible Tags
  12. Ansible Debugger
  13. What are Ansible Roles and a Ansible roles examples?
  14. Conclusion

Join 53 other followers

What is Ansible?

Ansible is an IT automation tool and is most widely used for deploying applications and system configurations easily on multiple servers, either hosted on a data center or on Cloud, etc.

Ansible is an agentless automation tool that manages machines over the SSH protocol by default. Once installed, Ansible does not require any database, no daemons to start or keep running.

Ansible uses inbuilt ansible ad hoc command and Ansible Playbooks to deploy the changes. Ansible Playbooks are YAML-based configuration files that you will see later in the tutorial in depth.

Ansible Architecture Diagram and Ansible components

Now that you have a basic idea about what is Ansible? Let’s further look at Ansible Architecture Diagram and Ansible components that will give you how Ansible works and the components Ansible requires.

Ansible Control Node

Ansible Control Node, also known as Ansible Controller host, is the server where Ansible is installed. This node executes all the Ansible ad hoc commands and Ansible playbooks. /commands. You can have multiple Control nodes but not windows. Must have Python Installed.

Ansible Remote Node or Ansible Managed Nodes

Ansible remote nodes or Ansible managed nodes are the servers or network devices on which you deploy applications or configurations using the Ansible ad hoc commands or playbook. These are also known as Ansible hosts.

Ansible Inventory

Ansible inventory is the file on the Ansible controller host or control node, which contains a list of all the remote hosts or managed nodes.

Ansible Core

Ansible core modules or ansible-core are the main building block and architecture for Ansible, including CLI tools such as ansible-playbook, ansible-doc, and interacting with automation.

The Ansible core modules are owned and managed by the core ansible team and will always ship with ansible itself.

Ansible Modules

Ansible modules or Ansible core modules are the code plugins or libraries plugins that can be used from the command line or a playbook task. Ansible executes each module, usually on the remote managed node, and collects return values.

Ansible Collections

Ansible Collections are a distribution format for Ansible content. Using collections, you can package and distribute playbooks, roles, modules, and plugins. A typical collection addresses a set of related use cases. You can create a collection and publish it to Ansible Galaxy or a private Automation Hub instance.

Ansible Task

Ansible Task is a unit of action executed when running the Ansible Playbook. Ansible Playbook contains one or more Ansible tasks.

Ansible Architecture Diagram and Ansible components
Ansible Architecture Diagram and Ansible components

Ansible Inventory

Ansible works against Ansible remote nodes or hosts to create or manage the infrastructure, but how does Ansible know those Ansible remote nodes? Yes, you guessed it right. Ansible Inventory is a file containing the list of all Ansible remote nodes or remote nodes grouped together, which Ansible uses while deploying or managing the resources.

The default location for Ansible inventory is a file called /etc/ansible/hosts. You can specify a different inventory file using the -I <path> option at the command line. Ansible Inventory is declared in two formats, i.e., INI and YAML.

Ansible installed on the control node communicates with remote nodes over SSH Protocol.

Related: working-with-ssh-connectivity

  • Ansible inventory in ini format is declared as shown below.
           automate2.mylabserver.com
           [httpd]
           automate3.mylabserver.com
           automate4.mylabserver.com
           [labserver]
           automate[2:6].mylabserver.com
  • Ansible inventory in YAML format is declared as shown below.
           all:
             hosts:
                automate2.mylabserver.com
             children:
                 httpd:
                   hosts:
                     automate3.mylabserver.com
                     automate4.mylabserver.com
                 labserver:
                    hosts:
                     automate[2:6].mylabserver.com

Ansible Adhoc command

If you plan to create, launch, deploy, or work with a single configuration file such as restarting an nginx service, a reboot of the machine, copying a file to a remote machine, starting or stopping any particular service, etc. on Ansible remote node then running ad hoc commands will suffice.

Ad hoc commands are a quick and efficient way to run a single command on Ansible remote nodes. Let’s quickly learn how to run Ansible Adhoc commands.

  • To ping all the Ansible remote nodes using Ansible Adhoc command.
ansible all -m ping            # Ping Module to Ping all the nodes
ping all the Ansible remote nodes
ping all the Ansible remote nodes
  • To run echo command on all the Ansible remote nodes using Ansible Adhoc command.
ansible all -a "/bin/echo Automate"  # Echo Command to Provide the output
echo command on all the Ansible remote nodes
echo command on all the Ansible remote nodes
  • To check uptime of all the Ansible remote nodes using Ansible Adhoc command.
ansible all -a /usr/bin/uptime   # Provides the Uptime of the Server
checking uptime of all the Ansible remote nodes
checking uptime of all the Ansible remote nodes
  • To create a user on a Ansible remote node using Ansible adhoc command.
ansible all -m ansible.builtin.user -a "name=name password=password" -b
creating Linux user on Ansible remote node
creating Linux user on Ansible remote node
  • To install Apache nginx service on all the Ansible remote nodes using Ansible Adhoc command.
# b is to become root and gain the root privileges
ansible all -m apt -a  "name=apache2 state=latest" -b  
  • To start Apache nginx service on all the Ansible remote nodes using Ansible Adhoc command.
ansible all -m ansible.builtin.service -a "name=apache2 state=started"
start Apache nginx service on all the Ansible remote nodes
start Apache nginx service on all the Ansible remote nodes
  • To reboot all the remote nodes that are part of america-servers group in Ansible inventory using Ansible adhoc command.
    • america-servers is a group of hosts which is saved in /etc/hosts.
    • To run a command use -a flag.
    • “/sbin/reboot” is to command to reboot the machine
    • -f is used to simultenously execute command on 100 servers.
    • -u is used to run this using a different username.
    • –become is used to run as a root.
ansible america-servers -a "/sbin/reboot"  -f 100 -u username --become

What is Ansible playbook and Ansible playbook examples!

Ansible playbooks are used to deploy complex applications, offer reusable and simple configuration management, offer multi-machine deployments, and perform multiple tasks multiple times. Ansible playbooks are written in YAML format containing multiple tasks and executed in sequential order.

Let’s learn how to declare the Ansible playbook to install Apache on the remote node.

# Playbook apache.yml
---
- name: Installing Apache service 
  hosts: my_app_servers                                  # Define all the hosts
  remote_user: ubuntu                                    # Remote_user is ubuntu
  # Defining the Ansible task
  tasks:                                                  
  - name: Install the Latest Apache
    apt:
      name: httpd
      state: latest
  • Before you run your first Ansible playbook using ansible-playbook command make sure to verify the Playbook to catch syntax errors and other problems before you run them using below command.
ansible-playbook apache.yml --syntax-check
executing ansible-playbook
executing ansible-playbook
  • To verify the playbook in detailed view run the ansible-lint command.
ansible-lint apache.yml
verify the playbook in detailed view
verify the playbook in the detailed view
  • To verify the Ansible playbook run the below command with –check flag.
ansible-playbook apache.yml --check
verify the Ansible playbook with --check flag
verify the Ansible playbook with –check flag
  • Finally execute the Ansible playbook using the below command.
ansible-playbook apache.yml 
 execute the Ansible playbook
execute the Ansible playbook
  • If you intend to install and start the Apache service using Ansible Playbook, copy/paste the below content and run the ansible-playbook command.
# Playbook of apache installation and service startup
---
- name: Install and start Apache service 
  hosts: my_app_servers                                  # Define all the hosts
  remote_user: ubuntu                                    # Remote_user is ubuntu
 # Defining the tasks
  tasks:                                                 
  - name: Install the Latest Apache
    apt:
      name: httpd
      state: latest
  - name: Ensure apache2 serice is running
    service:  
      name: apache2
      state: started
    become: yes                   # if become is set to yes means it has activated privileges
    become_user: ubuntu    # Changes user to ubuntu and by default it is root

Executing Ansible when conditional using Ansible playbook

The Ansible when is a Jinja2 expression that evaluates the test or the condition for all remote nodes and wherever the test passes (returns a value of True), run the Ansible task or Ansible playbook on that host.

  • In the below Ansible Playbook there are four tasks i.e
    • To check the latest version
    • Ensuring Apache service is running
    • Creating users with_item
    • Finally editing the file only when ip matches using Ansible when.
---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu

  tasks:
   - name: ensure apache is at the latest version
     apt:
       name: apache2
       state: latest

   - name: Ensure apache2 serice is running
     service:
        name: apache2
        state: started
     become: yes
 
   - name: Create users
     user:
         name: "{{item}}"
     with_items:
      -bob
      -sam
      -Ashok
 
   - name: Edit the text file
     lineinfile:
     path:  /tmp/file
     state: present
     line:  "LogLevel debug"
     when:
     - ansible_hostname == "ip-10-111-4-18" 
  • Finally execute the Ansible playbook using the below command.
ansible-playbook apache.yml 
execute the Ansible playbook
execute the Ansible playbook

In case you have a common parent layer then you don’t need to declare common things every time in the Ansible task as it automatically inherits directives applied at the block level.

In the below example, Ansible when, Ansible become Ansible become_user, ignore_errors are common for the entire block.

---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu

  tasks:
   - name: Install, configure and start Apache
     block:
      - name: Install apache
        apt:
          name: apache2
          state: present
      - name: Ensure apache2 serice is running
        service:
           name: apache2
           state: started
    # Below Directives are common for entire block i.e all tasks
     when: ansible_facts['distribution'] == "Debian" 
     become: true
     become_user: root
     ignore_errors: yes
Execute the Ansible playbook using Ansible when
Execute the Ansible playbook using Ansible when

Ansible Variables and Dictionary

In this section, let’s quickly look into how Ansible loops iterate over a dictionary item and convert it into list items.

  • Create another playbook named abc.yaml and copying the code below.
# Playbook using iterating over a dictionary

 - name: Add several users 
   ansible.builtin.user: 
      name: "{{ item.name }}" 
      state: present 
      groups: "{{ item.groups }}" 
   loop: 
     - { name: 'automate1', groups: 'root' }
     - { name: 'automate2', groups: 'root' } 
  • Now execute the Ansible Playbook using the below command.
ansible-playbook abc.yaml

As you can see below, the Dictionary items have been converted into the List.

Ansible Playbook containing Ansible variables and dictionary
Ansible Playbook containing Ansible variables and dictionary

Ansible Error Handling

When Ansible receives a non-zero return code from a command or a failure from a module, by default, it stops executing on that host and continues on other hosts. Still, at times, a non-zero return code indicates success, or you want a failure on one host to stop execution on all hosts.

Ansible uses Error handling to work with the above conditions to handle these situations and help you get the behavior and output you want.

  • Create below playbook named main.yml and copy/paste the below code and execute the playbook. The below task perform various tasks such as:
    • One of the Ansible tasks prints a message “i execute normally”
    • Fails a task
    • One task will not proceed
ansible-playbook main.yml
---
- name: update web servers
  hosts: localhost
  remote_user: ubuntu
  tasks:
  - name: Ansible block to perform error handling 
    block:

      - name: This task prints a message i execute normally
        ansible.builtin.debug:
          msg: 'I execute normally'

      - name: This task will fail
        ansible.builtin.command: /bin/false

      - name: This task will not proceed due to the previous task failing
        ansible.builtin.debug:
          msg: 'I never execute, '

    rescue:
      - name: Print when errors
        ansible.builtin.debug:
          msg: 'I caught an error, can do stuff here to fix it'

    always:
      - name: Always do this
        ansible.builtin.debug:
          msg: 'This executes always'
Ansible Playbook with Ansible Error Handling
Ansible Playbook with Ansible Error Handling

Ansible Handlers

Ansible handlers are used when you need to perform any task only when notified. You add all the tasks inside the handler block in Ansible Playbook, and Ansible handlers run whenever notify calls them.

For example, whenever there is any update or change in configuration or restarting, the service is required. Ansible Handlers run when they are notified.

In the below example, if there are changes in the /tmp/file.txt, the apache service is restarted. Ansible Handler will restart the Apache service only when the lineinfile task notifies it.

---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu
 
  tasks:
   - name: ensure apache is at the latest version
     apt:
      name: apache2
      state: latest

   - name: Ensure apache2 serice is running
     service:
        name: apache2
       state: started
     become: yes

# Edit the text file using lineinfile module
   - name: Edit the text file 
      lineinfile:
       path:  /tmp/file.txt
       state: present
       line:  "LogLevel debug"
      when:
        - ansible_hostname == "ip-10-111-4-18"
      notify:
      -  Restart Apache

  handlers:
   - name: Restart Apache
     ansible.builtin.service:
       name: apache2
       start: restarted 
Ansible Playbook with Ansible Handlers
Ansible Playbook with Ansible Handlers

Ansible Variables

Ansible uses Ansible variables to manage multiple configurations with various attributes. With Ansible, you can execute tasks and playbooks on multiple systems with a single command with variations among different systems.

Ansible variables are declared with standard YAML syntax, including lists and dictionaries. There are lots of ways in which you can set your Ansible variables inside the ansible-play; let’s learn by:

  • Defining Ansible variable normally
  • Defining Ansible variables from file
  • Defining Ansible variables from roles
  • Defining Ansible variable at run time
---
- name: update web servers
  hosts: webserver
  remote_user: ubuntu
  remote_install_path: /tmp/mypath       # Defining Simple Variable      ~1
  vars:                                  # Defining Variables from Role  ~2
     favcolor: blue   
  vars_files:                           # Defining Variables from file   ~3  
     - /vars/external_vars.yml
 
  tasks:
   - name: Check Config
     ansible.builtin.template:
       src: my.cfg.j2
       dest: '{{remote_install_path}}/my.cfg'
  • Defining Ansible variable at run time with key : value format
ansible-playbook myplaybook.yml --extra-vars "version=1.23.45 other_variable=auto"
  • Defining Ansible variable at run time with JSON format
ansible-playbook myplaybook.yml --extra-vars '{"version":"1.23.45","other_variable":"auto"}'

Ansible Tags

When you need to run specific tasks in the Ansible playbook, you need to consider using Ansible Tags. Ansible tags are applied on Ansible blocks level, Ansible playbook, Ansible task, or at Ansible role level.

Let’s learn how to add Ansible Tags at different levels.

  • Adding Ansible Tags to individual Ansible tasks.
---
 - hosts: appservers
   tasks:
   - name: Deploy App Binary
     copy:
	   src: /tmp/app.war
	   dest: /app/
	   
	tags: 
	  - apptag          # Applying Ansible Tag to task 1
	 
 - hosts: dbserver	   
   tasks:
   - name: Deploy DB Binary
     copy: 
       src: /tmp/db.war`
       dest: /db
     tags:   	         # Applying Ansible Tag to task 2
       - dbtag
  • Adding Ansible Tags to Ansible block.
tasks:
- block:
  tags: ntp                     # Applying Ansible Tags to Ansible block
  - name: Install ntp
    ansible.builtin.yum:
      name: ntp
      state: present

  - name: Enable and run ntpd
    ansible.builtin.service:
      name: ntpd
      state: started
      enabled: yes
  • Adding Ansible Tags to Ansible Playbook.
- hosts: all
  tags: ntp                     #  Applying Ansible Tags to Ansible Playbook
  tasks:
  - name: Install ntp
    ansible.builtin.yum:
      name: ntp
      state: present

Ansible Debugger

Ansible provides a debugger to fix errors during execution instead of editing it and then running.

  • Using Ansible debugger on a Ansible task.
- name: Execute a command
  ansible.builtin.command: "false"
  debugger: on_failed
Ansible debugger in Ansible Playbook
Ansible debugger in Ansible Playbook
  • Using Ansible debugger on a Ansible Playbook.
- name: My play
  hosts: all
  debugger: on_skipped
  tasks:
    - name: Execute a command
      ansible.builtin.command: "true"
      when: False

  • Using Ansible debugger on a Ansible Playbook and Ansible task.
- name: Play
  hosts: all
  debugger: never
  tasks:
    - name: Execute a command
      ansible.builtin.command: "false"
      debugger: on_failed

What are Ansible Roles and a Ansible roles examples

Ansible Roles is a way to structurally maintain your Ansible playbooks, i.e., it lets you load the variables from files, variables, handlers, tasks based on the structure. Similar to the Ansible modules, you can create different Ansible roles and reuse them as many times as you need.

  • Let’s look at what an Ansible Role directory structure looks like.
    • Tasks : This directory contains one or more files with tasks . These file can also refer to files and templates without needing to provide the exact path.
    • Handlers: Add all your handlers in this directory
    • Files: This directory contains all your static files and scripts that might be copied or executed to remote machine.
    • Templates: This directory is reserved for templates that generate files on remote hosts.
    • Vars: You define variables inside this directory and then can be referenced elsewhere in role.
    • defaults: This directory lets you define default variables for included or dependent role.
    • meta: This directory is used for dependency management such as dependency roles.
Ansible Role directory structure
Ansible Role directory structure

Join 53 other followers
  • Creating a Sample Ansible Playbook as shown below which you will break in different file to understand how Ansible role has file structure.
--- 
- hosts: all 
  become: true 
  vars: 
    doc_root: /var/www/example 
  tasks: 
    - name: Update apt 
      apt: update_cache=yes 
 
    - name: Install Apache 
      apt: name=apache2 state=latest 
 
    - name: Create custom document root 
      file: path={{ doc_root }} state=directory owner=root group=root  
 
    - name: Set up HTML file 
      copy: src=index.html dest={{ doc_root }}/index.html owner=root group=root mode=0644 
 
    - name: Set up Apache virtual host file 
      template: src=vhost.tpl dest=/etc/apache2/sites-available/000-default.conf 
      notify: restart apache 
 
  handlers: 
    - name: restart apache 
      service: name=apache2 state=restarted
  • Let’s break the playbook that you created previously into Ansible roles by:
    • Creating a directory named roles inside home directory.
    • Creating a directory named apache inside roles inside home directory.
    • Creating a directory named defaults, tasks, files, handlers, vars, meta, templates inside apache
  • Create main.yml inside ~/roles/apache/tasks directory.
---    
- name: Update apt
      apt: update_cache=yes

    - name: Install Apache
      apt: name=apache2 state=latest

    - name: Create custom document root
      file: path={{ doc_root }} state=directory owner=www-data group=www-data

    - name: Set up HTML file
      copy: src=index.html dest={{ doc_root }}/index.html owner=www-data group=www-data mode=0644

    - name: Set up Apache virtual host file
      template: src=vhost.tpl dest=/etc/apache2/sites-available/000-default.conf
      notify: restart apache
  • Create main.yml inside ~/roles/apache/handlers directory.
---
handlers:
    - name: restart apache
    service: name=apache2 state=restarted
  • Create index.html inside ~/roles/apache/files directory.
<html>
<head><title>Configuration Management Hands On</title></head>
<h1>This server was provisioned using Ansible</h1>
</html>
  • Create vhost.tpl inside cd ~/roles/apache/templates
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot {{ doc_root }}

<Directory {{ doc_root }}>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
  • Create main.yml inside cd ~/roles/apache/meta
---
dependencies:
  - apt
  • Create my_app.yml inside home directory
---
- hosts: all
  become: true
  roles:
    - apache
  vars:
    - doc_root: /var/www/example

Join 53 other followers

Conclusion

In this Ultimate Guide, you learned what is Ansible, Ansible architecture, and understood Ansible roles and how to declare Ansible Playbooks.

Now that you have gained a handful of Knowledge on Ansible, what do you plan to deploy using it?

Advertisement

One thought on “The Ultimate Ansible tutorial with Ansible Playbook Examples

  1. Pingback: How to Install Ansible and Run ansible-playbooks on Ubuntu 18.04 LTS | Automateinfra

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s