Ansible Complete Guide — Agentless Configuration Management
Advertisement
Introduction
Why This Matters
Ansible is the most popular agentless configuration management tool, used by thousands of organizations to automate server provisioning, application deployment, and infrastructure configuration. Because it uses SSH and requires no agent installation on managed nodes, it is easy to adopt and integrates naturally into existing infrastructure. Its YAML-based playbooks are readable by non-developers, making it ideal for cross-functional DevOps teams.
Installation and Inventory
# Install Ansible (macOS)
pip3 install ansible
# Verify version
ansible --version
# Static inventory file
cat > inventory.ini << 'EOF'
[webservers]
web1.example.com ansible_user=ubuntu
web2.example.com ansible_user=ubuntu
[databases]
db1.example.com ansible_user=ubuntu ansible_port=2222
[production:children]
webservers
databases
[all:vars]
ansible_ssh_private_key_file=~/.ssh/prod_key
EOF
# Test connectivity
ansible all -i inventory.ini -m ping
# Run ad-hoc command
ansible webservers -i inventory.ini -m command -a "uptime"
ansible databases -i inventory.ini -m shell -a "df -h"Dynamic Inventory (AWS)
# Install AWS collection
ansible-galaxy collection install amazon.aws
# aws_ec2 dynamic inventory plugin
cat > aws_ec2.yaml << 'EOF'
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
filters:
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role
EOF
ansible-inventory -i aws_ec2.yaml --list
ansible all -i aws_ec2.yaml -m pingPlaybooks
---
# deploy-app.yml
- name: Deploy Node.js application
hosts: webservers
become: true
vars:
app_version: "{{ lookup('env', 'APP_VERSION') | default('latest') }}"
app_port: 3000
app_dir: /opt/my-app
tasks:
- name: Install Node.js dependencies
apt:
name:
- nodejs
- npm
- nginx
state: present
update_cache: true
- name: Create app directory
file:
path: "{{ app_dir }}"
state: directory
owner: ubuntu
mode: '0755'
- name: Copy application files
copy:
src: dist/
dest: "{{ app_dir }}/"
owner: ubuntu
mode: '0644'
notify: restart app
- name: Install npm dependencies
npm:
path: "{{ app_dir }}"
state: present
- name: Configure nginx
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/my-app
mode: '0644'
notify: reload nginx
- name: Enable nginx site
file:
src: /etc/nginx/sites-available/my-app
dest: /etc/nginx/sites-enabled/my-app
state: link
handlers:
- name: restart app
systemd:
name: my-app
state: restarted
- name: reload nginx
service:
name: nginx
state: reloadedRoles
Roles provide reusable, structured Ansible automation:
# Create role structure
ansible-galaxy init roles/webserver
# Directory structure
roles/webserver/
tasks/main.yml
handlers/main.yml
templates/
files/
vars/main.yml
defaults/main.yml
meta/main.yml# roles/webserver/tasks/main.yml
---
- name: Install web packages
apt:
name: "{{ webserver_packages }}"
state: present
update_cache: true
- name: Start and enable nginx
systemd:
name: nginx
state: started
enabled: true
- name: Deploy site configuration
template:
src: site.conf.j2
dest: "/etc/nginx/sites-available/{{ site_name }}"
notify: reload nginx
# roles/webserver/defaults/main.yml
---
webserver_packages:
- nginx
- curl
site_name: default
site_port: 80# Using the role in a playbook
---
- hosts: webservers
become: true
roles:
- role: webserver
vars:
site_name: my-app
site_port: 8080Variables and Vault
# group_vars/production/vars.yml
db_host: prod-db.example.com
db_port: 5432
app_env: production
# group_vars/production/vault.yml (encrypted)
# Create with: ansible-vault create group_vars/production/vault.yml
vault_db_password: "super-secret-password"
vault_api_key: "my-api-key"# Encrypt a file
ansible-vault encrypt group_vars/production/vault.yml
# Edit encrypted file
ansible-vault edit group_vars/production/vault.yml
# Run playbook with vault password
ansible-playbook deploy.yml --ask-vault-pass
ansible-playbook deploy.yml --vault-password-file ~/.vault_passCommon Mistakes
- Not using
become: truewhen tasks require root — tasks fail silently or with permission errors - Forgetting
update_cache: trueinapttasks when installing packages on fresh hosts - Using
commandmodule instead of specific modules (apt,service) — loses idempotency - Storing secrets as plain text in
group_vars— always encrypt sensitive data withansible-vault - Not testing playbooks with
--check(dry run) mode before running against production
Best Practices
- Use
ansible-lintin CI to catch style issues and potential bugs before they reach production - Prefer specific modules (
apt,yum,copy,template) overcommand/shellfor idempotency - Group hosts logically in inventory and use
group_varsandhost_varsfor variable scoping - Write roles for reusable components; publish to Ansible Galaxy for sharing across teams
- Always run
ansible-playbook --check(dry-run) before applying to production - Pin collection versions in
requirements.ymlto ensure reproducible automation
Key Takeaways
- Ansible uses SSH and Python — no agent required on managed nodes, making adoption very low friction
- Inventory can be static (
.ini/.yaml) or dynamic (cloud plugins for AWS, GCP, Azure) - Playbooks are idempotent by design — running the same playbook multiple times produces the same result
- Handlers only execute once at the end of a play, even if notified multiple times — ideal for service restarts
- Roles structure automation into reusable components with clear directory conventions
ansible-vaultencrypts sensitive variables at rest while keeping them in version controlgroup_varsandhost_varsallow scoping variables to inventory groups or individual hosts- The
--checkflag performs a dry run showing what would change without modifying any systems
Advertisement
Related reading
Ansible Configuration Management 2026 — Automate Server Setup and Deployments7 min readAI Tools for DevOps — Generate Dockerfiles, CI/CD Pipelines, and Kubernetes Manifests5 min readGitHub Actions Complete Guide 2026 — CI/CD Pipelines, Workflows, and Automation5 min readTerraform Infrastructure Guide 2026 — Infrastructure as Code for AWS, GCP, and Azure6 min readCI/CD Pipeline Design Guide 2026 — From Commit to Production in Under 15 Minutes6 min readPython Virtual Environments — The Right Way to Manage Dependencies5 min read