Install Ansible and Write Your First Playbook: Inventory, Modules and Roles
What you will build #
Ansible connects to servers over SSH and applies a desired state described in YAML. You need nothing installed on the servers except Python and SSH. In this tutorial you install Ansible, write an inventory, run a command on all hosts and write a playbook that installs and configures NGINX.
For test servers, create three VMs with Vagrant and VirtualBox or use EC2 instances created with Terraform.
Install #
On the control machine (your laptop or a bastion). pipx keeps Ansible isolated:
sudo apt update && sudo apt install -y pipx
pipx ensurepath
pipx install --include-deps ansible
ansible --version
On macOS: brew install ansible. Windows is supported only as the control node through WSL 2.
Inventory #
The inventory lists the hosts, grouped:
[web]
node1 ansible_host=192.168.56.11
node2 ansible_host=192.168.56.12
[db]
node3 ansible_host=192.168.56.13
[all:vars]
ansible_user=vagrant
ansible_ssh_private_key_file=~/.ssh/id_ed25519For cloud servers that change all the time, see dynamic inventory. Authentication uses SSH keys, as in public key authentication.
Ad hoc commands #
ansible all -i inventory.ini -m ping
ansible web -i inventory.ini -m command -a "uptime"
ansible web -i inventory.ini -b -m apt -a "name=htop state=present update_cache=true"
-m selects the module, -a its arguments and -b (become) uses sudo.
Your first playbook #
- name: Configure web servers
hosts: web
become: true
vars:
site_title: "Hello from Ansible"
tasks:
- name: Install NGINX
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Publish the home page
ansible.builtin.template:
src: index.html.j2
dest: /var/www/html/index.html
mode: "0644"
notify: Reload NGINX
- name: Make sure NGINX is running and enabled
ansible.builtin.service:
name: nginx
state: started
enabled: true
handlers:
- name: Reload NGINX
ansible.builtin.service:
name: nginx
state: reloaded<h1>{{ site_title }}</h1>
<p>Served by {{ inventory_hostname }}</p>Run it:
ansible-playbook -i inventory.ini web.yml
The result shows changed for what Ansible modified. Run it a second time: everything is ok and nothing changes. That is idempotence, the main property of a good playbook. Use --check --diff to preview the changes without applying them.
Handlers, variables and conditions #
- A handler runs once at the end, and only if a task notified it (the reload happens only when the page changed).
- Variables come from the playbook, the inventory (
group_vars/andhost_vars/) or-e name=value. when: ansible_facts['os_family'] == "Debian"restricts a task.loop:repeats it.- Encrypt secrets with
ansible-vault encrypt_string.
Move it to a role #
When a playbook grows, split it into a role:
ansible-galaxy init roles/nginx
Put the tasks in roles/nginx/tasks/main.yml, the template in roles/nginx/templates/, the handler in roles/nginx/handlers/main.yml and the defaults in roles/nginx/defaults/main.yml. Then the playbook becomes:
- hosts: web
become: true
roles:
- nginx
Follow the roles best practices and the playbook structure, and keep several environments with separate inventories.
Next steps #
Build a complete Kubernetes cluster with the Ansible and Vagrant tutorial.