Getting Notification from Ansible

I have been learning from this book Fabio Alessandro Locati, published under Packt>. The example can be found at https://github.com/PacktPublishing/Learning-Ansible-2.X-Third-Edition/tree/master/Chapter06

Today I am learning about working with notification. One of the biggest advantages of Ansible is its ability, compared to a bash script to notify. The 6 tools which Ansible could easily work with include

  • Email Notification
  • Ansible XMPP/Jabber
  • Slack and Rocket Chat
  • Sending a message to an IRC Channel
  • Amazon Simple Notification Service
  • Nagios

For the full list of notification modules, we can refer to https://docs.ansible.com/ansible/2.8/modules/list_of_notification_modules.html

Email:

---
- hosts: localhost 
  connection: local
  tasks: 
    - name: Read the machine uptime 
      command: uptime -p 
      register: uptime 
    - name: Send the uptime via e-mail 
      mail: 
        host: mail.fale.io 
        username: ansible@fale.io 
        password: PASSWORD 
        to: me@fale.io 
        subject: Ansible-report 
        body: 'Local system uptime is {{ uptime.stdout }}.' 

To sent the email, we will need the SMTP host, credential and content of the email. Do note that mail modules support the following

  • The attachment parameter: To attach attachments
  • The port parameter: port to use by the email server.

Working with Roles in Ansible

I have been learning from this book Fabio Alessandro Locati, published under Packt>. The example can be found at https://github.com/PacktPublishing/Learning-Ansible-2.X-Third-Edition/tree/master/Chapter04

Today I am learning about working with roles. According to the author, the definition of a role is a set of playbooks, templates, files or variables used to achieve a specific goal. For example, the database role and the web server role can be cleanly separated.

You can see the structure in https://github.com/PacktPublishing/Learning-Ansible-2.X-Third-Edition/tree/master/Chapter04

According to the author, he recommends 3 files in the root folder

  • ansible.cfg: A small configuration file to explain to Ansible where to find the files in the folder structure
  • hosts: host files
  • master.yml: A playbook that aligns the whole infrastructure.

2 more folders

  • playbooks: This will contain the playbooks and a folder called groups for groups management
  • roles: Contain all the roles required.

Working with the Conditional feature in Ansible

I have been learning from this book Fabio Alessandro Locati, published under Packt>. The example can be found at https://github.com/PacktPublishing/Learning-Ansible-2.X-Third-Edition/tree/master/Chapter04

Today I am learning about working with local_action feature. This feature allows us to run certain tasks locally on the machine that runs Ansible rather than logging into a remote box and running these commands.

--- 
- hosts: database 
  remote_user: vagrant
  tasks: 
    - name: Count processes running on the remote system 
      shell: ps | wc -l 
      register: remote_processes_number 
    - name: Print remote running processes 
      debug: 
        msg: '{{ remote_processes_number.stdout }}' 
    - name: Count processes running on the local system 
      local_action: shell ps | wc -l 
      register: local_processes_number 
    - name: Print local running processes 
      debug: 
        msg: '{{ local_processes_number.stdout }}' 

Delegating a Task

If you wish to execute an action a different system. For example, you may want to do something on a database node while working on an application node or a localhost, you can use the delegate_to: HOST Property. This is useful when there are complex procedures need to be executed by the local machine or any other machine

--- 
- hosts: database 
  remote_user: vagrant
  tasks: 
    - name: Count processes running on the remote system 
      shell: ps | wc -l 
      register: remote_processes_number 
    - name: Print remote running processes 
      debug: 
        msg: '{{ remote_processes_number.stdout }}' 
    - name: Count processes running on the local system 
      shell: ps | wc -l 
      delegate_to: localhost 
      register: local_processes_number 
    - name: Print local running processes 
      debug: 
        msg: '{{ local_processes_number.stdout }}' 

Working with Conditionals

Ansible provides conditional statements to run a task only when a specified condition(s) is met

--- 
- hosts: webserver 
  remote_user: vagrant
  tasks: 
    - name: Print the ansible_os_family value 
      debug: 
        msg: '{{ ansible_os_family }}' 
    - name: Ensure the httpd package is updated 
      yum: 
        name: httpd 
        state: latest 
      become: True 
      when: ansible_os_family == 'RedHat' 
    - name: Ensure the apache2 package is updated 
      apt: 
        name: apache2 
        state: latest 
      become: True 
      when: ansible_os_family == 'Debian' 

Boolean Conditionals

Apart from matching string, you can check whether a variable is true. Ansible provides a way to check whether a variable is defined. The below features allow us to put the Ansible playgroup in a failure state if the backup_folder is not set

--- 
- hosts: all 
  remote_user: ansible 
  vars: 
    backup: True 
  tasks: 
    - name: Check if the backup_folder is set 
      fail: 
        msg: 'The backup_folder needs to be set' 
      when: backup_folder is not defined 
    - name: Copy the crontab in tmp if the backup variable is true 
      copy: 
        src: /etc/crontab 
        dest: '{{ backup_folder }}/crontab' 
        remote_src: True 
      when: backup 

Working with Handlers

Every handler will run at the end of the playbook if notified. Ansible will make sure, how many times you notify the service, it will call that task once after all other tasks has completed.

--- 
- hosts: webserver 
  remote_user: vagrant
  tasks: 
    - name: Ensure the HTTPd package is installed 
      yum: 
        name: httpd 
        state: present 
      become: True 
    - name: Ensure the HTTPd service is enabled and running 
      service: 
        name: httpd 
        state: started 
        enabled: True 
      become: True 
    - name: Ensure HTTP can pass the firewall 
      firewalld: 
        service: http 
        state: enabled 
        permanent: True 
        immediate: True 
      become: True 
    - name: Ensure HTTPd configuration is updated 
      copy: 
        src: website.conf 
        dest: /etc/httpd/conf.d 
      become: True 
      notify: Restart HTTPd 
  handlers: 
    - name: Restart HTTPd 
      service: 
        name: httpd 
        state: restarted 
      become: True 

Working with Inventory File in Ansible

I have been learning from this book Fabio Alessandro Locati, published under Packt>. The example can be found at https://github.com/PacktPublishing/Learning-Ansible-2.X-Third-Edition/tree/master/Chapter03

Basics

Today I am learning on working with Inventory Files. This time we are dealing with multiple hosts. These hosts have to be placed in the inventory file. An example is pasted here. In hosts.ini, we have

[Compute] 
node01.example.com 
node02.example.com
 
[Login] 
login.example.com

To run the ansible playfile

ansible-playbook -i hosts.ini firstrun.yaml

firstrun.yaml is taken from the site listed. It is to ensure the ansible user exist, accept the SSH keys and provided with sudoers rights with no password.

 hosts: all 
  user: vagrant 
  tasks: 
    - name: Ensure ansible user exists 
      user: 
        name: ansible 
        state: present 
        comment: Ansible 
      become: True
    - name: Ensure ansible user accepts the SSH key 
      authorized_key: 
        user: ansible 
        key: https://github.com/fale.keys 
        state: present 
      become: True
    - name: Ensure the ansible user is sudoer with no password required 
      lineinfile: 
        dest: /etc/sudoers 
        state: present 
        regexp: '^ansible ALL\=' 
        line: 'ansible ALL=(ALL) NOPASSWD:ALL' 
        validate: 'visudo -cf %s'
      become: True

Regular Expressions

If you have a larger number of servers with predictable names, you may want to consider the following expression. You can save 100 lines of listing the server with the following expression

[Compute] 
node[01:100].example.com 

 
[Login] 
login.example.com

Group Variables

If you wish to set a variable for the whole group, you may want to set a variable that is valid for the whole group,. A quick note from the book is that the host variables will override the group variables if the same variable is declared in both spaces.

[Compute] 
node[01:100].example.com 

[compute:vars]
firewalld_enabled=false
 
[Login] 
login.example.com

Working with iterates in Ansibles. For example in a un-iterates codes below

- name: Ensure the HTTP can pass the firewall 
      firewalld: 
        name: http 
        state: enabled 
        permament: True
        immediate: True
    - name: Ensure the HTTPS can pass the firewall 
      service: 
        name: https 
        state: enabled 
        enabled: True 
      become: True 

The codes can be shortened with the following with_items

- name: Ensure HTTP and HTTPS can pass the firewall 
      firewalld: 
        service: '{{ item }}'  
        state: enabled 
        permanent: True 
        immediate: True 
      become: True
      with_items:
        - http
        - https

Using nested loops – with_nested

If you need to iterate all elements of a list with all items from other lists. For example, you may want to create multiple folderw in multiple paths

--- 
- hosts: all 
  remote_user: ansible
  vars: 
    users: 
      - alice 
      - bob 
    folders: 
      - mail 
      - public_html 
  tasks: 
    - name: Ensure the users exist 
      user: 
        name: '{{ item }}' 
      become: True 
      with_items: 
        - '{{ users }}' 
    - name: Ensure the folders exist 
      file: 
        path: '/home/{{ item.0 }}/{{ item.1 }}' 
        state: directory 
      become: True 
      with_nested: 
        - '{{ users }}' 
        - '{{ folders }}' 

Fileglobs loop – with_fileglobs

If you want to perform an action on every file present in a certain folder like copying multiples files with similar names from one folder to another, you can do the following

--- 
- hosts: all 
  remote_user: ansible
  tasks: 
    - name: Ensure the folder /tmp/iproute2 is present 
      file: 
        dest: '/tmp/iproute2' 
        state: directory 
      become: True 
    - name: Copy files that start with rt to the tmp folder 
      copy: 
        src: '{{ item }}' 
        dest: '/tmp/iproute2' 
        remote_src: True 
      become: True 
      with_fileglob: 
        - '/etc/iproute2/rt_*' 

How to check Disk Usage

Checking whether the root partition has run out of inodes. Use the command. If it shows 100%, there are many small files. Perhaps, do look for some of these files at /tmp

df -i
Filesystem                                      Inodes        IUsed        IFree IUse% Mounted on
/dev/mapper/centos-root                        9788840       320849      9467991    4% /
devtmpfs                                      70101496          560     70100936    1% /dev
tmpfs                                         70105725            8     70105717    1% /dev/shm
tmpfs                                         70105725         1581     70104144    1% /run
.....
.....

You may want to check which directories is using the most space with the commands below

% du -hx -d 1 |sort -h
1.3M    ./Espresso-BEEF
4.9M    ./NB07
8.3M    ./Gaussian2
31M     ./Gaussian
65M     ./MATLAB
478M    ./Abaqus
647M    ./pytorch-GAN
10G     ./COMSOL
12G     .

-h argument produces the human-readable output
-x restricts the search to the current directory
-d 1 is the summary for each directory
sort -h produces human-readable output and the directories with the largest usage will appear at the bottom of the list.

Enabling EPEL, Python Bindings for SELinux, and Firewall Settings

I have been learning from this book Fabio Alessandro Locati, published under Packt>.

There is one simple exercise where there is an example of “Configuring a basic server”. The codes can be found

Enabling EPEL

To enable EPEL, in RHEL/CentOS 7, just install the epel-release package

--- 
- hosts: all 
  remote_user: ansible
  tasks: 
    - name: Ensure EPEL is enabled 
      yum: 
        name: epel-release 
        state: present 
      become: True 
    

Python bindings for SELINUX

Ansible is written in python, and mainly use the Python bindings to operate on the operating system.

--- 
- hosts: all 
  remote_user: ansible
  tasks: 
     - name: Ensure libselinux-python is present 
      yum: 
        name: libselinux-python  
        state: present 
      become: True 
    - name: Ensure libsemanage-python is present 
      yum: 
        name: libsemanage-python 
        state: present 
      become: True 

Firewall Settings

--- 
- hosts: all 
  remote_user: ansible
  tasks: 
    - name: Ensure FirewallD is running 
      service: 
        name: firewalld 
        state: started 
        enabled: True 
      become: True 
    - name: Ensure SSH can pass the firewall 
      firewalld: 
        service: ssh 
        state: enabled 
        permanent: True 
        immediate: True 
      become: True 

Basic Installing and Configuring NTP with Ansible

I have been learning from this book Fabio Alessandro Locati, published under Packt>.

There is one simple exercise where there is an example of “Ensuring that NTP is installed, configured and running”. The codes can be found at https://github.com/PacktPublishing/Learning-Ansible-2.X-Third-Edition/tree/master/Chapter02

--- 
- hosts: all 
  remote_user: ansible
  tasks: 
    - name: Ensure NTP is installed 
      yum: 
        name: ntp 
        state: present 
      become: True 
    - name: Ensure the timezone is set to UTC 
      file: 
        src: /usr/share/zoneinfo/GMT 
        dest: /etc/localtime 
        state: link 
      become: True 
    - name: Ensure the NTP service is running and enabled 
      service: 
        name: ntpd 
        state: started 
        enabled: True 
      become: True 

Basic Installing and Configuring a Web Server with Ansible

I have been learning from this book Fabio Alessandro Locati, published under Packt>

There is one simple exercise where there is an example of “Installing and Configuring a Web Server”. The codes can be found at https://github.com/PacktPublishing/Learning-Ansible-2.X-Third-Edition/tree/master/Chapter02

Installing and Configuring a Web Server

The first set of codes deal with the installation and enabling of HTTPd package and services. In addition, both HTTP and HTPS must be able to pass through the firewalld

-- 
- hosts: all 
  remote_user: ansible
  tasks: 
    - name: Ensure the HTTPd package is installed 
      yum: 
        name: httpd 
        state: present 
      become: True 
    - name: Ensure the HTTPd service is enabled and running 
      service: 
        name: httpd 
        state: started 
        enabled: True 
      become: True 
    - name: Ensure HTTP can pass the firewall 
      firewalld: 
        service: http 
        state: enabled 
        permanent: True 
        immediate: True 
      become: True 
    - name: Ensure HTTPS can pass the firewall 
      firewalld: 
        service: https 
        state: enabled 
        permanent: True 
        immediate: True 
      become: True  

Reviewing and Running the Deployment, we can use the command to fire it.

$ ansible-playbook webserver.yaml --list-tasks
$ ansible-playbook -i host webserver.yaml

Publishing a Simple Website

Assuming the Website is a simple single-page website using a simple template call index.html.j2

--- 
- hosts: all 
  remote_user: ansible
  tasks: 
    - name: Ensure the website is present and updated 
      template: 
        src: index.html.j2 
        dest: /var/www/html/index.html 
        owner: root 
        group: root 
        mode: 0644 
      become: True  

Just a note that the “become: True” parameter represents the fact that the tasks should be executed with sudo access. In other words, the sudo user’s file should allow access

Basic Ansible Introductory Learning Notes

I have been learning from this book Fabio Alessandro Locati, published under Packt>

I thought I just capture a few learning notes as I read.

Introduction to Playbooks

Playgroups are one of the core features of Ansible and tell what Ansible what to execute. They are like a do-list for Ansible that contains a list of tasks; each task internally links to a piece of code called a module

- hosts: all 
  remote_user: vagrant
  tasks: 
    - name: Ensure the HTTPd package is installed 
      yum: 
        name: httpd 
        state: present 
      become: True 
    - name: Ensure the HTTPd service is enabled and running 
      service: 
        name: httpd 
        state: started 
        enabled: True 
      become: True 

What it means?

  • hosts: List the Host or Host groups. The Host field is required. The –list-hosts-host will let us know which hosts the playbook is using.
  • remote_user: The user Ansible will be using while logging onto the system.
  • There are 2 tasks.
    • The first one is to ensure that the httpd package is present
    • The 2nd one is to enable the httpd service is enabled and running
  • The tasks are quite self-explanatory.
  • become: True. The commands should be executed with sudo access. If the sudo user’s file does not allow the user to run the particular command, the command will fail

Running a Playbook

$ ansible-playbook -i host setup_apache.yml

Ansible Verbosity

You can increase the verbosity by using the parameter -v, -vv or -vvv

Variables in Ansible

---
- hosts: all
  remote_user: vagrant
  tasks: 
    - name: Print OS and version
      debug:
        msg: '{{ ansible_distribution }} {{ ansible_distribution_version }}'

Creating the Ansible User

--- 
- hosts: all 
  user: vagrant 
  tasks: 
    - name: Ensure ansible user exists 
      user: 
        name: ansible 
        state: present 
        comment: Ansible 
      become: True
    - name: Ensure ansible user accepts the SSH key 
      authorized_key: 
        user: ansible 
        key: https://github.com/fale.keys 
        state: present 
      become: True
    - name: Ensure the ansible user is sudoer with no password required 
      lineinfile: 
        dest: /etc/sudoers 
        state: present 
        regexp: '^ansible ALL\=' 
        line: 'ansible ALL=(ALL) NOPASSWD:ALL' 
        validate: 'visudo -cf %s'
      become: True

The lineinfile is an interesting module. It works in a similar way to sed (a stream editor) where you specify the regular expression that will be used to match the line, and then specify the new line that will be used to substitute the matched line.