====== 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**