ansible - part2

dos2unix

Well-Known Member
Joined
May 3, 2019
Messages
3,244
Reaction score
2,993
Credits
28,670
Well OK, now we've ran a few playbooks. But these have been pretty simple, can I put some logic in a playbook?

Code:
---
# Ansible playbook to update packages and reboot if any were upgraded

- name: Update and reboot if packages were upgraded
  hosts: fedora,redhat,ubuntu,debian
  become: yes  # Use sudo to become root
  tasks:
    - name: Determine the OS type
      ansible.builtin.shell: |
        if [[ -f /etc/debian_version ]]; then
          echo "debian"
        elif [[ -f /etc/fedora-release ]]; then
          echo "fedora"
        elif [[ -f /etc/redhat-release ]]; then
          echo "redhat"
        fi
      register: os_type  # Register the output of the command to use in next tasks

    - name: Update packages for Debian-based systems
      ansible.builtin.apt:
        upgrade: dist
        update_cache: yes
      when: os_type.stdout == "debian"
      register: debian_upgrade

    - name: Update packages for Fedora-based systems
      ansible.builtin.dnf:
        name: "*"
        state: latest
        update_cache: yes
      when: os_type.stdout == "fedora" or os_type.stdout == "redhat"
      register: fedora_upgrade

    - name: Check if packages were upgraded (Debian-based)
      ansible.builtin.debug:
        msg: "Nothing was updated."
      when: os_type.stdout == "debian" and debian_upgrade.changed == false

    - name: Check if packages were upgraded (Fedora-based)
      ansible.builtin.debug:
        msg: "Nothing was updated."
      when: (os_type.stdout == "fedora" or os_type.stdout == "redhat") and fedora_upgrade.changed == false

    - name: Reboot if packages were upgraded (Debian-based)
      ansible.builtin.reboot:
      when: os_type.stdout == "debian" and debian_upgrade.changed == true

    - name: Reboot if packages were upgraded (Fedora-based)
      ansible.builtin.reboot:
      when: (os_type.stdout == "fedora" or os_type.stdout == "redhat") and fedora_upgrade.changed == true

This playbook will check to see what distro you are running and then adjust the commands accordingly.
This tries to do a system update. If anything gets upgraded, it will reboot your computer. If not, it does
nothing.

Notice I didn't use an ansible module to find out which distro we are using. I used the shell command
to check the /etc/release file. While are hundreds of modules, there still isn't one for everything, sometimes
you have to use your own commands.

But in this case, I actually could have done it another way, the ansible way. :)

Code:
---
# Ansible playbook to update packages and reboot if any were upgraded

- name: Update and reboot if packages were upgraded
  hosts: fedora,redhat,ubuntu,debian
  gather_facts: yes  # Gather facts to use ansible_distribution
  become: yes  # Use sudo to become root
  tasks:
    - name: Update packages for Debian-based systems
      ansible.builtin.apt:
        upgrade: dist
        update_cache: yes
      when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"
      register: debian_upgrade

    - name: Update packages for Fedora-based systems
      ansible.builtin.dnf:
        name: "*"
        state: latest
        update_cache: yes
      when: ansible_distribution == "Fedora" or ansible_distribution == "RedHat"
      register: fedora_upgrade

    - name: Check if packages were upgraded (Debian-based)
      ansible.builtin.debug:
        msg: "Nothing was updated."
      when: (ansible_distribution == "Ubuntu" or ansible_distribution == "Debian") and debian_upgrade.changed == false

    - name: Check if packages were upgraded (Fedora-based)
      ansible.builtin.debug:
        msg: "Nothing was updated."
      when: (ansible_distribution == "Fedora" or ansible_distribution == "RedHat") and fedora_upgrade.changed == false

    - name: Reboot if packages were upgraded (Debian-based)
      ansible.builtin.reboot:
      when: (ansible_distribution == "Ubuntu" or ansible_distribution == "Debian") and debian_upgrade.changed == true

    - name: Reboot if packages were upgraded (Fedora-based)
      ansible.builtin.reboot:
      when: (ansible_distribution == "Fedora" or ansible_distribution == "RedHat") and fedora_upgrade.changed == true

Near the top of my script, I have a "gather_facts" line. This will check the ansible_distribution for me. No shell commands
are necessary. The gather_facts is kind of an all or none option, it can slow your playbook down by a second or two while it
gathers the facts from your client computer.
 
Last edited:


So OK, we can do some conditional logic, and if and elif statements, but what about math?
Can we do math in an ansible script?

Code:
---
# Ansible playbook to check CPU utilization and give output as a combined percentage using the last minute value

- name: Check CPU utilization
  hosts: localhost
  gather_facts: yes
  become: yes
  tasks:
    - name: Get the number of processor cores
      set_fact:
        processor_cores: "{{ ansible_processor_cores }}"

    - name: Get CPU load average using 'uptime' command
      ansible.builtin.command: uptime
      register: uptime_output

    - name: Extract the last minute load average
      set_fact:
        load_avg_1: "{{ uptime_output.stdout.split('load average:')[1].split(',')[0] | trim }}"

    - name: Convert load average to float
      set_fact:
        load_avg_1_float: "{{ load_avg_1 | float }}"

    - name: Calculate CPU utilization percentage
      set_fact:
        cpu_utilization: "{{ (load_avg_1_float | float / processor_cores) * 100 | round(2) }}"

    - name: Print CPU utilization
      ansible.builtin.debug:
        msg: "CPU Utilization is {{ cpu_utilization }}%"

This script takes the system load for the last one minute from the "uptime" command.
It then determines how many CPUs you have and divides this by 100. Now we get the cpu
usage as a % (percentage) that is a little easier to read.
 
Last edited:
Can I transfer files with ansible? Here is an example.

First let's make a motd file and put it in the same directory as our playbook.

Code:
 cat motd

***********************************************
*                                             *
*   Unauthorized access is prohibited.        *
*   Violators will face legal action.         *
*   (We mean it! Lawyers are expensive.)      *
*                                             *
*   Have a great day!                       *
*                                             *
***********************************************

We want to upload this to all our computers. So here is the playbook.

Code:
---
# Ansible playbook to install the /etc/motd file

- name: Deploy MOTD to all servers
  hosts: all
  become: yes
  tasks:
    - name: Copy the MOTD file
      ansible.builtin.copy:
        src: /path/to/your/motd  # Replace this with the actual path to your MOTD file on the Ansible server
        dest: /etc/motd
        owner: root
        group: root
        mode: '0644'

You can copy from the client computer to the ansible server, or from the ansible server to client,
or even from client to client.

By now you're starting to see some of the potential of ansible.
 
So far, all of our playbook have been relatively simple, usually just doing one task like adding
users or copy a motd file. But what if I want to do a lot of tasks on one playbook?

Code:
---
# Ansible playbook to install nginx, php, and postgresql
# Also enables systemd services and opens ports 80 and 443 on the firewall

- name: Install and configure services
  hosts: all
  gather_facts: yes
  become: yes
  tasks:
    - name: Install required packages on Debian-based systems
      ansible.builtin.apt:
        name:
          - nginx
          - php
          - postgresql
        state: present
        update_cache: yes
      when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"

    - name: Install required packages on Fedora-based systems
      ansible.builtin.dnf:
        name:
          - nginx
          - php
          - postgresql
        state: present
        update_cache: yes
      when: ansible_distribution == "Fedora" or ansible_distribution == "RedHat"

    - name: Enable and start nginx service
      ansible.builtin.systemd:
        name: nginx
        enabled: yes
        state: started

    - name: Enable and start php-fpm service (Debian-based)
      ansible.builtin.systemd:
        name: php7.4-fpm  # Adjust the PHP version if necessary
        enabled: yes
        state: started
      when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"

    - name: Enable and start php-fpm service (Fedora-based)
      ansible.builtin.systemd:
        name: php-fpm
        enabled: yes
        state: started
      when: ansible_distribution == "Fedora" or ansible_distribution == "RedHat"

    - name: Enable and start postgresql service
      ansible.builtin.systemd:
        name: postgresql
        enabled: yes
        state: started

    - name: Open ports 80 and 443 on Debian-based systems
      ansible.builtin.apt:
        name: ufw
        state: present
        update_cache: yes
      when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"

    - name: Allow ports 80 and 443 on Debian-based systems
      ansible.builtin.ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop:
        - 80
        - 443
      when: ansible_distribution == "Ubuntu" or ansible_distribution == "Debian"

    - name: Open ports 80 and 443 on Fedora-based systems
      ansible.builtin.firewalld:
        port: "{{ item }}/tcp"
        permanent: yes
        state: enabled
      loop:
        - 80
        - 443
      when: ansible_distribution == "Fedora" or ansible_distribution == "RedHat"

    - name: Reload firewalld on Fedora-based systems
      ansible.builtin.firewalld:
        immediate: yes
        state: reloaded
      when: ansible_distribution == "Fedora" or ansible_distribution == "RedHat"

This script does a lot, it determines what distro you are running and then install a "lnpp-stack"
(linux, nginx, postgresql, php). Not only that, it enables the services for us, and opens the firewall
ports for us, all based on which distro we are running.

So, you see, the sky is pretty much the limit to what you can do here.
 
While I'm thinking about it. Let's talk about a fancy word. "idempotent".

In Ansible, tasks are designed to be idempotent. This means that if a task has already been successfully completed, running the same playbook again won't redo the task. Ansible checks the current state and only makes changes if necessary to reach the desired state.

In other words, if ansible already did something, it won't do it again, even if I run the same playbook again.
Now for system updates, it will check to see if there are any new updates to install, but for most things, it simply
checks to see if it was already done.
 

Staff online


Top