What is Ansible?
Ansible is an open-source automation platform that simplifies configuration management, application deployment, task automation, and orchestration. It uses a simple, human-readable language (YAML) and operates over SSH without requiring agents on target machines.
Key Benefits
- Agentless: No need to install software on target machines
- Simple: Uses YAML syntax that's easy to read and write
- Powerful: Can manage complex multi-tier deployments
- Idempotent: Safe to run multiple times without side effects
- Secure: Uses SSH and doesn't leave software installed on targets
Core Concepts
Inventory
A list of managed nodes (servers, network devices, etc.) that Ansible controls. Can be static files or dynamic sources.
Playbooks
YAML files containing a series of tasks to execute on target hosts. Think of them as automation scripts.
Tasks
Individual units of work that Ansible executes, like installing packages or copying files.
Modules
Reusable units of code that Ansible executes. Examples: yum
, copy
, service
, user
.
Roles
Way to organize playbooks and reuse code. Roles group related tasks, variables, files, and templates.
Variables
Data that can change how playbooks behave. Can be defined in multiple places with different precedence levels.
Installation
On Control Node (where you run Ansible)
Using pip:
pip install ansible
Using package managers:
# Ubuntu/Debian
sudo apt update
sudo apt install ansible
# CentOS/RHEL/Fedora
sudo dnf install ansible
# or
sudo yum install ansible
# macOS
brew install ansible
Basic Configuration
Ansible Configuration File
Create /etc/ansible/ansible.cfg
or ~/.ansible.cfg
:
[defaults]
inventory = ./inventory
host_key_checking = False
retry_files_enabled = False
gathering = smart
fact_caching = memory
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True
Inventory Management
Static Inventory
Basic inventory file (inventory
or hosts
):
# Individual hosts
web1.example.com
web2.example.com
# Groups
[webservers]
web1.example.com
web2.example.com
[databases]
db1.example.com
db2.example.com
# Groups of groups
[production:children]
webservers
databases
# Variables
[webservers:vars]
http_port=80
nginx_version=1.18
YAML inventory format:
all:
hosts:
web1.example.com:
web2.example.com:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
vars:
http_port: 80
databases:
hosts:
db1.example.com:
db2.example.com:
Dynamic Inventory
For cloud environments, you can use dynamic inventory scripts or plugins that query cloud APIs.
Ad-hoc Commands
Run single tasks without playbooks:
# Ping all hosts
ansible all -m ping
# Check uptime
ansible webservers -m command -a "uptime"
# Install package
ansible webservers -m yum -a "name=nginx state=present" --become
# Copy file
ansible webservers -m copy -a "src=/local/file dest=/remote/file"
# Start service
ansible webservers -m service -a "name=nginx state=started enabled=yes" --become
Playbooks
Basic Playbook Structure
---
- name: Web server setup
hosts: webservers
become: yes
vars:
http_port: 80
tasks:
- name: Install nginx
yum:
name: nginx
state: present
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yes
- name: Open firewall for HTTP
firewalld:
port: "{{ http_port }}/tcp"
permanent: yes
state: enabled
immediate: yes
Advanced Playbook Features
Handlers (run when notified):
tasks:
- name: Update nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
Conditionals:
tasks:
- name: Install Apache (RedHat family)
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Install Apache (Debian family)
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
Loops:
tasks:
- name: Create users
user:
name: "{{ item.name }}"
group: "{{ item.group }}"
shell: "{{ item.shell | default('/bin/bash') }}"
loop:
- { name: alice, group: admin }
- { name: bob, group: users }
- { name: charlie, group: users, shell: /bin/zsh }
Error handling:
tasks:
- name: Attempt risky operation
command: /bin/false
ignore_errors: yes
register: result
- name: Handle failure
debug:
msg: "Previous task failed, but continuing"
when: result is failed
Variables and Templates
Variable Precedence (highest to lowest)
- Command line values (
-e
) - Role defaults
- Inventory file variables
- Host facts
- Play variables
- Role variables
- Block variables
- Task variables
Using Templates (Jinja2)
Template file (templates/nginx.conf.j2
):
server {
listen {{ http_port }};
server_name {{ ansible_hostname }};
location / {
root /var/www/html;
index index.html;
}
}
Using the template:
- name: Deploy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/conf.d/default.conf
notify: restart nginx
Roles
Role Directory Structure
roles/
webserver/
tasks/
main.yml
handlers/
main.yml
templates/
nginx.conf.j2
files/
index.html
vars/
main.yml
defaults/
main.yml
meta/
main.yml
Using Roles in Playbooks
---
- name: Configure web servers
hosts: webservers
roles:
- common
- webserver
- { role: monitoring, when: monitoring_enabled }
Creating Roles with Ansible Galaxy
# Create role structure
ansible-galaxy init webserver
# Install role from Galaxy
ansible-galaxy install geerlingguy.nginx
# Install from requirements file
ansible-galaxy install -r requirements.yml
Common Modules
System Modules
-
yum
/apt
- Package management -
service
/systemd
- Service management -
user
/group
- User management -
cron
- Crontab management -
mount
- Filesystem mounting
File Modules
-
copy
- Copy files to remote hosts -
template
- Process Jinja2 templates -
file
- Set file attributes -
lineinfile
- Ensure line in file -
replace
- Replace text in files
Command Modules
-
command
- Run commands -
shell
- Run shell commands -
script
- Run local script on remote hosts -
raw
- Run raw SSH commands
Cloud Modules
-
ec2
- AWS EC2 management -
azure_rm_*
- Azure resource management -
gcp_*
- Google Cloud Platform
Best Practices
Playbook Organization
- Use descriptive names for plays and tasks
- Keep playbooks focused and modular
- Use roles for reusable code
- Group related functionality together
Variable Management
- Use group_vars and host_vars directories
- Keep sensitive data in Ansible Vault
- Use meaningful variable names
- Document variable purposes
Security
- Use Ansible Vault for sensitive data
- Limit sudo usage with
become_user
- Use SSH key authentication
- Regularly update Ansible
Testing
- Use
--check
mode for dry runs - Test on non-production first
- Use
ansible-lint
for syntax checking - Version control your playbooks
Ansible Vault
Encrypting Files
# Create encrypted file
ansible-vault create secret.yml
# Encrypt existing file
ansible-vault encrypt secret.yml
# Edit encrypted file
ansible-vault edit secret.yml
# View encrypted file
ansible-vault view secret.yml
# Decrypt file
ansible-vault decrypt secret.yml
Using Vaulted Variables
# Run playbook with vault password
ansible-playbook site.yml --ask-vault-pass
# Use password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass
Running Playbooks
Basic Execution
# Run playbook
ansible-playbook site.yml
# Dry run (check mode)
ansible-playbook site.yml --check
# Limit to specific hosts
ansible-playbook site.yml --limit webservers
# Use different inventory
ansible-playbook site.yml -i production
# Override variables
ansible-playbook site.yml -e "http_port=8080"
# Increase verbosity
ansible-playbook site.yml -vvv
Tags
tasks:
- name: Install packages
yum:
name: "{{ item }}"
state: present
loop: "{{ packages }}"
tags: packages
- name: Configure service
template:
src: config.j2
dest: /etc/service/config
tags: config
# Run only tagged tasks
ansible-playbook site.yml --tags packages
# Skip tagged tasks
ansible-playbook site.yml --skip-tags config
Troubleshooting
Common Issues
- SSH connectivity problems
- Permission errors (use
--become
) - Python interpreter issues
- Module not found errors
- YAML syntax errors
Debugging Commands
# Verbose output
ansible-playbook site.yml -vvv
# Debug specific host
ansible hostname -m setup
# Check inventory
ansible-inventory --list
# Syntax check
ansible-playbook site.yml --syntax-check
# List tasks
ansible-playbook site.yml --list-tasks
Example: Complete Web Server Deployment
---
- name: Deploy web application
hosts: webservers
become: yes
vars:
app_name: myapp
app_port: 8080
tasks:
- name: Update system packages
yum:
name: "*"
state: latest
tags: system
- name: Install required packages
yum:
name:
- nginx
- python3
- git
state: present
tags: packages
- name: Create application user
user:
name: "{{ app_name }}"
system: yes
shell: /bin/false
home: "/opt/{{ app_name }}"
create_home: yes
tags: users
- name: Clone application repository
git:
repo: https://github.com/company/myapp.git
dest: "/opt/{{ app_name }}/app"
version: main
become_user: "{{ app_name }}"
tags: deploy
- name: Install application dependencies
pip:
requirements: "/opt/{{ app_name }}/app/requirements.txt"
virtualenv: "/opt/{{ app_name }}/venv"
become_user: "{{ app_name }}"
tags: deploy
- name: Configure nginx
template:
src: nginx-app.conf.j2
dest: "/etc/nginx/conf.d/{{ app_name }}.conf"
notify: restart nginx
tags: config
- name: Configure systemd service
template:
src: app.service.j2
dest: "/etc/systemd/system/{{ app_name }}.service"
notify:
- reload systemd
- restart app
tags: config
- name: Start and enable services
service:
name: "{{ item }}"
state: started
enabled: yes
loop:
- nginx
- "{{ app_name }}"
tags: services
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
- name: reload systemd
systemd:
daemon_reload: yes
- name: restart app
service:
name: "{{ app_name }}"
state: restarted
This guide covers the fundamentals of Ansible. Start with simple playbooks and gradually incorporate more advanced features as you become comfortable with the basics.
Top comments (0)