Skip to content

Ansible automates configuration of servers over SSH with no agent to install. You describe the desired state in YAML playbooks and Ansible makes each host match. In the IaC picture (Infrastructure as Code), Terraform creates the servers and Ansible configures what runs on them.

Parts at a glance

Part Meaning
Control node The machine you run Ansible from
Managed nodes Servers it configures (need SSH and Python)
Inventory The list of hosts and groups
Playbook YAML file of plays: which hosts, which tasks
Task One call to a module (apt, copy, service...)
Module A unit of work that is idempotent
Role Reusable bundle of tasks, files, templates and defaults
Handler Task that runs only when notified (e.g. restart a service)
Facts Information gathered about each host
Vault Encrypted storage for secrets

Inventory

# inventory.yml
all:
  children:
    webservers:
      hosts:
        web01:
          ansible_host: 192.168.1.10
        web02:
          ansible_host: 192.168.1.11
  vars:
    ansible_user: administrator
    ansible_ssh_private_key_file: ~/.ssh/id_ed25519

Check connectivity first:

$ ansible -i inventory.yml all -m ping
$ ansible -i inventory.yml webservers -m command -a "uptime"

A First Playbook

# install_software.yml
---
- hosts: webservers
  become: true               # escalate to root with sudo
  tasks:
    - name: Install software
      ansible.builtin.apt:
        name:
          - wget
          - curl
          - mc
        state: present        # absent would uninstall

    - name: Ensure nginx is running
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true
$ ansible-playbook -i inventory.yml install_software.yml
$ ansible-playbook -i inventory.yml install_software.yml --check --diff    # dry run

The output reports ok (already correct), changed (it made a change) or failed. Running it twice should show no changes the second time: that is idempotency.


Handlers, Variables and Templates

- hosts: webservers
  become: true
  vars:
    http_port: 8080
  tasks:
    - name: Deploy nginx config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/conf.d/app.conf
      notify: Reload nginx

  handlers:
    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded
# nginx.conf.j2
server {
    listen {{ http_port }};
    server_name {{ inventory_hostname }};
}

Useful keywords: when (conditions), loop, register, tags, block / rescue, delegate_to.


Roles and Collections

roles/
└── webserver/
    ├── tasks/main.yml
    ├── handlers/main.yml
    ├── templates/
    ├── files/
    ├── defaults/main.yml
    └── vars/main.yml
- hosts: webservers
  roles:
    - webserver
$ ansible-galaxy init roles/webserver
$ ansible-galaxy collection install community.general

Secrets with Ansible Vault

$ ansible-vault create secrets.yml
$ ansible-vault edit secrets.yml
$ ansible-playbook site.yml --ask-vault-pass

Never commit plain-text passwords or private keys in an inventory file.


Ansible and Other Tools

Task Tool
Create servers, networks, databases Terraform / Bicep / CloudFormation
Configure OS packages, users, files, services Ansible
Build application images Docker (3. Container Images and Build Tools)
Run applications at scale Kubernetes (Orchestration Platforms)
Run ordered automation in a pipeline Jenkins / GitHub Actions (Jenkins, GitHub Actions)

In the cloud, cloud-init or an image pipeline often replaces Ansible for first boot, but Ansible stays useful for fleets of long-lived servers and for network devices.


Worked Example: Baseline for Three EC2 Servers

sharifndlovu123/ansible-server-baseline creates three EC2 instances with CloudFormation (two "leaf" nodes and one "spine") and configures them with Ansible.

aws/       CloudFormation: 1.network.yaml (VPC, subnet, IGW, security group with SSH limited to your IP),
           2.launch-template.yaml (launch template, key pair, Auto Scaling Group of 3), deploy.sh,
           get-key.sh (pulls the generated key pair from SSM Parameter Store), connect.sh
ansible/   ini.yaml (YAML inventory with leafs and spines groups), int.ini (flat inventory), ansible.cfg,
           test.sh (validate inventory, ping, run both playbooks)
books/     playbook.yaml (connectivity smoke test), playbook2.yaml (installs curl, git, nginx, apache2)

Lessons: CloudFormation builds the servers and Ansible configures them (see CloudFormation, Bicep and ARM Templates); fetch generated keys from SSM Parameter Store instead of committing them; restrict SSH to your own IP in the security group; validate the inventory and ping before running playbooks.