Ansible is a tool for automating provisioning, configuration, and deployment across multiple hosts via SSH.

Installing Ansible on Ubuntu 20.04

sudo apt update -y
sudo apt install ansible -y

Installing Ansible on CentOS 8

yum update -y
yum install epel-release -y
yum install ansible -y

Add the hosts at the end of /etc/ansible/hosts:

[servers]
192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
[servers:vars]
ansible_user=root

Create and transfer the root SSH key:

ansible all --list-hosts
ssh-keygen -t rsa -C "[email protected]"
ssh-copy-id 192.168.1.1
ssh-copy-id 192.168.1.2
ssh-copy-id 192.168.1.3
ssh-copy-id 192.168.1.4

Running simple commands with Ansible:

ansible-inventory --list -y
ansible servers -m ping
ansible servers -a "apt update"
ansible servers -a "apt upgrade -y"
ansible all -m ping -u root

Check the full list of modules in the Ansible documentation [Link].


PLAYBOOK

nano playbook1.yaml

A playbook is made up of one or more plays, and each play contains one or more tasks.

Use the template below and customize as needed:

---

- name: PLAYBOOK ONE
  hosts: servers
  remote_user: root
  become: true

  pre_tasks:
  - name: APT UPDATE
    apt:
      update_cache: yes
    when: ansible_distribution == "Ubuntu"

  tasks:
  - name: INSTALL LOCATE
    apt:
      name: locate
      state: latest

  - name: COPY FILE
    tags: webserver,apache
    copy:
      src: /data/site.html
      dest: /var/www/html/index.html
      owner: root
      group: root
      mode: 0644

  - name: START SERVICE
    service:
      name: cron
      state: started

  - name: REMOVE LOCATE
    apt:
      name: locate
      state: absent
    when: ansible_distribution in ["Debian","Ubuntu"]

  - name: INSTALL UNZIP
    package:
      name: unzip

  - name: DOWNLOAD AND EXTRACT A ZIP
    src: https://example.com/file.zip
    dest: /root
    remote_src: yes
    mode: 0755
    owner: root
    group: root

  - name: START SERVICE
    service:
      name: httpd
      state: started
      enabled: yes

  - name: CHANGE VARIABLE DATA IN FILE
    lineinfile:
      path: /etc/nginx/nginx.conf
      regexp: '^server_name'
      line: server_name example.com;
    register: webserver_admin_email

  - name: RESTART IF CONFIG CHANGED ABOVE
    service:
      name: nginx
      state: restarted
    when: webserver_admin_email.changed


- name: PLAYBOOK TWO - USERS MANAGEMENT
  hosts: all
  become: true
  tasks:

    - name: CREATE USER
      user:
        name: username
        groups: groupname

    - name: ADD SSH KEY
      user:
        name: username
        key: "copy and paste the key here"

Dry-run first, then execute the playbook:

ansible-playbook playbook1.yaml --check
ansible-playbook playbook1.yaml

Check mode can also be set directly inside a play:

check_mode: yes

OR

check_mode: no

Other useful commands:

ansible all -m gather_facts
ansible all -m gather_facts --limit 192.168.1.1
ansible all -m apt -a update_cache=true --become --ask-become-pass
ansible all -m apt -a name=locate --become --ask-become-pass
ansible all -m apt -a "name=locate state=latest" --become --ask-become-pass
ansible all -m apt -a upgrade=dist --become --ask-become-pass

ROLES

A playbook can be split into sections called roles to make it easier to manage.

- name: ROLES THAT APPLY TO SERVERS
  hosts: servers
  become: true
  roles:
    - base_role
    - server_role

- name: ROLES THAT APPLY TO WORKSTATIONS
  hosts: workstations
  become: true
  roles:
    - base_role
    - workstation_role

HANDLERS

- name: CHANGE VARIABLE DATA IN FILE
  lineinfile:
    path: /etc/nginx/nginx.conf
    regexp: '^server_name'
    line: server_name example.com;
  notify: restart_nginx

File structure for roles with tasks and handlers:

roles/
      base_role/
                tasks/main.yml
                handlers/main.yml
      server_role/
                  tasks/main.yml
                  handlers/main.yml
      workstation_role/
                       tasks/main.yml
                       handlers/main.yml

PLAYBOOK OPTIMIZATION

Use callback plugins to identify tasks that may be slowing down the playbook.

In ansible.cfg, add:

[defaults]
inventory = ./hosts
callbacks_enabled = timer, profile_tasks, profile_roles

The forks setting controls how many tasks run in parallel.

In ansible.cfg, add the desired number:

[defaults]
inventory = ./hosts
forks=25

Or set it at runtime:

ansible-playbook playbookName.yaml --forks 25

Reuse established SSH connections for a set period to reduce overhead:

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=30s

In a controlled environment, you can disable host SSH key checking to improve performance:

[defaults]
host_key_checking = False

To allow tasks to run across servers without waiting for all hosts to finish each step:

---
- name: PLAYBOOK
  hosts: all
  strategy: free

PULL

Instead of pushing configuration from a manager host, Ansible can pull a configuration file from a Git repository and run it locally:

ansible-pull -U http://example.com/file.git

BONUS

Ansible has no built-in graphical interface, but pairing it with Ansible Semaphore greatly improves the user experience [Link].