Ansible: Playbooks for easy operations


Before moving on to creating more complete projects that include several operations grouped into roles, let's start by creating playbooks with simple operations. This will allow us to get familiar with Ansible tasks.

Playbooks and roles, which we'll cover later, are written in YAML syntax, described in the YAML Appendix.

In the playbooks we will follow the following structure:

  • Playbook Name
  • Indicate if we want to retrieve information from managed hosts
  • Hosts on which to apply the playbook
  • To Do List

Example: Example playbook local.yml to display local information and a message on the screen

---

- name: Basic playbook run locally
  gather_facts: true
  hosts: localhost
  tasks:
    - name: Doing a ping
      ping:

    - name: Show info
      debug:
        msg: "Machine name: {{ ansible_hostname }}"

  • name: playbook name
  • gather_facts: Get information about destination hosts. It would not be necessary because it is the default option
  • hosts: Hosts on which to apply the following tasks
  • tasks: List of tasks to execute

Playbooks are executed with ansible-playbook, which in its most basic syntax executes the playbook we pass to it as an argument.
To run the previous playbook we would type the following command:

$ ansible-playbook local.yml
Below is the result of running the playbook
PLAY [Basic playbook run locally] **********************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Doing a ping] ************************************************************
ok: [localhost]

TASK [Show info] ***************************************************************
ok: [localhost] => {
    "msg": "Machine name: ansible-control"
}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0

In the execution of a playbook it is possible to obtain information from the managed hosts. This process is known as gather facts and by default this information is obtained. The Get Information from Managed Nodes appendix provides information on this functionality.
Parameters of interest for execution of playbooks
-i inventory_file: Allows you to use a specific inventory file
--start-at-task=startup_task: Indicates the task for which to start executing the playbook
--step: Allows you to run the playbook step by step
--become: Execute operations like root
The following command steps through the playbook mysql.yml as root starting with the task Update package cache
$ ansible-playbook mysql.yml --become --start-at-task "Update package cache" --step

Modification of files with blockinfile
The module blockinfile inserts, updates or deletes a block of lines in a file. The modified text is delimited by lines that act as markers.
Example: Playbook blockinfile.yml

---

- name: Blockinfile to edit files
  gather_facts: false
  hosts: all
  tasks:
    - name: "Adding Ansible manager and managed nodes to /etc/hosts"
      blockinfile:
        name: /etc/hosts
        block: |
          # Manager
          20.0.1.7 manager

          # Managed-1
          20.0.1.11 managed-1

          # Managed-2
          20.0.1.4 managed-2
        marker: "# {mark} ANSIBLE MANAGED BLOCK manager and managed nodes"
name: File to modify
block: Block of text to include
marker: Text to delimit the added text block
We will execute it with ansible-playbook
$ ansible-playbook blockinfile.yml --become

PLAY [Blockinfile to edit files] ***********************************************

TASK [Adding Ansible manager and managed nodes to /etc/hosts] ******************
changed: [20.0.1.4]
changed: [20.0.1.11]

PLAY RECAP *********************************************************************
20.0.1.11                  : ok=1    changed=1    unreachable=0    failed=0
20.0.1.4                   : ok=1    changed=1    unreachable=0    failed=0

Since Ansible is idempotent, repeated execution of the playbook will not add new blocks on each execution. Running an Ansible playbook should be understood as this is the desired state for managed machines . A change to the values ​​in the playbook would cause a change, and running the playbook again would push the change to the managed machines.
Below is an excerpt from the file /etc/hostson managed machines as a result of running the above playbook.
127.0.0.1 localhost

....

# 1- BEGIN ANSIBLE MANAGED BLOCK manager and managed nodes
# 2 - Manager
20.0.1.7 manager

# Managed-1
20.0.1.11 managed-1

# Managed-2
20.0.1.4 managed-2
# 3 - END ANSIBLE MANAGED BLOCK manager and managed nodes
1 - Start of block delimiter text
2 - Entered text
3 - End of block delimiter text

Using a variable file for playbooks
The variables defined in group_vars/all.ymlwill be visible to all playbooks in the same directory without the need to indicate or include anything.
As an example, let's define a variable file group_vars/all.ymlwith the name and IP address of a set of machines.
Example: The filegroup_vars/all.yml

manager: { name: manager, ip: 20.0.1.7 }
managed_1: { name: managed-1, ip: 20.0.1.11 }
managed_2: { name: managed-2, ip: 20.0.1.4 }
Let's now look at a review of the previous playbook example using variables.
Example: Playbook of blockinfileusing variables
---

- name: Blockinfile to edit files
  gather_facts: false
  hosts: all
  tasks:
    - name: "Adding Ansible manager and managed nodes to /etc/hosts"
      blockinfile:
        name: /etc/hosts
        block: |
          # Manager
          {{ manager.ip }} {{ manager.name }}

          # Managed-1
          {{ managed_1.ip }} {{ managed_1.name }}

          # Managed-2
          {{ managed_2.ip }} {{ managed_2.name }}
        marker: "# {mark} ANSIBLE MANAGED BLOCK manager and managed nodes"

  • Variables for the IP and name of the nodemanager
  • Variables for the IP and name of the nodemanaged-1
  • Variables for the IP and name of the nodemanaged-2

The Using Variables appendix contains information about how and where you can define variables in Ansible. It also shows how to ask for variables at execution time and how to iterate over them.
Using templates to create custom files
With template we can include files in the managed nodes by previously replacing the variables that they include with their corresponding values.
Example: The base template file sample-template.txt
Custom file example using templates:
node {{ manager.name }} has the IP: {{ manager.ip }}.

node {{ managed_1.name }} has the IP: {{ managed_1.ip }}.
node {{ managed_2.name }} has the IP: {{ managed_2.ip }}.

Example: Playbook template.yml
---

- name: Template to customize files
  gather_facts: false
  hosts: all
  tasks:
    - name: "Creating customized sample-template.txt in /home/ubuntu/sample-template.txt"
      template: >
        src=/home/ubuntu/cursostic/sample-template.txt
        dest=/home/ubuntu/sample-template.txt
        owner=ubuntu
        group=ubuntu
        mode=0644
The result on managed nodes:
Custom file example using templates:
node manager has the IP: 20.0.1.7.

node managed-1 has the IP: 20.0.1.11.
node managed-2 has the IP: 20.0.1.4.

Modification of files with lineinfile
The module lineinfile ensures that a line with a specific text exists in a file. Regular expressions are used for the search .

For example, when the hostname is not in the file /etc/hosts/, an annoying message like the following appears on the command line when we switch to root mode with sudo su -
sudo: unable to resolve host ...
To solve it, simply add 127.0.0.1followed by the name of the machine to the /etc/hosts. Next we will see how to locate the entry 127.0.0.1 localhost in the file and enter a line below to solve the annoying message.
Example: The playbook lineinfile.yml
---

- name: Lineinfile to edit files
  hosts: all
  tasks:
    - name: "Adding hostname to /etc/hosts"
      lineinfile:
        path: /etc/hosts
        insertafter: '^127\.0\.0\.1'
        line: 127.0.0.1 {{ ansible_hostname }}
path: File to modify
insertafter: Find the last line starting with 127.0.0.1to insert a line below ( insertafter)
line: Insert the line with 127.0.0.1and the name of the machine, obtained in the process of Gathering factsand available in the variableansible_hostname

Repository management APT
The module apt_repository allows you to add or remove repositories APT in Ubuntu and Debian distributions.
Example: The playbook apt_repository.yml

---

- name: apt_repository to manage APT repositories
  gather_facts: false
  hosts: all
  tasks:
    - name: "Add APT OpenStack repository for Ubuntu Xenial"
      apt_repository:
        repo: "deb http://ubuntu-cloud.archive.canonical.com/ubuntu xenial-updates/ocata main"
After executing the playbook we can check that the destination machines have the repository available by showing the content of the file
/etc/apt/sources.list.d/ubuntu_cloud_archive_canonical_com_ubuntu.list
The result will be:
deb http://ubuntu-cloud.archive.canonical.com/ubuntu xenial-updates/ocata main
To delete a repository the parameter state: absentofapt_repository
Example: The playbook remove-apt_repository.yml
---

- name: apt_repository to manage APT repositories
  gather_facts: false
  hosts: all
  tasks:
    - name: "Add APT OpenStack repository for Ubuntu Xenial"
      apt_repository:
        repo: "deb http://ubuntu-cloud.archive.canonical.com/ubuntu xenial-updates/ocata main"
      state: absent
After executing the playbook we can verify that the destination machines no longer have the repository available and that the file does not exist
/etc/apt/sources.list.d/ubuntu_cloud_archive_canonical_com_ubuntu.list

Installation of packages
The module apt handles package management on Ubuntu and Debian. When we want to install a list of packages we will define the list of packages and normally we will do it with a variable
Example: The playbook apt.yml

---

- name: Blockinfile to edit files
  gather_facts: false
  hosts: all
  vars: #1
    packages:
      - mysql-server
      - phpmyadmin

  tasks:
    - name: Install packages old style with explicit list
      apt:
        name: "{{ item }}" #2
      with_items:  #3
        - mysql-server
        - phpmyadmin

    - name: Install packages old style using variables
      apt:
        name: "{{ item }}"
      with_items:
        - "{{ packages }}"  #4


    - name: Install packages new style with explicit list
      apt:
        name: ['mysql-server', 'phpmyadmin'] #5

    - name: Install packages new style using variables
      apt:
        name: "{{ packages }}" #6

  1. Defining variables in the playbook itself
  2. {{ item }}represents the iteration variable of a loopwith_items
  3. Specifying a loop
  4. Using a variable to supply the values ​​to iterate over
  5. Compact syntax specifying a list instead of using a loop
  6. Compact syntax using a variable that provides the iteration elements

state: absent To remove packages we use the en parameter apt.
Example: Playbook to remove a package ( remove-apt.yml)

---

- name: Remove apt packages
  gather_facts: false
  hosts: all

  tasks:
    - name: Removing phpmyadmin
      apt:
        name: phpmyadmin
        state: absent

Running commands on managed machines
The module shell takes a command as an argument and executes it on the remote machine.
Example: Playbook shell.yml for copying a file

---

- name: Run commands with shell
  hosts: all

  tasks:
    - name: Copy sample-template.txt to sample-template.bak
      shell: 'cp sample-template.txt sample-template.bak' #1
      args:
        chdir: /home/ubuntu #2

  1. Command to execute
  2. Directory on which to run the command

File management
The module file allows you to configure attributes of files and directories. It also allows the creation and deletion of files.
Example: Playbook for file management file.yml

---

- name: Run file commands
  hosts: all
  gather_facts: false

  tasks:
    - name: Create a directory
      file: #1
        path: /home/ubuntu/myfolder
        state: directory
        owner: ubuntu
        group: ubuntu

    - name: Delete sample-template.bak file
      file:
        path: /home/ubuntu/sample-template.bak
        state: absent #2

  1. Creating a directory and changing the owner
  2. Delete the file

Restart services
The module service allows the administration of services on remote nodes.

Example: Playbook for services restart services.yml

---

- name: Restart services
  hosts: all
  gather_facts: false

  tasks:
    - name: Restart MySQL and Apache
      service:
        name: "{{ item }}" #1
        state: restarted
      with_items: #2
        - mysql
        - apache2

  1. Element of the loop being iterated over
  2. List of services to iterate over

Restart a farm
We can use the module shell to launch an reboot on managed nodes. In addition, we can combine this operation with the wait_for_connection module that waits the number of seconds that we indicate. Once the connection is recovered within that period, the playbook execution continues.
Example: Playbook reboot-and-wait.yml

---

- name: Reboot and wait
  hosts: all

  tasks:
    - name: Rebooting
      shell: sleep 2 && reboot
      async: 1
      poll: 0

    - name: Waiting for rebooting
      wait_for_connection:
        delay: 15
        sleep: 10
        timeout: 300

    - debug:
        msg: "{{ inventory_hostname }} is up and running"

Conditional file download
The module fetch allows the download of files from the managed machines to the manager node.
We can combine this module with the conditional execution that allows for example to download the file only if the remote machine has a certain name. The clause when allows the evaluation of expressions.
As an example, we will use the facts ( facts ) retrieved from the remote machines to obtain their name and execute the file download task only if the name matches the one we are looking for.
Another use could be installing packages with yum or apt depending on whether the distribution is from the ( ansible_facts['os_family']) Red Hat or Debian family, respectively.
Example: Playbook for conditional file download conditions.yml

---

- name: Get remote files
  hosts: all

  tasks:
    - name: Get remote file checking conditions
      fetch:
        src: /etc/hosts
        dest: /home/ubuntu/hosts-from-managed-1
        flat: yes #1
      when:
        ansible_facts['hostname'] == "ansible-managed-1" #2

  1. Download the file without adding the machine name and the full path of the file. The default behavior would download the file in/home/ubuntu/hosts-from-managed-1/20.0.1.11/etc/hosts
  2. The task is only executed on those hosts whose hostnameis the indicated

No comments

Powered by Blogger.