Ansible’s magic isn’t in what it does, but in its radical simplicity of how it does it.
Let’s see it in action. Imagine you need to set up a web server on a new Ubuntu machine.
First, we need an inventory file to tell Ansible where to manage. This is a simple text file, often called hosts.
[webservers]
192.168.1.100
192.168.1.101
Now, a playbook. This is a YAML file that describes the desired state of your infrastructure. We’ll call it webserver.yml.
---
- name: Configure web server
hosts: webservers
become: yes # Run tasks as root
tasks:
- name: Ensure Apache is installed
apt:
name: apache2
state: present
- name: Ensure Apache is running and enabled
service:
name: apache2
state: started
enabled: yes
- name: Deploy a simple index.html
copy:
content: "<h1>Hello from Ansible!</h1>"
dest: /var/www/html/index.html
To run this, you’d execute:
ansible-playbook -i hosts webserver.yml
Ansible connects to 192.168.1.100 and 192.168.1.101 (using SSH by default, no agent needed on the target), checks if apache2 is installed, ensures it’s running and enabled, and then drops a basic index.html file into place. If any of these steps are already met, Ansible reports "ok" and moves on. If a change is made, it reports "changed." This idempotency is key: running the playbook again won’t break anything; it just ensures the desired state.
The problem Ansible solves is the tedious, error-prone, and often undocumented process of manually configuring servers, installing software, and deploying applications. It shifts infrastructure management from a series of imperative commands to a declarative description of the final state.
Internally, Ansible works by using a connection plugin (most commonly SSH) to execute modules on remote hosts. These modules are small, self-contained scripts that perform specific actions (like installing a package, starting a service, or copying a file). Ansible pushes these modules to the target machines, runs them, and then cleans them up. You don’t need to install an agent on the target machines; they just need Python and SSH access.
The become: yes directive is crucial for tasks requiring elevated privileges. It tells Ansible to use sudo (or other configured privilege escalation methods) on the remote host to execute the task. Without it, tasks like installing packages or modifying system files would fail due to permission errors.
You can organize your playbooks using roles. A role is a predefined collection of tasks, variables, handlers, and other Ansible artifacts that can be reused across multiple playbooks. For example, you could have a webserver role that encapsulates all the logic for setting up Apache, and then simply include that role in different playbooks for different environments. This promotes modularity and reusability, making your automation much more manageable.
Ansible’s module system is extensive, covering everything from package management (apt, yum) and service control (service, systemd) to file manipulation (copy, template), user management (user), and even cloud provider integrations. The template module, for instance, uses Jinja2 templating to dynamically generate configuration files based on variables you provide, allowing for environment-specific settings without duplicating entire files.
The real power of Ansible lies in its ability to define complex workflows across multiple hosts and services. You can orchestrate deployments, perform rolling updates, and manage entire application stacks with a few lines of YAML.
The core conceptual leap is realizing that Ansible isn’t just for deploying things; it’s for defining the state of your entire infrastructure and letting Ansible figure out how to get there. You tell it what you want, and it makes it happen.
The next step is understanding how to manage complex variable hierarchies and use Ansible Vault to encrypt sensitive data.