Skip to main content

Ansible Fundamentals

What is Ansible?

Ansible is an open-source IT automation and configuration management platform built on Python. It simplifies provisioning, configuration management, application deployment, and task automation across infrastructure by using simple, human-readable automation language.

Key Characteristics

  • Agentless: No agents required on target systems (uses SSH/WinRM)
  • Simple: YAML-based syntax, easy to learn
  • Powerful: Orchestration and multi-tier deployments
  • Scalable: Manages infrastructure at any scale
  • Idempotent: Safe to run repeatedly with the same result

Agentless Architecture

Ansible operates without installing software on managed nodes:

Ansible Architecture Diagram

Execution Flow

Advantages:

  • Minimal dependencies on target systems
  • No background services consuming resources
  • Easy bootstrapping of new servers
  • Reduced attack surface

Inventory Files

Inventory files define the hosts Ansible manages.

INI Format

[webservers]
web1.example.com
web2.example.com
web3.example.com

[databases]
db1.example.com
db2.example.com

[all:vars]
ansible_user=ubuntu
ansible_private_key_file=~/.ssh/id_rsa
ansible_python_interpreter=/usr/bin/python3

YAML Format

all:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
vars:
http_port: 80
databases:
hosts:
db1.example.com:
db2.example.com:
vars:
db_port: 5432
vars:
ansible_user: ubuntu
ansible_private_key_file: ~/.ssh/id_rsa

Host Variables

servers:
hosts:
webserver:
ansible_host: 192.168.1.10
ansible_port: 2222
app_env: production

Ad-Hoc Commands

Run single tasks without playbooks.

# Ping hosts
ansible all -i inventory.ini -m ping

# Run shell command
ansible webservers -i inventory.ini -m shell -a "uptime"

# Copy file
ansible all -i inventory.ini -m copy -a "src=/local/path dest=/remote/path"

# Install package
ansible databases -i inventory.ini -m apt -a "name=postgresql state=present"

# Restart service
ansible webservers -i inventory.ini -m service -a "name=nginx state=restarted"

# Gather facts
ansible all -i inventory.ini -m setup

Playbooks

Playbooks are YAML files defining a series of plays and tasks.

Basic Playbook Structure

---
- name: Configure web servers
hosts: webservers
gather_facts: yes
vars:
http_port: 80
app_version: 1.0

tasks:
- name: Update system packages
apt:
update_cache: yes
become: yes

- name: Install nginx
apt:
name: nginx
state: present
become: yes

- name: Start nginx service
service:
name: nginx
state: started
enabled: yes
become: yes

Playbook Execution

ansible-playbook -i inventory.ini site.yml
ansible-playbook -i inventory.ini site.yml -v # Verbose
ansible-playbook -i inventory.ini site.yml --check # Dry-run
ansible-playbook -i inventory.ini site.yml --tags web # Run specific tags

Roles

Roles organize playbooks into reusable components.

Role Directory Structure

roles/
webserver/
tasks/
main.yml # Tasks definition
handlers/
main.yml # Event handlers
templates/
nginx.conf.j2 # Jinja2 templates
files/
app.conf # Static files
vars/
main.yml # Variables
defaults/
main.yml # Default variables
meta/
main.yml # Role dependencies

Using Roles in Playbooks

---
- name: Deploy web application
hosts: webservers

roles:
- webserver
- { role: ssl, become: yes }
- role: monitoring
vars:
monitoring_port: 9100

Modules

Modules are the units of work in Ansible. Some common ones:

ModulePurposeExample
apt / yumPackage managementInstall, update packages
copyCopy filesLocal to remote
fileFile/directory operationsCreate, delete, permissions
templateDeploy Jinja2 templatesConfig files with variables
serviceManage servicesStart, stop, restart
userUser managementCreate, modify users
command / shellExecute commandsRun shell commands
lineinfileModify filesEdit specific lines
gitGit operationsClone, pull repositories
docker_containerDocker managementRun containers
debugOutput debugging infoPrint variables

Module Example

- name: Configure system
tasks:
- name: Create application user
user:
name: appuser
state: present
shell: /bin/bash
home: /home/appuser

- name: Set file permissions
file:
path: /var/www/app
owner: appuser
group: appuser
mode: '0755'
state: directory

Handlers

Handlers execute tasks only when notified by other tasks (event-driven).

---
- name: Web server configuration
hosts: webservers

tasks:
- name: Copy nginx config
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify: restart nginx

- name: Update app config
template:
src: app.conf.j2
dest: /etc/app/config.conf
notify:
- restart app
- send notification

handlers:
- name: restart nginx
service:
name: nginx
state: restarted

- name: restart app
service:
name: myapp
state: restarted

- name: send notification
debug:
msg: "Application configuration updated"

Variables

Variables store dynamic values used throughout playbooks.

Variable Definition

---
- name: Variable demonstration
hosts: all

vars:
web_servers: ['web1', 'web2', 'web3']
config:
port: 8080
timeout: 30
debug: true

vars_files:
- vars/common.yml
- vars/secrets.yml

tasks:
- name: Display variables
debug:
msg: "Server port is {{ config.port }}"

- name: Loop through list
debug:
msg: "Server: {{ item }}"
loop: "{{ web_servers }}"

Variable Sources (Priority Order)

  1. Extra vars (-e flag)
  2. Task vars
  3. Block vars
  4. Play vars
  5. Role vars
  6. Host facts
  7. Inventory host vars
  8. Inventory group vars
  9. Role defaults

Facts

Facts are system information gathered automatically:

tasks:
- name: Display system information
debug:
msg: |
OS: {{ ansible_os_family }}
Distribution: {{ ansible_distribution }}
IP Address: {{ ansible_default_ipv4.address }}
Processors: {{ ansible_processor_count }}

Templates (Jinja2)

Jinja2 templating allows dynamic file generation.

Template Example

Template file (config.j2):

# Configuration for {{ app_name }}
server {
listen {{ http_port }};
server_name {{ server_name }};

location / {
proxy_pass http://localhost:{{ app_port }};
}

{% if enable_ssl %}
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
{% endif %}
}

Playbook task:

- name: Deploy application config
template:
src: config.j2
dest: /etc/nginx/sites-available/app.conf
vars:
app_name: myapp
server_name: app.example.com
http_port: 80
app_port: 3000
enable_ssl: true
ssl_cert_path: /etc/ssl/certs/app.crt
ssl_key_path: /etc/ssl/private/app.key

Conditionals

Execute tasks based on conditions.

tasks:
- name: Install nginx (Debian)
apt:
name: nginx
state: present
when: ansible_os_family == "Debian"

- name: Install nginx (RedHat)
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"

- name: Create directory if not exists
file:
path: /var/myapp
state: directory
when: not ansible_check_mode

- name: Configure based on host group
set_fact:
app_env: production
when: inventory_hostname in groups['prod']

Loops

Iterate over lists or dictionaries.

tasks:
- name: Install multiple packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- git
- curl

- name: Create multiple users
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
shell: /bin/bash
loop:
- { name: user1, groups: wheel }
- { name: user2, groups: sudo }

- name: Configure services
service:
name: "{{ item }}"
state: started
enabled: yes
loop: "{{ services }}"

Ansible Galaxy

Ansible Galaxy is a repository for community roles and collections.

Galaxy Commands

# Install a role
ansible-galaxy role install geerlingguy.docker

# Install from requirements file
ansible-galaxy role install -r requirements.yml

# Create a new role
ansible-galaxy role init my_new_role

# Install collections
ansible-galaxy collection install community.general

# List installed roles
ansible-galaxy role list

Requirements File (requirements.yml)

---
roles:
- name: geerlingguy.docker
version: 5.0.0

- name: geerlingguy.nginx
src: https://github.com/geerlingguy/ansible-role-nginx.git
scm: git

collections:
- name: community.general
version: ">=5.0.0"
- name: ansible.posix

Ansible vs Chef vs Puppet

FeatureAnsibleChefPuppet
AgentAgentless (SSH)Agent requiredAgent required
Learning CurveEasy (YAML)Steep (Ruby DSL)Moderate (Puppet DSL)
LanguagePython/YAMLRubyPuppet DSL
ScalabilityVery HighHighVery High
Real-time UpdatesPush-basedPull-basedPull-based
CommunityLarge/ActiveLarge/ActiveLarge/Active
CostFree/Open-sourceCommercialCommercial
Setup TimeMinutesHoursHours

Practical Exercises

Exercise 1: Create Basic Inventory and Run Ad-Hoc Commands

Create an inventory file and run commands against local environment:

# Create inventory
cat > inventory.ini << 'EOF'
[local]
localhost ansible_connection=local
EOF

# Run ad-hoc commands
ansible local -i inventory.ini -m ping
ansible local -i inventory.ini -m setup -a "filter=ansible_os_family"

Exercise 2: Write a Simple Playbook

Create a playbook to install and configure Nginx:

---
- name: Install and configure Nginx
hosts: local
become: yes

tasks:
- name: Update package cache
apt:
update_cache: yes

- name: Install Nginx
apt:
name: nginx
state: present

- name: Start Nginx
service:
name: nginx
state: started
enabled: yes

- name: Verify Nginx is running
shell: systemctl status nginx
register: nginx_status

- name: Display status
debug:
var: nginx_status.stdout_lines

Exercise 3: Use Variables and Jinja2 Templates

Create a playbook with dynamic configuration:

---
- name: Deploy application with variables
hosts: local
vars:
app_name: myapp
app_port: 3000
app_user: appuser

tasks:
- name: Create application user
user:
name: "{{ app_user }}"
state: present

- name: Create app directory
file:
path: "/opt/{{ app_name }}"
owner: "{{ app_user }}"
state: directory

Exercise 4: Create a Role Structure

Organize tasks in a reusable role:

ansible-galaxy role init webserver

# In roles/webserver/tasks/main.yml
---
- name: Update package cache
apt:
update_cache: yes

- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- curl

Exercise 5: Use Handlers and Conditionals

Create a playbook with event-driven actions:

---
- name: Conditional and handler example
hosts: local

tasks:
- name: Check if config exists
stat:
path: /etc/myapp/config.yml
register: config_stat

- name: Deploy config if missing
copy:
src: config.yml
dest: /etc/myapp/config.yml
notify: restart app
when: not config_stat.stat.exists

handlers:
- name: restart app
service:
name: myapp
state: restarted

Key Takeaways

  • Ansible is agentless and uses SSH for communication
  • Playbooks define automation workflows in simple YAML
  • Roles organize reusable automation components
  • Handlers provide event-driven task execution
  • Jinja2 templates enable dynamic content generation
  • Variables and facts make playbooks flexible
  • Ansible Galaxy extends functionality through community roles