Top Ansible Interview Questions You Need to Know

Ansible Interview Questions — Comprehensive Guide


1. Basic / Fundamental Questions

Q: What is idempotency in Ansible and why does it matter? Idempotency means running the same playbook multiple times produces the same result without unintended side effects. If nginx is already installed, the yum module won’t reinstall it. This makes Ansible safe to run repeatedly — useful for drift correction and CI/CD pipelines.

Q: What is the difference between a Play and a Playbook?

  • Play — maps a group of hosts to a set of tasks
  • Playbook — a YAML file containing one or more plays
# This entire file = Playbook
- name: Play 1 - Configure webservers # ← Play
hosts: webservers
tasks:
- name: Install nginx
yum:
name: nginx
state: present
- name: Play 2 - Configure databases # ← Play
hosts: dbservers
tasks:
- name: Install postgresql
yum:
name: postgresql
state: present

Q: What is inventory and what types exist?

# Static inventory — INI format
[webservers]
web1.example.com
web2.example.com ansible_user=ec2-user ansible_port=2222
[dbservers]
db1.example.com
db2.example.com
[production:children] # Group of groups
webservers
dbservers
[webservers:vars] # Group variables
http_port=80

# Static inventory — YAML format
all:
children:
webservers:
hosts:
web1.example.com:
ansible_user: ec2-user
web2.example.com:
dbservers:
hosts:
db1.example.com:
db2.example.com:
# Dynamic inventory — script or plugin
ansible-inventory -i aws_ec2.yml --list # AWS dynamic inventory
ansible-inventory -i inventory/ --graph # View inventory tree

2. Variables & Precedence

Q: What is the variable precedence order in Ansible?

From lowest to highest priority (higher overrides lower):

1. Role defaults (roles/x/defaults/main.yml)
2. Inventory file vars
3. Inventory group_vars/all
4. Playbook group_vars/all
5. Inventory group_vars/*
6. Playbook group_vars/*
7. Inventory host_vars/*
8. Playbook host_vars/*
9. Host facts (gathered)
10. Play vars
11. Play vars_prompt
12. Play vars_files
13. Role vars (roles/x/vars/main.yml)
14. Block vars
15. Task vars
16. include_vars
17. set_facts / registered vars
18. Role params
19. Extra vars (-e) ← HIGHEST PRIORITY
# Extra vars always win
ansible-playbook site.yml -e "env=prod db_password=secret"

Q: What are magic variables? Special variables Ansible populates automatically:

- debug:
msg: |
Current host: {{ inventory_hostname }}
Short hostname: {{ inventory_hostname_short }}
All groups: {{ group_names }}
All hosts: {{ groups['all'] }}
Playbook dir: {{ playbook_dir }}
Role path: {{ role_path }}
Hostvars of web1: {{ hostvars['web1']['ansible_default_ipv4'] }}

Q: What is set_fact and when do you use it?

- name: Set derived variable
set_fact:
app_url: "https://{{ ansible_hostname }}:{{ app_port }}/{{ app_path }}"
is_production: "{{ env == 'prod' }}"
cacheable: true # Persists fact across plays in same run

3. Tasks & Modules

Q: What is the difference between command, shell, and raw modules?

ModuleUse CaseNotes
commandRun commands without shell featuresNo pipes, redirects, variables
shellRun commands with shell featuresSupports pipes, &&, |, globs
rawLow-level SSH — no Python neededFor bootstrapping, network devices
# command — safe, no shell interpretation
- command: /usr/bin/systemctl restart nginx
# shell — needed for pipes/redirects
- shell: ps aux | grep nginx | wc -l
# raw — no Python required on target
- raw: apt-get install -y python3

Prefer built-in modules over command/shell for idempotency.

Q: Explain commonly used modules.

# File management
- ansible.builtin.copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
- ansible.builtin.template:
src: templates/app.conf.j2
dest: /etc/app/app.conf
- ansible.builtin.file:
path: /var/log/myapp
state: directory # absent | directory | file | link | touch
mode: '0755'
# Package management
- ansible.builtin.yum:
name:
- nginx
- python3
state: present # present | absent | latest
- ansible.builtin.apt:
name: nginx
state: present
update_cache: true
# Service management
- ansible.builtin.service:
name: nginx
state: started # started | stopped | restarted | reloaded
enabled: true
# User management
- ansible.builtin.user:
name: appuser
uid: 1001
groups: wheel
shell: /bin/bash
state: present
# Fetch file from remote
- ansible.builtin.fetch:
src: /var/log/app.log
dest: /local/logs/
flat: true

Q: What is the register keyword? Captures module output into a variable:

- name: Check if file exists
ansible.builtin.stat:
path: /etc/nginx/nginx.conf
register: nginx_conf_stat
- name: Show result
debug:
msg: "File exists: {{ nginx_conf_stat.stat.exists }}"
- name: Only run if file missing
ansible.builtin.copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
when: not nginx_conf_stat.stat.exists

4. Conditionals, Loops & Filters

Q: How do conditionals work in Ansible?

# Simple condition
- name: Install on RedHat only
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
# Multiple conditions
- name: Install on RHEL 8+
yum:
name: nginx
state: present
when:
- ansible_os_family == "RedHat"
- ansible_distribution_major_version | int >= 8
# OR condition
- name: Run on web or proxy nodes
service:
name: nginx
state: started
when: >
inventory_hostname in groups['webservers'] or
inventory_hostname in groups['proxies']
# Check registered result
- name: Restart only if config changed
service:
name: nginx
state: restarted
when: config_result.changed
# Check if variable is defined
- name: Use custom port if defined
debug:
msg: "Port is {{ custom_port }}"
when: custom_port is defined

Q: Explain loops in Ansible.

# Simple loop
- name: Create multiple users
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- carol
# Loop with dictionaries
- name: Create users with attributes
ansible.builtin.user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
loop:
- { name: alice, uid: 1001, groups: wheel }
- { name: bob, uid: 1002, groups: docker }
# Loop with index
- name: Show item and index
debug:
msg: "Item {{ ansible_loop.index }}: {{ item }}"
loop: "{{ packages }}"
loop_control:
label: "{{ item }}" # Cleaner output
index_var: idx
# Loop over dict
- name: Set sysctl values
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
loop: "{{ sysctl_settings | dict2items }}"
vars:
sysctl_settings:
vm.swappiness: 10
net.core.somaxconn: 65535
# until loop — retry until condition met
- name: Wait for service to respond
uri:
url: http://localhost:8080/health
status_code: 200
register: result
until: result.status == 200
retries: 10
delay: 5

Q: What are Jinja2 filters and give examples?

vars:
my_list: [3, 1, 4, 1, 5, 9]
my_string: " Hello World "
my_dict: {a: 1, b: 2}
packages: ["nginx", "python3"]
tasks:
- debug:
msg:
# String filters
upper: "{{ my_string | upper }}"
lower: "{{ my_string | lower }}"
trim: "{{ my_string | trim }}"
replace: "{{ my_string | replace('World', 'Ansible') }}"
default: "{{ undefined_var | default('fallback') }}"
# List filters
sorted: "{{ my_list | sort }}"
unique: "{{ my_list | unique }}"
joined: "{{ packages | join(', ') }}"
first: "{{ my_list | first }}"
last: "{{ my_list | last }}"
length: "{{ my_list | length }}"
# Type conversion
int_val: "{{ '42' | int }}"
bool_val: "{{ 'true' | bool }}"
list_val: "{{ my_dict | dict2items }}"
# Math
max_val: "{{ my_list | max }}"
min_val: "{{ my_list | min }}"
# Conditional
ternary: "{{ (env == 'prod') | ternary('production', 'staging') }}"
# Path
basename: "{{ '/etc/nginx/nginx.conf' | basename }}"
dirname: "{{ '/etc/nginx/nginx.conf' | dirname }}"

5. Handlers, Tags & Error Handling

Q: How do handlers work?

tasks:
- name: Update nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify:
- Reload nginx
- Send alert
- name: Update SSL cert
copy:
src: cert.pem
dest: /etc/ssl/cert.pem
notify: Reload nginx # Same handler — only fires ONCE
handlers:
- name: Reload nginx
service:
name: nginx
state: reloaded
- name: Send alert
uri:
url: https://hooks.slack.com/...
method: POST
body: '{"text": "nginx config updated"}'

Handlers run once at the end of a play, even if notified multiple times.

Q: How do you force handlers to run immediately?

tasks:
- name: Update config
template:
src: app.conf.j2
dest: /etc/app/app.conf
notify: Restart app
- name: Flush handlers now
meta: flush_handlers # Runs handlers immediately here
- name: Run health check # Now runs after restart
uri:
url: http://localhost/health

Q: How do tags work?

tasks:
- name: Install packages
yum:
name: nginx
state: present
tags:
- install
- packages
- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
tags:
- configure
- nginx
- name: Start nginx
service:
name: nginx
state: started
tags:
- start
- always # 'always' tag runs even with --tags filter
# Run only tagged tasks
ansible-playbook site.yml --tags install
ansible-playbook site.yml --tags "install,configure"
# Skip tagged tasks
ansible-playbook site.yml --skip-tags configure
# List all tags in a playbook
ansible-playbook site.yml --list-tags

Q: How do you handle errors in Ansible?

# Ignore errors and continue
- name: Try to stop service (may not exist)
service:
name: myapp
state: stopped
ignore_errors: true
# Custom failure condition
- name: Run script
command: /usr/local/bin/check-status.sh
register: result
failed_when:
- result.rc != 0
- '"CRITICAL" in result.stdout'
# Custom changed condition
- name: Run idempotent script
command: /usr/local/bin/configure.sh
register: result
changed_when: '"already configured" not in result.stdout'
# Blocks for error handling (try/catch/finally)
- block:
- name: Try risky operation
command: /usr/bin/risky-script.sh
- name: Another task in block
service:
name: myapp
state: started
rescue:
- name: Handle the error
debug:
msg: "Something went wrong: {{ ansible_failed_result }}"
- name: Rollback
command: /usr/bin/rollback.sh
always:
- name: Always run cleanup
file:
path: /tmp/lockfile
state: absent

6. Advanced Topics

Q: What is delegate_to and run_once?

# Run task on a DIFFERENT host
- name: Add to load balancer
uri:
url: http://lb.example.com/api/add
method: POST
body: '{"host": "{{ inventory_hostname }}"}'
delegate_to: localhost # Run on control node
- name: Take DB backup before deploy
command: pg_dump mydb > /backup/pre-deploy.sql
delegate_to: db1.example.com # Run on DB server
# Run only once across all hosts
- name: Create database schema
command: psql -f schema.sql
run_once: true # Runs on first host in play only
delegate_to: db1.example.com

Q: Difference between include_tasks and import_tasks?

import_tasksinclude_tasks
ParsedAt playbook load time (static)At runtime (dynamic)
Supports loopsNoYes
Supports conditionalsLimited (on the import)Full
Tags visibilityTags visible at startTags not visible until runtime
Use caseAlways-needed task filesConditional or looped includes
# import — static, parsed upfront
- import_tasks: tasks/setup.yml # No loop/conditional on the import
# include — dynamic, parsed at runtime
- include_tasks: "tasks/{{ ansible_os_family }}.yml" # Dynamic path OK
when: setup_needed
loop: "{{ environments }}"

Q: What is ansible-pull and when is it used? Reverses the push model — nodes pull their configuration from a Git repo. Used for:

  • Large fleets where push is impractical
  • Nodes behind firewalls
  • Self-provisioning scenarios
ansible-pull -U https://github.com/myorg/ansible-config.git \
-C main \
--inventory localhost, \
local.yml

Q: What is a dynamic inventory plugin and how do you configure one?

# aws_ec2.yml — AWS dynamic inventory
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
- us-west-2
filters:
instance-state-name: running
tag:Environment: production
keyed_groups:
- key: tags.Role
prefix: role
- key: placement.region
prefix: region
hostnames:
- private-ip-address
compose:
ansible_host: private_ip_address
# Test dynamic inventory
ansible-inventory -i aws_ec2.yml --graph
ansible-inventory -i aws_ec2.yml --list

7. Ansible for OpenShift/Kubernetes

Q: How do you manage OCP resources with Ansible?

- name: Manage OpenShift resources
hosts: localhost
collections:
- kubernetes.core
- redhat.openshift
tasks:
- name: Create namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: my-app
labels:
environment: production
- name: Apply manifest from file
kubernetes.core.k8s:
state: present
src: /path/to/deployment.yaml
- name: Apply from template
kubernetes.core.k8s:
state: present
definition: "{{ lookup('template', 'deployment.j2') }}"
- name: Wait for deployment rollout
kubernetes.core.k8s_rollout_status:
name: my-deployment
namespace: my-app
timeout: 300
- name: Get pod info
kubernetes.core.k8s_info:
kind: Pod
namespace: my-app
label_selectors:
- app=my-app
register: pod_info
- name: Scale deployment
kubernetes.core.k8s_scale:
name: my-deployment
namespace: my-app
replicas: 5

8. Performance & Best Practices

Q: How do you speed up Ansible playbooks?

# ansible.cfg optimizations
[defaults]
# SSH pipelining — reduces SSH connections
pipelining = True
# Parallel execution
forks = 20
# Fact caching — don't re-gather every run
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
[ssh_connection]
# Reuse SSH connections
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
control_path_dir = /tmp/ansible-ssh
# Disable fact gathering when not needed
- hosts: webservers
gather_facts: false # Skip if you don't need facts
tasks: ...
# Gather only specific facts
- hosts: webservers
gather_facts: true
gather_subset:
- network
- hardware
- "!all" # Only gather what's listed

Q: What are Ansible best practices?

  • Use roles for reusable, testable code
  • Store secrets in Vault, never plaintext
  • Use requirements.yml for collections and roles
  • Use tags for selective execution
  • Prefix MCs with numbers for ordering (00-base, 99-custom)
  • Use handlers instead of always restarting services
  • Set become: true at play level, not task level
  • Use block/rescue/always for error handling
  • Test roles with Molecule
  • Pin collection versions in requirements.yml
  • Use --check (dry run) and --diff before applying changes
# Dry run
ansible-playbook site.yml --check --diff
# Syntax check
ansible-playbook site.yml --syntax-check
# List hosts that would be affected
ansible-playbook site.yml --list-hosts
# Step through tasks one at a time
ansible-playbook site.yml --step

9. Molecule — Role Testing

# Install
pip install molecule molecule-docker
# Initialize molecule in a role
cd roles/my-role
molecule init scenario --driver-name docker
# molecule/default/molecule.yml
driver:
name: docker
platforms:
- name: rhel8-instance
image: registry.access.redhat.com/ubi8/ubi
pre_build_image: true
privileged: true
provisioner:
name: ansible
playbooks:
converge: converge.yml
verify: verify.yml
verifier:
name: ansible
# molecule/default/verify.yml
- name: Verify
hosts: all
tasks:
- name: Check nginx is running
service_facts:
- name: Assert nginx is active
assert:
that:
- "'nginx' in services"
- "services['nginx'].state == 'running'"
- name: Check port 80 is listening
wait_for:
port: 80
timeout: 5
# Run full test cycle
molecule test
# Individual stages
molecule create # Spin up containers
molecule converge # Run the role
molecule verify # Run tests
molecule destroy # Tear down
molecule login # SSH into instance for debugging

Quick-Fire Round

  • when vs failed_when? when controls if a task runs; failed_when defines what counts as failure.
  • How to run a task on localhost? delegate_to: localhost or hosts: localhost.
  • What is ansible_facts? Auto-collected system info — OS, IP, memory, CPU.
  • Difference between notify and direct task? notify triggers handlers at end of play only if task changed; direct task always runs.
  • What is check mode? --check — dry run, no changes made, shows what would change.
  • What is become? Privilege escalation — equivalent to sudo. become: true + become_user: root.
  • How to encrypt a single variable? ansible-vault encrypt_string.
  • What is gather_facts: false good for? Speeds up plays that don’t need system facts.
  • What does any_errors_fatal: true do? Stops the entire play on any host failure.
  • What is the free strategy? Hosts run tasks as fast as they can without waiting for others (vs linear default).

Leave a Reply