☁ Cloud & DevOps

Ansible: configuring many servers in a repeatable way

How agentless Ansible works over SSH: inventory, playbooks, idempotent modules, handlers, roles and variables, with an example that sets up a web server.

Creating a server now takes seconds. Configuring it properly, however, still takes dozens of steps: users, SSH keys, packages, firewall, services, configuration files, automatic updates. Doing it by hand once is tolerable; doing it on ten machines, always the same way, is an invitation to error. Ansible is one of the simplest tools for turning these steps into code.

Why Ansible is easy to adopt

Ansible has two features that explain its popularity:

  • it needs no agents: it connects to servers over SSH, the same access you already use, and runs tasks with the Python present on the remote machine;
  • it uses readable YAML files: a playbook can be understood even without knowing the tool, because it describes tasks with explicit names.

You just install it on a control computer, your laptop or a dedicated server, and you can start.

The inventory

The inventory lists the machines to manage, divided into groups. In its simplest form it is a text file:

[web]
web1.example.com
web2.example.com

[db]
db1.example.com ansible_user=admin

Groups let you apply different configurations to different roles. With cloud providers you can also use a dynamic inventory, which queries the APIs and builds the server list based on labels or tags.

The playbook

A playbook maps groups of machines to a list of tasks. Each task uses a module, a ready-made action: install a package, copy a file, manage a service.

- name: Configure the web servers
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Copy the site configuration
      ansible.builtin.template:
        src: site.conf.j2
        dest: /etc/nginx/sites-available/site.conf
      notify: Reload nginx

    - name: Make sure nginx is running
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

  handlers:
    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

You run it with ansible-playbook -i inventory site.yml. The become option runs tasks with administrative privileges.

Idempotence: describing the state, not the actions

Ansible modules are idempotent: the "install nginx" task does not reinstall the package if it is already there, it simply checks it. That is why a playbook can be run as many times as you like; on the second pass, if nothing has changed, Ansible reports that there is nothing to do.

It is a valuable property but not an automatic one. The command or shell module, which runs an arbitrary command, cannot know whether the command has already had its effect. It should be used only when no specific module exists, and accompanied by conditions such as creates, which indicate when the command must not be repeated.

Handlers: acting only when needed

In the example, the task that copies the configuration notifies the "Reload nginx" handler. The handler runs only once at the end, and only if the file actually changed. That way the service is not restarted needlessly on every run, and if five tasks change five files the reload still happens once.

Templates and variables

The template module uses the Jinja2 engine to generate configuration files with variable values: domain name, number of worker processes, database address. Variables are defined per group, per host or in the playbook, and allow the same code to be used for staging and production by changing only the values.

For passwords, Ansible offers Ansible Vault, which encrypts files or individual variables. The encrypted file can live in the repository; the key to decrypt it stays outside.

Roles: organizing the code

As playbooks grow, it pays to split them into roles: folders with a standard structure containing tasks, handlers, templates and default variables for a single purpose. A "base" role creates users and configures SSH; a "web" role installs and configures the web server; a "backup" role schedules copies. A playbook then becomes a simple list of roles to apply to each group.

There are ready-made public roles for many pieces of software. Before using one, read its code: it runs commands with administrative privileges on your machines.

Good practices

  • Test in check mode: --check shows what would change without applying it, and --diff highlights the differences in files.
  • Limit the scope with --limit when you want to apply a change to a single machine before extending it.
  • Give every task a clear name: the output becomes a readable log of what was done.
  • Keep everything in Git: inventory, playbooks and roles become the documentation of how the servers are configured.
  • Run playbooks regularly, not just at installation, to bring machines back to the intended state if someone changed them by hand.

Ansible and Terraform together

The two tools complement each other. Terraform creates the resources, meaning machines, networks and DNS; Ansible configures what runs inside the machines. With container images part of Ansible's work moves into the Dockerfile, but it remains useful for preparing hosts, managing traditional servers and automating recurring operations such as rotating certificates or adding a user on every machine.

In short

  • Ansible configures servers over SSH without agents, using readable YAML playbooks.
  • The inventory groups machines; idempotent modules allow repeated runs without unwanted effects.
  • Handlers restart services only when the configuration really changes.
  • Templates, variables, Vault and roles make the code reusable and secure.
  • Use --check and --diff before applying, and leave resource creation to Terraform.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. With degrees in computer science and psychology, he combines technical analysis with an understanding of the human factor.

Read also