Ansible has revolutionized the way IT professionals manage infrastructure, making complex automation tasks simple and efficient. As an agentless automation platform, Ansible enables you to configure systems, deploy software, and orchestrate advanced IT tasks across multiple servers simultaneously.
What is Ansible?
Ansible is an open-source automation tool that simplifies configuration management, application deployment, and task automation. Unlike other automation tools, Ansible doesn’t require agents on target machines, using SSH for Linux/Unix systems and WinRM for Windows systems.
Key Features of Ansible
- Agentless Architecture: No need to install software on managed nodes
- Simple YAML Syntax: Easy-to-read playbooks written in YAML
- Idempotent Operations: Safe to run multiple times without side effects
- Extensive Module Library: 3000+ built-in modules for various tasks
- Push-based Model: Control node pushes configurations to managed nodes
Installing Ansible on Linux
Ubuntu/Debian Installation
# Update package index
sudo apt update
# Install Ansible
sudo apt install ansible -y
# Verify installation
ansible --version
CentOS/RHEL Installation
# Install EPEL repository
sudo yum install epel-release -y
# Install Ansible
sudo yum install ansible -y
# For RHEL 8/CentOS 8
sudo dnf install ansible -y
Installation via Pip
# Install pip if not available
sudo apt install python3-pip -y
# Install Ansible via pip
pip3 install ansible
# Verify installation
ansible --version
Ansible Architecture and Components
Control Node
The machine where Ansible is installed and from which all tasks and playbooks are executed. This is typically your local machine or a dedicated management server.
Managed Nodes
The target machines that Ansible manages. These don’t require Ansible installation, just SSH access and Python.
Inventory
A file listing all managed nodes, which can be static (INI or YAML format) or dynamic (scripts that generate inventory).
Modules
Units of code that Ansible executes on managed nodes. Each module performs a specific task like installing packages or copying files.
Playbooks
YAML files containing a series of tasks to be executed on managed nodes in a specific order.
Setting Up Your First Ansible Environment
Creating an Inventory File
Create a basic inventory file named hosts:
[webservers]
web1.example.com
web2.example.com
192.168.1.10
[databases]
db1.example.com
db2.example.com
[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
Testing Connectivity
# Test connection to all hosts
ansible all -m ping -i hosts
# Expected output:
# web1.example.com | SUCCESS => {
# "ansible_facts": {
# "discovered_interpreter_python": "/usr/bin/python3"
# },
# "changed": false,
# "ping": "pong"
# }
Ansible Ad-hoc Commands
Ad-hoc commands are one-liners for quick tasks without writing playbooks.
Basic Ad-hoc Command Structure
ansible [pattern] -m [module] -a "[module options]" -i [inventory]
Common Ad-hoc Commands Examples
# Check disk space on all servers
ansible all -m shell -a "df -h" -i hosts
# Install a package on web servers
ansible webservers -m apt -a "name=nginx state=present" -i hosts --become
# Restart a service
ansible webservers -m service -a "name=nginx state=restarted" -i hosts --become
# Copy a file to all servers
ansible all -m copy -a "src=/tmp/file.txt dest=/tmp/file.txt" -i hosts
# Gather facts about systems
ansible all -m setup -i hosts
Writing Your First Ansible Playbook
Basic Playbook Structure
Create a file named webserver-setup.yml:
---
- name: Configure Web Servers
hosts: webservers
become: yes
vars:
http_port: 80
domain_name: example.com
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install Nginx
apt:
name: nginx
state: present
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: yes
- name: Create web directory
file:
path: /var/www/{{ domain_name }}
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Copy index.html
copy:
content: |
<html>
<head><title>Welcome to {{ domain_name }}</title></head>
<body>
<h1>Hello from {{ inventory_hostname }}</h1>
<p>This server is managed by Ansible</p>
</body>
</html>
dest: /var/www/{{ domain_name }}/index.html
owner: www-data
group: www-data
mode: '0644'
Running the Playbook
# Execute the playbook
ansible-playbook webserver-setup.yml -i hosts
# Run with verbose output
ansible-playbook webserver-setup.yml -i hosts -v
# Check what would change (dry run)
ansible-playbook webserver-setup.yml -i hosts --check
Essential Ansible Modules
System Modules
# Package management
- name: Install multiple packages
apt:
name:
- git
- curl
- vim
state: present
# Service management
- name: Manage services
service:
name: apache2
state: started
enabled: yes
# User management
- name: Create user
user:
name: developer
shell: /bin/bash
groups: sudo
append: yes
File Operations
# Copy files
- name: Copy configuration file
copy:
src: /local/path/config.conf
dest: /remote/path/config.conf
backup: yes
owner: root
group: root
mode: '0644'
# Template files
- name: Generate config from template
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
backup: yes
notify: restart nginx
# File operations
- name: Ensure directory exists
file:
path: /opt/myapp
state: directory
owner: app
group: app
mode: '0755'
Advanced Ansible Features
Using Variables
# Group variables file: group_vars/webservers.yml
nginx_port: 80
ssl_enabled: true
server_name: mywebsite.com
# Host variables file: host_vars/web1.example.com.yml
server_role: primary
backup_enabled: true
Handlers
---
- name: Configure Apache
hosts: webservers
become: yes
tasks:
- name: Update Apache configuration
template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify:
- restart apache
- reload firewall
handlers:
- name: restart apache
service:
name: httpd
state: restarted
- name: reload firewall
command: firewall-cmd --reload
Conditionals and Loops
# Conditionals
- name: Install package on Ubuntu only
apt:
name: nginx
state: present
when: ansible_distribution == "Ubuntu"
# Loops
- name: Create multiple users
user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlie
# Complex loops
- name: Install packages with specific versions
apt:
name: "{{ item.name }}={{ item.version }}"
state: present
loop:
- { name: nginx, version: "1.18*" }
- { name: mysql-server, version: "8.0*" }
Ansible Vault for Security
Creating Encrypted Variables
# Create encrypted file
ansible-vault create secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# Encrypt existing file
ansible-vault encrypt vars.yml
# Decrypt file
ansible-vault decrypt vars.yml
Using Vault in Playbooks
# secrets.yml (encrypted)
database_password: supersecret123
api_key: abc123def456
# playbook using vault
---
- name: Deploy application
hosts: app_servers
vars_files:
- secrets.yml
tasks:
- name: Configure database connection
template:
src: db_config.j2
dest: /opt/app/config/database.yml
vars:
db_pass: "{{ database_password }}"
Running Playbooks with Vault
# Run with vault password prompt
ansible-playbook deploy.yml --ask-vault-pass
# Run with vault password file
ansible-playbook deploy.yml --vault-password-file ~/.vault_pass
Ansible Roles
Creating a Role Structure
# Generate role structure
ansible-galaxy init webserver
# Directory structure created:
webserver/
├── defaults/
│ └── main.yml
├── files/
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
├── tests/
└── vars/
└── main.yml
Example Role Implementation
# roles/webserver/tasks/main.yml
---
- name: Install web server packages
package:
name: "{{ web_packages }}"
state: present
- name: Configure web server
template:
src: "{{ web_config_template }}"
dest: "{{ web_config_path }}"
notify: restart web service
- name: Ensure web service is running
service:
name: "{{ web_service_name }}"
state: started
enabled: yes
# roles/webserver/defaults/main.yml
---
web_packages:
- nginx
web_service_name: nginx
web_config_template: nginx.conf.j2
web_config_path: /etc/nginx/nginx.conf
Using Roles in Playbooks
---
- name: Setup web infrastructure
hosts: webservers
become: yes
roles:
- common
- webserver
- { role: database, database_type: mysql }
- role: monitoring
vars:
monitoring_enabled: true
Dynamic Inventories
AWS EC2 Dynamic Inventory
# Install boto3
pip install boto3
# Create aws_ec2.yml inventory plugin
plugin: aws_ec2
regions:
- us-east-1
- us-west-2
keyed_groups:
- key: tags.Environment
prefix: env
- key: instance_type
prefix: type
hostnames:
- tag:Name
- dns-name
Best Practices for Ansible
Directory Structure
ansible-project/
├── inventories/
│ ├── production/
│ │ ├── hosts.yml
│ │ └── group_vars/
│ └── staging/
│ ├── hosts.yml
│ └── group_vars/
├── roles/
├── playbooks/
├── group_vars/
├── host_vars/
└── ansible.cfg
Ansible Configuration
# ansible.cfg
[defaults]
inventory = inventories/production/hosts.yml
remote_user = ansible
host_key_checking = False
retry_files_enabled = False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 86400
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null
pipelining = True
Real-world Automation Examples
LAMP Stack Deployment
---
- name: Deploy LAMP Stack
hosts: webservers
become: yes
vars:
mysql_root_password: "{{ vault_mysql_root_password }}"
tasks:
- name: Update system packages
apt:
update_cache: yes
upgrade: dist
- name: Install LAMP packages
apt:
name:
- apache2
- mysql-server
- php
- php-mysql
- libapache2-mod-php
state: present
- name: Configure MySQL root password
mysql_user:
name: root
password: "{{ mysql_root_password }}"
login_user: root
login_password: "{{ mysql_root_password }}"
check_implicit_admin: yes
priv: "*.*:ALL,GRANT"
host: "{{ item }}"
loop:
- "{{ inventory_hostname }}"
- 127.0.0.1
- ::1
- localhost
- name: Create application database
mysql_db:
name: webapp
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
Troubleshooting Common Issues
Debug and Verbose Output
# Debug playbook execution
ansible-playbook -vvv playbook.yml
# Debug specific tasks
- name: Debug variable content
debug:
var: ansible_facts
verbosity: 2
# Register and display task output
- name: Check service status
command: systemctl status nginx
register: service_status
- name: Display service status
debug:
var: service_status.stdout_lines
Connection Testing
# Test SSH connectivity
ansible all -m ping -u ubuntu --private-key ~/.ssh/id_rsa
# Test with different connection methods
ansible all -m ping -c paramiko
ansible all -m ping -c ssh
Performance Optimization
Parallel Execution
# Increase parallelism in ansible.cfg
[defaults]
forks = 20
# Use strategy plugins
- name: Fast deployment
hosts: all
strategy: free
tasks:
- name: Quick task
shell: echo "Done on {{ inventory_hostname }}"
Fact Caching
# Enable fact caching in ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400
Conclusion
Ansible has transformed infrastructure management by making automation accessible and reliable. Its agentless architecture, simple YAML syntax, and extensive module library make it an ideal choice for organizations of all sizes. Whether you’re managing a few servers or thousands of nodes, Ansible provides the tools and flexibility needed to automate complex IT operations efficiently.
Start with simple ad-hoc commands and gradually build complex playbooks and roles. Remember to follow best practices, use version control for your automation code, and always test in non-production environments first. With consistent practice and proper implementation, Ansible will become an invaluable tool in your Linux administration toolkit.
- What is Ansible?
- Installing Ansible on Linux
- Ansible Architecture and Components
- Setting Up Your First Ansible Environment
- Ansible Ad-hoc Commands
- Writing Your First Ansible Playbook
- Essential Ansible Modules
- Advanced Ansible Features
- Ansible Vault for Security
- Ansible Roles
- Dynamic Inventories
- Best Practices for Ansible
- Real-world Automation Examples
- Troubleshooting Common Issues
- Performance Optimization
- Conclusion








