Configuration and Inventory

Last updated: June 29, 2025

My Configuration Journey with Ansible

When I first started using Ansible, I thought it was just about writing playbooks and running them. But I quickly learned that properly configuring Ansible and building effective inventories are crucial skills that separate beginners from effective automation engineers. In this article, I'll share what I've learned about Ansible configuration and inventory management, focusing on both Linux and Windows automation scenarios.

Ansible Configuration: The Control Center

Ansible's behavior is controlled by its configuration system. Over the years, I've found that understanding this system is essential for both simple tasks and complex enterprise deployments.

The Configuration Hierarchy

One of the first things that surprised me about Ansible was its configuration precedence. Ansible looks for configuration in several places, in the following order (from lowest to highest precedence):

  1. Default configuration embedded in the code

  2. /etc/ansible/ansible.cfg (global system-wide configuration)

  3. ~/.ansible.cfg (user's home directory configuration)

  4. ./ansible.cfg (configuration in the current directory)

  5. ANSIBLE_* environment variables

  6. Command-line options

This means that settings specified on the command line will override anything in configuration files. This hierarchy has saved me countless times when I needed to temporarily change a setting without modifying configuration files.

Creating Your Configuration File

For new projects, I typically start with a fresh ansible.cfg file in my project directory. You can generate a complete template with all available settings using:

ansible-config init --disabled > ansible.cfg

But for most of my projects, I keep it simple with just the essential settings:

[defaults]
inventory = ./inventory
remote_user = ansible
host_key_checking = False
timeout = 30

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

[ssh_connection]
pipelining = True

Essential Configuration Settings for Mixed Environments

When working with both Linux and Windows hosts, these are the configuration settings I've found most valuable:

For Linux Hosts

[defaults]
interpreter_python = auto_silent

This setting automatically detects the Python interpreter on remote systems without printing warnings. Essential when dealing with different Linux distributions that might have Python in different locations.

For Windows Hosts with SSH

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o PreferredAuthentications=publickey

These settings optimize SSH connections for both Linux and Windows hosts. With Windows embracing OpenSSH, I've found these settings improve connection stability and performance when automating Windows servers via SSH.

PowerShell Configuration

[powershell]
binary_module_suffix = .ps1

This setting helps Ansible properly handle PowerShell scripts when targeting Windows hosts over SSH. It ensures that script-based modules use the correct file extension, which is essential for PowerShell execution on Windows systems.

Inventory: The Foundation of Automation

Learning to structure my inventory properly was a game-changer for me. A well-organized inventory makes automation scalable and maintainable.

Basic Inventory Formats

Ansible supports multiple inventory formats, but I primarily use these two:

INI Format

# Simple groups
[web_servers]
web1.example.com
web2.example.com

[db_servers]
db1.example.com
db2.example.com

# OS-specific groups
[linux]
web1.example.com
db1.example.com

[windows]
web2.example.com
db2.example.com

YAML Format

all:
  children:
    web_servers:
      hosts:
        web1.example.com:
        web2.example.com:
    db_servers:
      hosts:
        db1.example.com:
        db2.example.com:
    linux:
      hosts:
        web1.example.com:
        db1.example.com:
    windows:
      hosts:
        web2.example.com:
        db2.example.com:

I personally prefer YAML format because it's more readable for complex structures and consistent with playbook syntax. However, for quick tasks, I still sometimes use INI format for its simplicity.

Host and Group Variables

One of my favorite Ansible features is the ability to assign variables to hosts and groups. This allows me to customize behavior without modifying playbooks.

For host-specific variables (in YAML format):

all:
  hosts:
    web1.example.com:
      http_port: 8080
      ansible_python_interpreter: /usr/bin/python3
    db1.example.com:
      db_port: 5432

For group variables:

all:
  children:
    web_servers:
      vars:
        http_port: 80
        max_connections: 1000
    db_servers:
      vars:
        backup_enabled: true

Connection Parameters for Linux and Windows

When managing both Linux and Windows hosts with SSH, the inventory needs to specify the correct connection parameters for each system. Here's how I typically structure this:

all:
  children:
    linux:
      hosts:
        linux1.example.com:
          ansible_user: admin
          ansible_ssh_private_key_file: ~/.ssh/linux_key
    windows:
      hosts:
        win1.example.com:
          ansible_user: Administrator
          ansible_ssh_private_key_file: ~/.ssh/windows_key
          ansible_shell_type: powershell

The ansible_shell_type: powershell parameter is crucial when using SSH with Windows, as it tells Ansible to use PowerShell commands instead of Bash.

Using Directory Structure for Organization

For larger projects, I organize my inventory using the directory structure approach:

inventory/
├── group_vars/
│   ├── all.yml           # Variables for all hosts
│   ├── linux.yml         # Variables for Linux hosts
│   └── windows.yml       # Variables for Windows hosts
├── host_vars/
│   ├── web1.example.com.yml
│   └── db1.example.com.yml
└── hosts                # Main inventory file

This approach makes it much easier to manage variables as your infrastructure grows.

Inventory Patterns I've Found Useful

Over time, I've developed some inventory patterns that have served me well:

Pattern 1: Environment-Based Grouping

all:
  children:
    production:
      children:
        prod_linux:
          hosts:
            prod-lin-1.example.com:
            prod-lin-2.example.com:
        prod_windows:
          hosts:
            prod-win-1.example.com:
            prod-win-2.example.com:
    staging:
      children:
        stage_linux:
          hosts:
            stage-lin-1.example.com:
        stage_windows:
          hosts:
            stage-win-1.example.com:

This hierarchical structure makes it easy to target all production hosts, or specifically production Linux hosts, etc.

Pattern 2: Function-Based Grouping

all:
  children:
    web:
      hosts:
        web-lin-1.example.com:
        web-win-1.example.com:
    database:
      hosts:
        db-lin-1.example.com:
    monitoring:
      hosts:
        mon-lin-1.example.com:
        mon-win-1.example.com:

This approach groups hosts by their function regardless of OS type, which is useful for role-based playbooks.

Sequence Diagram: Authentication Flow for Linux and Windows via SSH

When Ansible connects to remote hosts via SSH, there's a specific authentication flow that happens. Here's a sequence diagram that illustrates how it works for both Linux and Windows systems:

This diagram shows how Ansible's configuration and inventory settings influence the authentication process for both Linux and Windows hosts. The key difference is that on Linux, Ansible executes Python modules, while on Windows with SSH, it executes PowerShell modules.

Best Practices I've Learned the Hard Way

Through many projects and some painful lessons, I've developed these best practices:

For Configuration

  1. Version Control Your Config: Always keep your ansible.cfg in version control with your playbooks.

  2. Environment-Specific Configs: Use environment variables for settings that change between environments.

  3. Minimal Configuration: Only specify settings that differ from defaults to keep your configuration manageable.

For Inventory

  1. Use Dynamic Inventory When Possible: For cloud environments, dynamic inventories keep your host list up-to-date automatically.

  2. Group Hosts Multiple Ways: A host can (and often should) belong to multiple groups. Use this to your advantage.

  3. Keep Sensitive Variables in Vault: Never store passwords or secret keys in plain text. Use Ansible Vault instead.

  4. Document Your Inventory Structure: As your inventory grows, document the group hierarchy and variable precedence.

  5. Use Patterns for Consistency: Establish naming conventions for hosts and groups and stick to them.

Inventory for Mixed Linux and Windows Environments

Managing both Linux and Windows systems used to require separate approaches, but with SSH now available on Windows, I've been able to standardize my approach. Here's a template I use for mixed environments:

all:
  vars:
    ansible_connection: ssh
  children:
    linux:
      vars:
        ansible_python_interpreter: /usr/bin/python3
      hosts:
        linux1.example.com:
        linux2.example.com:
    windows:
      vars:
        ansible_shell_type: powershell
        ansible_become: false  # Most Windows tasks don't use become
      hosts:
        win1.example.com:
        win2.example.com:

This setup allows me to use most of the same playbooks for both Linux and Windows hosts, with only OS-specific tasks requiring separate handling.

Conclusion

Mastering Ansible configuration and inventory management has dramatically improved my automation capabilities. While it might seem tedious at first, the time invested in setting up a proper inventory structure pays off tremendously as your infrastructure grows.

For those just starting with Ansible, I recommend beginning with a simple inventory and gradually introducing more structured approaches as you become familiar with the tool. Remember that Ansible is highly flexible - there's no single "right way" to organize your inventory, only what works best for your specific needs.

Whether you're managing Linux servers, Windows machines, or both, a well-designed inventory combined with proper configuration will make your Ansible automation more reliable, maintainable, and powerful.


What inventory organization strategies have you found most useful in your environment? I'd love to hear your approaches in the comments below!

Last updated