Key Concepts
- Control Node: Where Ansible is installed and runs from
- Managed Nodes: Servers managed by Ansible (no agent required)
- Inventory: List of managed nodes
- Playbooks: YAML files describing automation tasks
- Tasks: Individual units of work
- Modules: Tools for executing specific actions
- Roles: Reusable collections of tasks
- Handlers: Tasks run only when notified
Installation
# Ubuntu/Debian
$ sudo apt update
$ sudo apt install ansible
# RHEL/CentOS
$ sudo yum install epel-release
$ sudo yum install ansible
# macOS
$ brew install ansible
# Python pip (recommended for version control)
$ pip install ansible
Configuration File
Location hierarchy (first one found is used):
ANSIBLE_CONFIG
environment variable./ansible.cfg
(current directory)~/.ansible.cfg
(home directory)/etc/ansible/ansible.cfg
(system-wide)
Essential settings:
[defaults]
inventory = ./inventory
remote_user = ansible
host_key_checking = False
forks = 20
timeout = 30
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
Inventory Basics
Simple inventory file (INI format)
# Individual hosts
web1.example.com
web2.example.com ansible_host=192.168.1.101
# Groups
[webservers]
web1.example.com
web2.example.com
[dbservers]
db1.example.com
db2.example.com
# Group of groups
[datacenter:children]
webservers
dbservers
# Variables
[webservers:vars]
http_port=80
YAML format inventory
all:
hosts:
mail.example.com:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_host: 192.168.1.101
vars:
http_port: 80
dbservers:
hosts:
db1.example.com:
db2.example.com:
Common inventory parameters
ansible_host
: IP address/hostnameansible_port
: SSH portansible_user
: SSH usernameansible_connection
: Connection type (ssh, local, docker)ansible_ssh_private_key_file
: SSH private key
Ad-hoc Commands
Basic syntax:
ansible [host-pattern] -m [module] -a "[module options]" [options]
Common examples:
# Ping all hosts
$ ansible all -m ping
# Run shell command
$ ansible webservers -m shell -a "uptime"
# Check facts (system info)
$ ansible web1.example.com -m setup
# File operations
$ ansible all -m file -a "path=/tmp/test state=touch mode=0644"
# Package management
$ ansible webservers -m apt -a "name=nginx state=present"
# User management
$ ansible all -b -m user -a "name=deploy state=present"
# Service management
$ ansible webservers -b -m service -a "name=nginx state=started enabled=yes"
# Copy files
$ ansible all -m copy -a "src=/local/path dest=/remote/path"
Basic Playbook Structure
---
- name: Example playbook
hosts: webservers
become: yes
vars:
http_port: 80
max_clients: 200
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Configure nginx site
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/default
notify: Restart nginx
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
Running Playbooks
# Basic execution
$ ansible-playbook playbook.yml
# Dry run (no changes)
$ ansible-playbook --check playbook.yml
# Run against specific hosts
$ ansible-playbook playbook.yml --limit web1.example.com
# Pass variables
$ ansible-playbook playbook.yml -e "http_port=8080"
# With vault password
$ ansible-playbook playbook.yml --ask-vault-pass
Variables
Variable definition in playbooks
vars:
http_port: 80
max_clients: 200
Variable files
# vars/server.yml
http_port: 80
max_clients: 200
Referenced in playbook:
vars_files:
- vars/server.yml
Command line variables
$ ansible-playbook playbook.yml -e "http_port=8080 max_clients=250"
$ ansible-playbook playbook.yml --extra-vars "@vars/production.json"
Using registered variables
- name: Check service status
command: systemctl status nginx
register: result
- name: Show status
debug:
var: result.stdout
Conditionals
When clause
- name: Install Apache on RedHat systems
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Create file if not exists
file:
path: /etc/app.conf
state: touch
when: not stat_result.stat.exists
Multiple conditions
- name: Install package if sufficient memory
apt:
name: large-app
state: present
when: ansible_memtotal_mb > 1024 and ansible_distribution == "Ubuntu"
Loops
Simple loop
- name: Create users
user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlie
Loop with dictionary
- name: Create users with properties
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: alice, groups: admin }
- { name: bob, groups: developers }
- { name: charlie, groups: developers }
Handlers
tasks:
- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
Templates (Jinja2)
Basic template (nginx.conf.j2)
server {
listen {{ http_port | default(80) }};
server_name {{ server_name }};
root {{ doc_root }};
location / {
try_files $uri $uri/ =404;
}
}
Conditionals in templates
{% if ansible_distribution == 'Ubuntu' %}
apt_cmd = apt-get
{% elif ansible_distribution == 'CentOS' %}
apt_cmd = yum
{% endif %}
Loops in templates
{% for host in groups['webservers'] %}
server {{ host }};
{% endfor %}
Simple Role Structure
roles/
webserver/
defaults/ # Default variables
main.yml
files/ # Static files
handlers/ # Handlers
main.yml
tasks/ # Tasks
main.yml
templates/ # Jinja2 templates
vars/ # Role variables
main.yml
Creating a role
$ ansible-galaxy init rolename
Using roles in playbooks
- hosts: webservers
roles:
- webserver
- database
Error Handling
Ignoring errors
- name: Run this command anyway
command: /opt/app/setup.sh
ignore_errors: yes
Block with rescue
- block:
- name: Service update
service:
name: myapp
state: restarted
rescue:
- name: Fallback task
debug:
msg: "Service update failed"
always:
- name: Always run this
debug:
msg: "Service update attempted"
Ansible Vault
Create encrypted file
$ ansible-vault create secrets.yml
Edit encrypted file
$ ansible-vault edit secrets.yml
Encrypt existing file
$ ansible-vault encrypt vars/credentials.yml
Run playbook with vault
$ ansible-playbook site.yml --ask-vault-pass
$ ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt
Common Modules
File management
- file: Manage files and directories
- copy: Copy files to remote hosts
- template: Process Jinja2 templates
- lineinfile: Modify specific lines in files
- fetch: Get files from remote hosts
Package management
- apt: Manage Debian/Ubuntu packages
- yum: Manage RHEL/CentOS packages
- dnf: Manage Fedora packages
- package: Generic package manager
Service management
- service: Manage services
- systemd: Manage systemd services
User management
- user: Manage user accounts
- group: Manage groups
- authorized_key: Add/remove SSH authorized keys
System
- command: Execute commands
- shell: Execute shell commands
- reboot: Reboot systems
- cron: Manage cron jobs
- mount: Manage mounts
Utilities
- debug: Print statements during execution
- wait_for: Wait for conditions
- assert: Assert given expressions are true
- set_fact: Set variables
Best Practices
Organization
- Use roles for reusable code
- Group related tasks
- Keep playbooks focused on specific purposes
Variables
- Use descriptive names
- Keep sensitive data in Ansible Vault
- Set defaults in roles/defaults/main.yml
Idempotence
- Ensure tasks can run multiple times without side effects
- Use state parameters consistently
Testing
- Use –syntax-check to validate playbooks
- Use –check for dry runs
- Test on staging before production
Performance
- Increase forks in ansible.cfg
- Use gather_facts: no when not needed
- Use –limit for targeting specific hosts