Use Ansible to Check and Configure Timezone to you area

Support you are staying in Singapore and your time zone is “Asia/Singapore”. How do you check and configure the Timezone?

- hosts: all
  tasks:

  - name: Check current Timezone
    command: timedatectl show --property=Timezone --value
    register: timezone_output
    changed_when: false

  - name: Configure Timezone to Asia/Singapore
    command: timedatectl set-timezone Asia/Singapore
    when: timezone_output.stdout != "Asia/Singapore"

The command module is used to execute the timedatectl command and retrieve the current timezone and registered as timezone_output

The 2nd command is to configure the timedatectl if the timezone_output is not your desired “Asia/Singapore”

Starting Commands for Ansible

Number 1: Preparing Ansible.cfg

ansible.cfg is used to customize the behavior of Ansible and define various settings and options for managing infrastructure and deploying applications. Inside you ansible_cluster. Create an ansible.cfg

[defaults]
inventory = inventory
private_key_file = ~/.ssh/id_rsa
become = true
become_user = root

Number 2: To Test the Connection with Nodes via SSH

[root@h001 ansible_cluster]# ansible all -m ping
192.168.200.161 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
192.168.200.160 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
192.168.200.162 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
192.168.200.163 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

Number 3: Listing Hosts

[root@h001 ansible_cluster]# ansible all --list-hosts
  hosts (4):
    192.168.200.160
    192.168.200.161
    192.168.200.162
    192.168.200.163

Number 4: Gather Facts about Hosts

Lots of information regarding the hosts….. If you want to limit to a single hosts, use the parameter “–limit”

[root@h001 ansible_cluster]# ansible all -m gather_facts --limit 192.168.200.161
192.168.200.161 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.200.161"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::5eed:8cff:fe80:aee3"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "02/02/2023",
        "ansible_bios_vendor": "HPE",
        "ansible_bios_version": "U46",
        "ansible_board_asset_tag": "NA",
        "ansible_board_name": "ProLiant DL360 Gen10 Plus",
...
...
...

Number 4a: Gather Facts about Hosts Distribution

[root@h001 ansible_cluster]# ansible all -m gather_facts --limit 192.168.200.161 |grep distribution
        "ansible_distribution": "Rocky",
        "ansible_distribution_file_parsed": true,
        "ansible_distribution_file_path": "/etc/redhat-release",
        "ansible_distribution_file_variety": "RedHat",
        "ansible_distribution_major_version": "8",
        "ansible_distribution_release": "Green Obsidian",
        "ansible_distribution_version": "8.7",

References:

  1. Learn Linux TV Chapter 4
  2. Ansible Quickstart
  3. Ansible CLI cheatsheet

Git clone fatal: Could not read from remote repository.

If you have an error

git clone git@github.com:kittycooldew/ansible_cluster.git
fatal: Could not read from remote repository.

If you encounter an SSH authentication issue, do add your key to the SSH keychain:

ssh-add ~/.ssh/id_rsa

If you encounter an error such as this

Could not open a connection to your authentication agent.

Start the process and add key to SSH Keychain

eval `ssh-agent -s`
ssh-add ~/.ssh/id_rsa

Try the git clone again. It should work

References:

Could not load the Qt platform plugin “xcb” in “” even though it was found for Rocky Linux 8

If you encounter this issue

qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: xcb.

You can resolve the issue by installing the xcb package

# dnf install xcb*
Last metadata expiration check: 1:39:51 ago on Tue 09 May 2023 11:47:04 AM +08.
Package xcb-util-0.4.0-10.el8.x86_64 is already installed.
Dependencies resolved.
====================================================================================================================================================================================================================
 Package                                                   Architecture                                 Version                                               Repository                                       Size
====================================================================================================================================================================================================================
Installing:
 xcb-util-image                                            x86_64                                       0.4.0-9.el8                                           appstream                                        20 k
 xcb-util-keysyms                                          x86_64                                       0.4.0-7.el8                                           appstream                                        15 k
 xcb-util-renderutil                                       x86_64                                       0.3.9-10.el8                                          appstream                                        18 k
 xcb-util-wm                                               x86_64                                       0.4.1-12.el8                                          appstream                                        31 k

Transaction Summary
====================================================================================================================================================================================================================
Install  4 Packages

Total download size: 83 k
Installed size: 134 k
Is this ok [y/N]: y

Setting up Git Repository for Ansible

Install DNF on the Client Side

$ dnf install git
================================================================================
 Package             Architecture  Version               Repository        Size
================================================================================
Upgrading:
 git                 x86_64        2.31.1-3.el8_7        appstream        160 k
 git-core            x86_64        2.31.1-3.el8_7        appstream        4.8 M
 git-core-doc        noarch        2.31.1-3.el8_7        appstream        2.6 M
 perl-Git            noarch        2.31.1-3.el8_7        appstream         77 k

Transaction Summary
================================================================================
Upgrade  4 Packages

Total download size: 7.6 M
Is this ok [y/N]: 

At github.com,

Do sign up for an account if you have not done so. At github.com, create a new Repository and give it a new name. Look at the pix for more information

Once you have created the repository, you should have something like this

At the profile Icon, click settings, you should enter the page where “SSH and GPG Keys” are present

Click the new SSH icon

Type the name of the key (Your Choice) and copy your ssh public keys into the key box. This can be found at ~/.ssh/id_rsa.pub or ~/.ssh/ed25519.pub. Copy the whole string and paste into the key box

Git Clone the Repository URL

Go back to your repository and copy the code

Next

At the Linux Server….

git clone the URL

$ git clone git@github.com:XXXXXXXXXX/ansible_cluster.git             
Cloning into 'ansible_cluster'...
Warning: Permanently added 'github.com,20.205.243.166' (ECDSA) to the list of known hosts.
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

At your directory go to the ansible_cluster folder, you should be able to see the README.md file which is same as your git repository.

$ cd ansible_cluster
$ cat README.md
# ansible_cluster

Setting some basic git information like user name and email address

$ git config --global user.name "user1"
$ git config --global user.email "user1@hello.com"
$ cat ~/.gitconfig
[user]
        name = melvin soh
        email = melvin@ntu.edu.sg
[init]
        defaultBranch = main

Pushing and cloning to the git repository

Let’s make some changes to the README.md

$ vim README.md
#ansible_tutorial

Hello

Checking their Status

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

Add the changes, coming and pushing the changes to git

$ git add README.md
$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md

$ git commit -m "Updated Readme file on 8/5/2023"
[main 06583ec] Updated Readme file on 8/5/2023
 1 file changed, 3 insertions(+), 1 deletion(-)

$ git push origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 308 bytes | 102.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:kittycooldew/ansible_cluster.git
   5524665..62b9b0a  main -> main

References:

  1. Top 20 Git Commands With Examples
  2. Learn Linux TV Chapter 3

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