15 min read

Ansible Fundamentals: Automating Infrastructure Configuration

A complete guide to Ansible automation covering agentless architecture, inventory management, playbooks, variables, Jinja2 templates, Ansible Vault, and best practices for reliable infrastructure configuration at scale.

Ansible Fundamentals: Configuration Management Made Simple

Key Takeaways

  • Ansible uses an agentless architecture, connecting via SSH to manage remote systems without installing software on target nodes.
  • Playbooks are YAML files that define automation workflows, with tasks executed in order against inventory hosts.
  • Variables, facts, and Jinja2 templates enable dynamic configuration that adapts to different environments.
  • Ansible Vault provides encryption for sensitive data like passwords and API keys in version-controlled code.

What is Ansible?

Ansible is an open-source automation platform that simplifies IT orchestration, configuration management, application deployment, and task automation. Created by Michael DeHaan in 2012 and now maintained by Red Hat, Ansible has become one of the most popular automation tools in the DevOps ecosystem.

Ansible architecture showing control node connecting to managed nodes via SSH with inventory, playbooks, and modules
Ansible architecture: Control node executes playbooks against managed nodes via SSH, with inventory defining targets and modules providing functionality

Agentless Architecture

Unlike tools such as Puppet or Chef that require agents installed on managed nodes, Ansible uses an agentless architecture. It connects to target systems via SSH (or WinRM for Windows) and executes tasks directly. This approach offers several advantages:

No Agent Installation

No software to install, update, or maintain on managed nodes. Only Python is required on target systems.

Push-Based Model

Changes are pushed from a control node when you run playbooks, giving you full control over timing.

SSH-Based Security

Uses existing SSH infrastructure and key-based authentication for secure connections.

Minimal Overhead

No daemon processes running on managed nodes consuming resources.

“Ansible is designed to be minimal in nature, consistent, secure, and highly reliable.”

— Ansible Documentation

Installing Ansible

Ansible can be installed on most Unix-like systems. The control node (where you run Ansible from) requires Python 3.9 or later. Managed nodes only need Python 2.7 or Python 3.5+.

Installation Methods

BASH
# Using pip (recommended)# On Ubuntu/Debian# On Ubuntu/Debian# On RHEL/CentOS/Fedora# On RHEL/CentOS/Fedora# On RHEL/CentOS/Fedora# On macOS with Homebrew# On macOS with Homebrew# Verify installation# Verify installationlation
ansible --version

Configuration File

Ansible looks for configuration in the following order:

  1. ANSIBLE_CONFIG environment variable
  2. ansible.cfg in current directory
  3. ~/.ansible.cfg in home directory
  4. /etc/ansible/ansible.cfg system-wide
ansible.cfgini
# ansible.cfg - Basic configuration
[defaults]
inventory = ./inventory
remote_user = ansible
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml

[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False

Inventory Management

The inventory defines the hosts and groups of hosts that Ansible manages. It can be a simple INI file, YAML, or dynamically generated from cloud providers and other sources.

INI Format Inventory

inventory/hosts.iniini
# inventory/hosts.ini# Ungrouped hosts# Web servers group# Database servers group# Database servers group# Database servers group# Database servers group# Group with variables# Group with variables# Group with variables# Group with variables# Parent group containing child groups# Parent group containing child groups# Host with connection variables# Host with connection variables# Host with connection variableses
[loadbalancers]
lb1.example.com ansible_user=admin ansible_ssh_private_key_file=~/.ssh/lb_key

YAML Format Inventory

inventory/hosts.ymlyaml
# inventory/hosts.yml
all:
  children:
    webservers:
      hosts:
        web1.example.com:
        web2.example.com:
      vars:
        http_port: 80
        max_clients: 200
    dbservers:
      hosts:
        db1.example.com:
          ansible_port: 2222
        db2.example.com:
    production:
      children:
        webservers:
        dbservers:

Dynamic Inventory

For cloud environments, Ansible supports dynamic inventory plugins that query APIs:

aws_ec2.ymlyaml
# aws_ec2.yml - AWS dynamic inventory
plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
  - eu-west-2
keyed_groups:
  - key: tags.Environment
    prefix: env
  - key: instance_type
    prefix: type
filters:
  instance-state-name: running
compose:
  ansible_host: public_ip_address

Ad-hoc Commands

Ad-hoc commands are quick, one-line commands for simple tasks. They're useful for one-off operations, testing, and quick system checks without writing a full playbook.

BASH
# Ping all hosts to verify connectivity# Run a command on all web servers# Using shell module for pipes and redirects# Using shell module for pipes and redirects# Copy a file to all hosts# Copy a file to all hosts# Copy a file to all hosts# Copy a file to all hosts# Install a package on all database servers# Install a package on all database servers# Restart a service# Restart a service# Restart a service# Restart a service# Restart a service# Restart a service# Gather facts about hosts# Gather facts about hosts# Gather facts about hosts# Gather facts about hosts# Filter specific facts# Filter specific facts# Run with elevated privileges# Run with elevated privileges# Limit to specific hosts# Limit to specific hosts# Limit to specific hosts# Limit to specific hosts# Run with specific user# Run with specific user# Run with specific userth specific user
ansible all -m ping -u deploy

Playbooks and YAML Syntax

Playbooks are the foundation of Ansible automation. They are YAML files that define a set of plays, each targeting specific hosts with a series of tasks to execute.

Basic Playbook Structure

site.ymlyaml
# site.yml - Main playbookbook
- name: Configure web servers
  hosts: webservers
  become: yes
  vars:
    http_port: 80
    document_root: /var/www/html

  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Start Nginx service
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Copy website content
      copy:
        src: files/index.html
        dest: "{{ document_root }}/index.html"
        owner: www-data
        group: www-data
        mode: '0644'

- name: Configure database servers
  hosts: dbservers
  become: yes
  
  tasks:
    - name: Install PostgreSQL
      apt:
        name:
          - postgresql
          - postgresql-contrib
        state: present

Running Playbooks

BASH
# Run a playbook# Run with specific inventory# Check mode (dry run)# Check mode (dry run)# Check mode (dry run)# Check mode (dry run)# Verbose output# Verbose output# Verbose output# Verbose output# Limit to specific hosts# Limit to specific hosts# Limit to specific hosts# More detail# Limit to specific hosts# Limit to specific hosts# Limit to specific hosts# Start at specific task# Start at specific task# Start at specific task# List all tasks# List all tasks# List all tasks# List all tasks# List all tasks# List all hosts# List all hosts# List all hostst all hosts
ansible-playbook site.yml --list-hosts

Tasks, Handlers, and Modules

Tasks

Tasks are the individual units of work in a playbook. Each task calls a module with specific arguments to perform an action on the target system.

YAML
tasks:
  # Simple task
  - name: Ensure Apache is installed
    apt:
      name: apache2
      state: present

  # Task with multiple arguments
  - name: Create application user
    user:
      name: appuser
      shell: /bin/bash
      groups: www-data
      append: yes
      create_home: yes

  # Task with register to capture output
  - name: Get server uptime
    command: uptime
    register: uptime_result

  - name: Display uptime
    debug:
      msg: "Uptime: {{ uptime_result.stdout }}"

  # Task with ignore_errors
  - name: Check if optional service exists
    command: systemctl status optional-service
    register: service_check
    ignore_errors: yes

Handlers

Handlers are special tasks that only run when notified by other tasks. They're commonly used to restart services after configuration changes.

YAML
---
- name: Configure Nginx
  hosts: webservers
  become: yes

  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present

    - name: Copy Nginx configuration
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Restart Nginx

    - name: Copy virtual host configuration
      template:
        src: templates/vhost.conf.j2
        dest: /etc/nginx/sites-available/mysite.conf
      notify:
        - Validate Nginx config
        - Restart Nginx

  handlers:
    - name: Validate Nginx config
      command: nginx -t

    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

Common Modules

Package Management

apt

Manages apt packages on Debian/Ubuntu

yum

Manages yum packages on RHEL/CentOS

dnf

Manages dnf packages on Fedora/RHEL 8+

pip

Manages Python packages via pip

File Management

file

Sets attributes of files and directories

copy

Copies files to remote locations

template

Renders Jinja2 templates to files

lineinfile

Manages lines in text files

System

service

Manages services (start, stop, restart)

systemd

Controls systemd units

user

Manages user accounts

group

Manages groups

Cloud & Infrastructure

aws_ec2

Creates/terminates EC2 instances

azure_rm_virtualmachine

Manages Azure VMs

gcp_compute_instance

Manages GCP instances

docker_container

Manages Docker containers

Variables and Facts

Defining Variables

Variables in Ansible can be defined in multiple locations with different precedence levels. Understanding variable precedence is crucial for managing complex configurations.

YAML
# Variables in playbookbook
- name: Deploy application
  hosts: webservers
  vars:
    app_name: myapp
    app_port: 8080
    app_env: production
    
    # Complex variables
    database:
      host: db.example.com
      port: 5432
      name: myapp_db
    
    # List variable
    packages:
      - nginx
      - python3
      - git

  tasks:
    - name: Install packages
      apt:
        name: "{{ packages }}"
        state: present

    - name: Display database host
      debug:
        msg: "Database is at {{ database.host }}:{{ database.port }}"

Variable Files

YAML
# group_vars/webservers.yml# host_vars/web1.example.com.yml# host_vars/web1.example.com.yml# host_vars/web1.example.com.yml# Include variables in playbook# Include variables in playbook# Include variables in playbook playbook
- name: Configure servers
  hosts: webservers
  vars_files:
    - vars/common.yml
    - "vars/{{ ansible_os_family }}.yml"

Ansible Facts

Facts are system information automatically gathered from managed nodes when a playbook runs. They provide details about the OS, network, hardware, and more.

YAML
# Using facts in tasks# Conditional based on facts# Conditional based on facts# Conditional based on facts# Conditional based on facts# Conditional based on facts# Conditional based on facts# Conditional based on facts# Conditional based on facts# Conditional based on facts# Custom facts (place in /etc/ansible/facts.d/*.fact)# Custom facts (place in /etc/ansible/facts.d/*.fact)# Custom facts (place in /etc/ansible/facts.d/*.fact)# Custom facts (place in /etc/ansible/facts.d/*.fact)# Custom facts (place in /etc/ansible/facts.d/*.fact)# Custom facts (place in /etc/ansible/facts.d/*.fact)# /etc/ansible/facts.d/app.fact# Access custom facts# Access custom facts# Access custom facts facts
- debug:
    msg: "App version: {{ ansible_local.app.application.version }}"

Conditionals and Loops

Conditionals with when

YAML
tasks:
  # Simple condition
  - name: Install Apache on Debian
    apt:
      name: apache2
      state: present
    when: ansible_os_family == "Debian"

  # Multiple conditions (AND)
  - name: Restart service if config changed and in production
    service:
      name: myapp
      state: restarted
    when:
      - config_changed | bool
      - env == "production"

  # OR condition
  - name: Install on Debian or Ubuntu
    apt:
      name: nginx
      state: present
    when: ansible_distribution == "Debian" or ansible_distribution == "Ubuntu"

  # Condition based on registered variable
  - name: Check if file exists
    stat:
      path: /etc/myapp/config.yml
    register: config_file

  - name: Create config if missing
    template:
      src: config.yml.j2
      dest: /etc/myapp/config.yml
    when: not config_file.stat.exists

  # Condition with failed_when
  - name: Run command that might fail
    command: /opt/myapp/check.sh
    register: check_result
    failed_when: "'CRITICAL' in check_result.stdout"

Loops

YAML
tasks:
  # Simple loop
  - name: Install multiple packages
    apt:
      name: "{{ item }}"
      state: present
    loop:
      - nginx
      - python3
      - git
      - curl

  # Loop with dictionaries
  - name: Create multiple users
    user:
      name: "{{ item.name }}"
      groups: "{{ item.groups }}"
      shell: "{{ item.shell | default('/bin/bash') }}"
    loop:
      - { name: 'alice', groups: 'admin' }
      - { name: 'bob', groups: 'developers' }
      - { name: 'charlie', groups: 'developers,docker' }

  # Loop with index
  - name: Create numbered files
    file:
      path: "/tmp/file_{{ index }}.txt"
      state: touch
    loop:
      - one
      - two
      - three
    loop_control:
      index_var: index

  # Loop with dict2items
  - name: Set sysctl values
    sysctl:
      name: "{{ item.key }}"
      value: "{{ item.value }}"
      state: present
    loop: "{{ sysctl_settings | dict2items }}"
    vars:
      sysctl_settings:
        net.ipv4.ip_forward: 1
        vm.swappiness: 10

  # Nested loops with subelements
  - name: Add SSH keys for users
    authorized_key:
      user: "{{ item.0.name }}"
      key: "{{ item.1 }}"
    loop: "{{ users | subelements('ssh_keys') }}"
    vars:
      users:
        - name: alice
          ssh_keys:
            - "ssh-rsa AAAA... alice@laptop"
            - "ssh-rsa AAAA... alice@desktop"

Templates with Jinja2

Ansible uses Jinja2 templating to dynamically generate configuration files. Templates allow you to create flexible configurations that adapt to different hosts and environments.

Template Example

{# templates/nginx.conf.j2 #}
# Managed by Ansible - Do not edit manually
user {{ nginx_user | default('www-data') }};
worker_processes {{ ansible_processor_cores }};
pid /run/nginx.pid;

events {
    worker_connections {{ worker_connections | default(1024) }};
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Virtual hosts
{% for vhost in virtual_hosts %}
    server {
        listen {{ vhost.port | default(80) }};
        server_name {{ vhost.server_name }};
        root {{ vhost.document_root }};

{% if vhost.ssl_enabled | default(false) %}
        listen 443 ssl;
        ssl_certificate {{ vhost.ssl_cert }};
        ssl_certificate_key {{ vhost.ssl_key }};
{% endif %}

{% for location in vhost.locations | default([]) %}
        location {{ location.path }} {
            {{ location.config }}
        }
{% endfor %}
    }
{% endfor %}
}

Common Jinja2 Features

{# Variables #}
{{ variable_name }}
{{ dict.key }}
{{ list[0] }}

{# Filters #}
{{ name | upper }}
{{ list | join(', ') }}
{{ value | default('fallback') }}
{{ path | basename }}
{{ content | b64encode }}
{{ timestamp | to_datetime }}

{# Conditionals #}
{% if environment == 'production' %}
DEBUG = False
{% elif environment == 'staging' %}
DEBUG = True
{% else %}
DEBUG = True
{% endif %}

{# Loops #}
{% for item in items %}
- {{ item.name }}: {{ item.value }}
{% endfor %}

{# Loop with conditions #}
{% for user in users if user.active %}
{{ user.name }}
{% endfor %}

{# Comments #}
{# This is a comment and won't appear in output #}

{# Set variables #}
{% set my_var = 'value' %}

{# Whitespace control #}
{%- if condition -%}
no extra whitespace
{%- endif -%}

Ansible Vault for Secrets

Ansible Vault provides encryption for sensitive data such as passwords, API keys, and certificates. This allows you to store secrets in version control safely.

Vault Commands

# Create a new encrypted file
ansible-vault create secrets.yml

# Encrypt an existing file
ansible-vault encrypt vars/production.yml

# Decrypt a file (permanently)
ansible-vault decrypt vars/production.yml

# View encrypted file contents
ansible-vault view secrets.yml

# Edit an encrypted file
ansible-vault edit secrets.yml

# Change encryption password
ansible-vault rekey secrets.yml

# Encrypt a string (inline)
ansible-vault encrypt_string 'mysecretpassword' --name 'db_password'

# Run playbook with vault password prompt
ansible-playbook site.yml --ask-vault-pass

# Run with password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass

Using Encrypted Variables

# vars/secrets.yml (encrypted)
---
db_password: supersecretpassword
api_key: abc123xyz789
ssl_private_key: |
  -----BEGIN PRIVATE KEY-----
  MIIEvQIBADANBgkqhkiG9...
  -----END PRIVATE KEY-----

# Using inline encrypted strings in a file
# group_vars/production.yml
---
app_name: myapp
db_host: db.example.com
db_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  61626364656667686970...

# Playbook using secrets
- name: Configure database
  hosts: dbservers
  vars_files:
    - vars/secrets.yml
  
  tasks:
    - name: Configure database user
      postgresql_user:
        name: appuser
        password: "{{ db_password }}"
        state: present

Security Best Practice

Store your vault password file outside of version control and protect it with appropriate file permissions (600). Consider using a password manager or secrets management service for team environments.

Best Practices

Following best practices ensures your Ansible automation is maintainable, secure, and reliable across environments.

🔄

Use Roles for Reusability

Organise playbooks into roles for better structure, reusability, and maintenance.

♻️

Idempotency Always

Ensure tasks produce the same result whether run once or multiple times.

🔐

Use Ansible Vault

Never store secrets in plain text. Always encrypt sensitive data with Vault.

📚

Version Control Everything

Store playbooks, roles, and inventory in Git for tracking and collaboration.

🏷️

Use Descriptive Names

Give tasks, plays, and variables meaningful names for readability.

🧪

Test Before Production

Use --check mode and molecule for testing before applying to production.

Recommended Directory Structure

ansible-project/
├── ansible.cfg
├── inventory/
│   ├── production/
│   │   ├── hosts.yml
│   │   ├── group_vars/
│   │   │   ├── all.yml
│   │   │   └── webservers.yml
│   │   └── host_vars/
│   │       └── web1.example.com.yml
│   └── staging/
│       └── hosts.yml
├── playbooks/
│   ├── site.yml
│   ├── webservers.yml
│   └── dbservers.yml
├── roles/
│   ├── common/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   ├── handlers/
│   │   │   └── main.yml
│   │   ├── templates/
│   │   ├── files/
│   │   ├── vars/
│   │   │   └── main.yml
│   │   └── defaults/
│   │       └── main.yml
│   ├── nginx/
│   └── postgresql/
├── vars/
│   └── secrets.yml  # encrypted
└── requirements.yml  # role dependencies

Using Roles

# roles/nginx/tasks/main.yml
---
- name: Install Nginx
  apt:
    name: nginx
    state: present
  notify: Restart Nginx

- name: Configure Nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: Restart Nginx

- name: Enable Nginx service
  service:
    name: nginx
    enabled: yes
    state: started

# roles/nginx/handlers/main.yml
---
- name: Restart Nginx
  service:
    name: nginx
    state: restarted

# playbooks/webservers.yml - Using the role
---
- name: Configure web servers
  hosts: webservers
  become: yes
  roles:
    - common
    - nginx
    - { role: app, tags: ['app', 'deploy'] }

Troubleshooting

Common issues and solutions when working with Ansible.

SSH connection failures

Error: “Failed to connect to the host via ssh” or “Permission denied”

Common causes:

  • SSH key not added to ssh-agent
  • Wrong username in inventory
  • Host key verification failure
  • Firewall blocking SSH port

Solution:

# Test SSH connection directly
ssh -vvv user@hostname

# Add key to ssh-agent
eval $(ssh-agent)
ssh-add ~/.ssh/id_rsa

# Disable host key checking (dev only)
export ANSIBLE_HOST_KEY_CHECKING=False

# Or in ansible.cfg
[defaults]
host_key_checking = False

# Specify SSH options in inventory
[webservers]
web1 ansible_host=10.0.0.1 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/mykey

Module not found or Python issues

Error: “Module failure” or “/usr/bin/python: not found”

Common causes:

  • Python not installed on target host
  • Python path different than expected
  • Missing Python dependencies for module

Solution:

# Specify Python interpreter
[webservers:vars]
ansible_python_interpreter=/usr/bin/python3

# Install Python using raw module (bootstrap)
- name: Install Python
  raw: apt-get update && apt-get install -y python3
  become: yes

# Use auto-discovery (Ansible 2.8+)
[defaults]
interpreter_python = auto_silent

Variable precedence confusion

Symptoms: Variables not having expected values, overrides not working

Common causes:

  • Variable defined in multiple places
  • Not understanding precedence order
  • Using wrong variable scope

Variable precedence (lowest to highest):

# Precedence order (later overrides earlier):
# 1. role defaults
# 2. inventory vars
# 3. inventory group_vars
# 4. inventory host_vars  
# 5. playbook group_vars
# 6. playbook host_vars
# 7. host facts
# 8. play vars
# 9. role vars
# 10. block vars
# 11. task vars
# 12. set_facts
# 13. extra vars (-e) <-- HIGHEST

# Debug variable value and source
- debug:
    var: my_variable
    verbosity: 0

# Run with increased verbosity
ansible-playbook site.yml -vvv

Idempotency issues - tasks always report changed

Symptoms: Tasks report “changed” on every run when nothing should change

Common causes:

  • Using shell/command modules without creates/removes
  • Template has dynamic content (timestamps)
  • File permissions or ownership constantly changing

Solution:

# Bad - always reports changed
- name: Create directory
  command: mkdir -p /app/data

# Good - idempotent
- name: Create directory
  file:
    path: /app/data
    state: directory

# For shell commands, use creates/removes
- name: Run migration
  command: python manage.py migrate
  args:
    creates: /app/.migrated

# Use changed_when for custom logic
- name: Check status
  command: systemctl is-active nginx
  register: nginx_status
  changed_when: false
  failed_when: false

Vault password issues

Error: “Attempting to decrypt but no vault secrets found”

Common causes:

  • Vault password not provided
  • Wrong vault password
  • Mixing vault IDs

Solution:

# Provide vault password
ansible-playbook site.yml --ask-vault-pass

# Use password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass

# Configure in ansible.cfg
[defaults]
vault_password_file = ~/.vault_pass

# Re-encrypt with correct password
ansible-vault rekey secrets.yml

# View encrypted file content
ansible-vault view secrets.yml

Conclusion

Ansible offers an approachable way to automate infrastructure configuration and application deployment. Its agentless architecture, human-readable YAML syntax, and extensive module library make it a practical choice for teams of all sizes.

The key concepts covered in this guide—inventory management, playbooks, tasks and handlers, variables and facts, conditionals and loops, Jinja2 templates, and Ansible Vault—form the foundation for building reliable automation solutions.

As you progress, explore Ansible Galaxy for community roles, investigate Ansible Tower/AWX for enterprise features, and consider integrating Ansible with your CI/CD pipelines for continuous infrastructure deployment.

Frequently Asked Questions

Ansible is an open-source automation platform for configuration management, application deployment, and task automation. It uses an agentless architecture, connecting to target systems via SSH (or WinRM for Windows) and executing tasks directly without requiring any software to be installed on managed nodes. Ansible reads playbooks written in YAML and translates them into commands that are executed on remote systems.

An Ansible playbook is a YAML file that defines automation workflows. It contains one or more "plays" that map groups of hosts to tasks. Each task calls an Ansible module with specific arguments to perform actions like installing packages, copying files, or managing services. Playbooks are executed in order and are designed to be idempotent, meaning running them multiple times produces the same result.

Ansible and Terraform serve different primary purposes. Ansible excels at configuration management and application deployment – configuring servers, installing software, and managing services on existing infrastructure. Terraform is an infrastructure provisioning tool that creates and manages cloud resources like VMs, networks, and databases. While there is some overlap, they are often used together: Terraform provisions the infrastructure, and Ansible configures it.

Ansible inventory is a file or script that defines the hosts and groups of hosts that Ansible manages. It can be a static file in INI or YAML format listing hostnames and IP addresses, or a dynamic inventory that queries cloud provider APIs to discover resources. Inventory also allows you to define variables for hosts and groups, such as connection settings, ports, and custom configuration values.

Ansible Vault encrypts sensitive data like passwords, API keys, and certificates so they can be safely stored in version control. Use "ansible-vault create" to create encrypted files, "ansible-vault encrypt" to encrypt existing files, and "ansible-vault edit" to modify them. When running playbooks, provide the vault password with --ask-vault-pass or --vault-password-file. You can also encrypt individual strings inline using "ansible-vault encrypt_string".

Ansible facts are system information automatically gathered from managed nodes when a playbook runs. They include details about the operating system, network interfaces, hardware, memory, CPU, and more. Facts are stored as variables (e.g., ansible_distribution, ansible_memtotal_mb) and can be used in tasks, conditionals, and templates to create dynamic configurations that adapt to each host's characteristics.

References & Further Reading

Related Articles

Ayodele Ajayi

Principal Engineer based in Kent, UK. Specialising in security governance, cloud architecture, and platform engineering.