====== Ansible ======
===== How to install Ansible =====
On a Linux machine. This may also we a WSL machine do the following:
sudo apt get update
sudo apt install ansible
sudo apt install sshpass
Installation is complete!
===== Configure Ansible =====
To configure Ansible create an inventory file. Make a directory on the root the Linux Directory:
mkdir inventory
Inside the inventory folder, create a file called hosts
nano hosts
Inside the hosts file add the following:
[servers]
212.71.234.106
192.168.1.30
192.168.1.40
192.168.1.50
192.168.1.6
The top line is the label that will be used when issuing Ansible commands. The IP's below are the machine that Ansible will communicate with.
Now run your first Ansible command!
ansible -i ./hosts servers -m ping --user root --ask-pass
The above takes the input of the hosts file created and specifies the label "servers" as the target for the action. **-m **indicates an ansible module to be used. In this case it is ping. **–user** specifies the user that will be used to run the commands remotely on the Servers. **–ask-pass** will prompt for a password
You should see the following:
192.168.1.40 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.1.30 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
212.71.234.106 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
192.168.1.50 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.1.6 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
===== Other Ansible commands =====
The command below runs and adhoc command in the way of directly communicating at a interactive session. **-a **indicates an adhoc command. In the case below the release version of the Linux OS will be displayed
ansible -i ./hosts servers -a "cat /etc/os-release" --user root --ask-pass
The following is displayed:
212.71.234.106 | CHANGED | rc=0>>
PRETTY_NAME="Ubuntu 21.10"
NAME="Ubuntu"
VERSION_ID="21.10"
VERSION="21.10 (Impish Indri)"
VERSION_CODENAME=impish
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=impish
192.168.1.40 | CHANGED | rc=0>>
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
192.168.1.30 | CHANGED | rc=0>>
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
192.168.1.50 | CHANGED | rc=0>>
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
192.168.1.6 | CHANGED | rc=0>>
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
To display the timezone on your Servers run the following:
ansible -i ./hosts servers -a "timedatectl" --user root --ask-pass
The following is displayed:
192.168.1.6 | CHANGED | rc=0>>
Local time: Thu 2022-06-16 06:47:41 UTC
Universal time: Thu 2022-06-16 06:47:41 UTC
RTC time: Thu 2022-06-16 06:47:41
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
212.71.234.106 | CHANGED | rc=0>>
Local time: Thu 2022-06-16 07:47:41 BST
Universal time: Thu 2022-06-16 06:47:41 UTC
RTC time: Thu 2022-06-16 06:47:41
Time zone: Europe/London (BST, +0100)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
192.168.1.40 | CHANGED | rc=0>>
Local time: Thu 2022-06-16 07:47:41 BST
Universal time: Thu 2022-06-16 06:47:41 UTC
RTC time: n/a
Time zone: Europe/London (BST, +0100)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
192.168.1.30 | CHANGED | rc=0>>
Local time: Thu 2022-06-16 07:47:41 BST
Universal time: Thu 2022-06-16 06:47:41 UTC
RTC time: n/a
Time zone: Europe/London (BST, +0100)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
192.168.1.50 | CHANGED | rc=0>>
Local time: Thu 2022-06-16 07:47:41 BST
Universal time: Thu 2022-06-16 06:47:41 UTC
RTC time: n/a
Time zone: Europe/London (BST, +0100)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
====== Ansible Playbooks ======
It is useful running adhoc commands but there are times when several commands need to be run at the same time or more complex commands or you want commands run in a efficient format. This is where ansible playbooks are used. A playbook is a list of commands that are run run against multiple machines. Playbooks are in YAML format.
==== Example 1 - Nano Install ====
See an example playbook below:
- name: nano
hosts: servers
tasks:
- name: ensure nano is installed
apt:
name: nano
state: present
The playbook above has a name of nano - This can be whatever is relevant to the task you want to run. **hosts **specifies the machines that the playbook will be run against. In this case it will be **servers, **this is in reference to the label created in the inventory hosts file. **tasks** will be various tasks that this playbook will run. **name **is the name of that task. **apt **is the module that will be used. **name** is the package that you want to perform an action for. **state** is the current state of the package, in this case it is **present**, meaning that nano should be present. If not it will be installed. There are a number of states, see below:
* absent
* build-dep
* latest
* present ← (default)
* fixed
The playbook can be run using the following:
ansible-playbook ./nanoeditor.yaml --user root --ask-pass --ask-become-pass -i ./hosts
The above is the same as other commands but uses **ansible-playbook** instead of **ansible. **The parameter **–ask-become-pass** means that when the apt module is used remotely to apply nano sudo privileges will be used.
The result of the command is:
SSH password:
BECOME password[defaults to SSH password]:
PLAY [nano] ********************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************
ok: [192.168.1.40]
ok: [192.168.1.6]
ok: [192.168.1.30]
ok: [192.168.1.50]
ok: [212.71.234.106]
TASK [ensure nano is installed] ************************************************************************************************************************************************************
ok: [192.168.1.6]
ok: [212.71.234.106]
ok: [192.168.1.50]
ok: [192.168.1.30]
ok: [192.168.1.40]
PLAY RECAP *********************************************************************************************************************************************************************************
192.168.1.30 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.40 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.50 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.6 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
212.71.234.106 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The above indicates that nano is installed and this can be seen by the **ok **given on the **Task** and **PLAY RECAP**
==== Example 2 - Set Timezone ====
Another playbook example would be to set the timezone for all Linux instances. The playbook for this is below:
- name: timezone
hosts: "*"
become: yes
tasks:
- name: Set Timezone
shell: timedatectl set-timezone Europe/London
The above YAML file is the same as the previous playbook with notable exceptions. The parameter **become** has been added. This means that elevated privileges are used to run this playbook. **shell** is used for the module. This is the same as directly running a command at an interactive session.
Run the playbook with the command:
ansible-playbook ./timezone.yaml --user root --ask-pass --ask-become-pass -i ./hosts
The result is:
SSH password:
BECOME password[defaults to SSH password]:
PLAY [timezone] ****************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************
ok: [212.71.234.106]
ok: [192.168.1.40]
ok: [192.168.1.6]
ok: [192.168.1.30]
ok: [192.168.1.50]
TASK [Set Timezone] ************************************************************************************************************************************************************************
changed: [192.168.1.40]
changed: [192.168.1.6]
changed: [212.71.234.106]
changed: [192.168.1.50]
changed: [192.168.1.30]
PLAY RECAP *********************************************************************************************************************************************************************************
192.168.1.30 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.40 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.50 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.6 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
212.71.234.106 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Note how the above result shows **changed=1. **This means that the configuration for timezone has changed and the timezone change has been applied.
==== Example 3 - Copy files ====
See the YAML below:
- name: file
hosts: "*"
become: yes
tasks:
- name: start copy
ansible.builtin.copy:
src: /home/amit/test.txt
dest: /root
remote_src: yes
owner: root
mode: '0777'
The above copies files from a particular source to a destination. It is very important to specify **remote_src **otherwise it will be assumed the files are coming from the same machine that is Hosting ansible.
==== Example 4 - Change file contents ====
See YAML below:
- name: Change file contents
hosts: "*"
become: yes
tasks:
- name: change file contents
ansible.builtin.lineinfile:
path: /home/amit/test.txt
regexp: 'hello mr amit'
line: ansible is working well!
The above uses the module **ansible.builtin.lineinfile **to replace contents in a file. **regexp** is used to search for text to be replaced. **line** is what will be replacing **regexp**
===== Use Key based authentication =====
The ideal way to connect to remote hosts is to use key based (rsa) authentication. To do this do the following on the ansible Host:
ssh-keygen -t rsa
This will generate a public and private key pair. The public keys need to be distributed to all Hosts that ansible will be serving.
To view the public key navigate to:
cd /root/.ssh
Then view the public key with:
cat id_rsa.pub
The following (similar) will be displayed:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7DMfSVbNJU+NRzc9oeYmVXctBcUyFGLToNal/X2VqTeMI9I/bw0hoKc4zBQzva/nbmTaNMf+JGk3anOKj4Sg9GXyi+/Z51rJ4yYr2fIowFNXm1ltbcOLtBTr6YfJcKkjbOXmVPF4KPWC516P9/kfOx8jSuQpmTx4vYhI0o8Yo9o1YHaNRuijRU5t8NPONDgVJp9rhBDMDDVzg6Y7xTdgawBav3L6hco1Wyk3j41HPSZUoICpXS2K8uVj427WLaFe7/J6U7fIkGlTbD5UL/TLdhgYV29Sskbf5UQxogw3SGxHUqTmbVggQpwNlCUMsyBM61eO+GKrpQvEXGTikQi2tXqRbm2HQROWC32v6VtvwrB5g7tBeqEl/PzMAaHiZm3Foj8juyp6JgFA1lHVDGx2aPGNQItCcrqwVqG/hV4+s4ps7P59a9aKgP2yQcacCcRm0Cx1cbK6AQuSQ7+3oAwUW/IxOsq44YWAuy+fvLfCJGk+LoRzyZjf0UXWlvQAe5L0= root@LT001752
Copy the above to the following location on all remote Host machines:
/root/.ssh/authorized_keys
Once the public key has been added to all Hosts, ansible commands can be run as follows:
ansible-playbook ./copysshpub.yaml -i ./oneserver
ansible-playbook ./changefile.yaml -i ./oneserver
ansible -i ./hosts servers -m ping
Notice how it is no longer necessary to use: ** --user root --ask-pass --ask-become-pass**