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!
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"
}
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
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.
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:
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
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.
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.
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
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