Skip to content
CloudOps
Newsletter
All guides
AI for Ansible By James Joyner IV · · 8 min read

Structuring Ansible Roles and Inventory for Real Environments

A practical guide to organizing Ansible roles and inventory so your automation scales past one host group without turning into spaghetti.

  • #iac
  • #ansible
  • #roles
  • #inventory
  • #configuration-management
  • #structure

The difference between Ansible that scales and Ansible that becomes a liability is almost never the tasks themselves. It’s how the roles and inventory are organized. Get the structure right and you can onboard a teammate in an afternoon. Get it wrong and every change is an archaeology dig.

After a couple of decades of building and rescuing Ansible setups, here’s the structure I reach for by default — and how AI helps you bootstrap it without the tedium.

What a role actually is

A role is a self-contained unit of automation with a fixed directory layout. That layout is the whole point: anyone who knows Ansible knows exactly where to look.

roles/nginx/
├── defaults/main.yml   # default vars (lowest precedence — overridable)
├── vars/main.yml       # internal vars (high precedence — don't override)
├── tasks/main.yml      # the work
├── handlers/main.yml   # restart/reload triggers
├── templates/          # Jinja2 templates
├── files/              # static files to copy
└── meta/main.yml       # dependencies and metadata

The split between defaults/ and vars/ trips people up. Defaults are knobs you expect callers to turn. vars/ holds internal constants you don’t want overridden. If you find yourself overriding a vars/ value from outside, it belonged in defaults/.

Roles should be single-purpose and parameterized

A good role does one thing — install and configure nginx — and exposes its variability through defaults/. A role that installs nginx and deploys your app and configures the firewall is three roles wearing a trench coat.

# roles/nginx/defaults/main.yml
nginx_worker_processes: auto
nginx_listen_port: 80
nginx_server_name: "{{ inventory_hostname }}"
# usage in a playbook
roles:
  - role: nginx
    nginx_listen_port: 8080
    nginx_server_name: api.internal

This is where AI saves real time. Hand it a working but messy playbook and ask: “extract this into an Ansible role with a standard directory layout, moving configurable values into defaults/main.yml.” It produces the scaffolding and the variable extraction in one pass. You review and adjust — but the boring part is done.

Inventory: group by what changes together

Inventory is where most setups go wrong. The principle: group hosts by the attributes that drive configuration differences. Role in the stack, environment, region — these are groups. A host belongs to many groups, and that’s intended.

# inventory/production/hosts.ini
[web]
web01.prod.example.com
web02.prod.example.com

[db]
db01.prod.example.com

[prod:children]
web
db

Then variables hang off groups, not hosts:

inventory/
├── production/
│   ├── hosts.ini
│   └── group_vars/
│       ├── all.yml      # everything in prod
│       ├── web.yml      # web-tier settings
│       └── db.yml
└── staging/
    ├── hosts.ini
    └── group_vars/

A single host might be in web, prod, and us-east, inheriting from all three. The precedence is well-defined, so design your groups so the most specific group wins on conflicts.

Separate inventory per environment

Notice that production/ and staging/ are sibling directories, each with its own inventory and group_vars/. This is deliberate. It makes “deploy to staging” literally -i inventory/staging, and it makes cross-environment promotion a question of diffing two group_vars/ trees rather than untangling a giant when: env == 'prod' ladder.

When you need to keep these in sync, AI is a useful diff partner: paste both group_vars/all.yml files and ask “what differs between these two environments, and which differences look intentional versus accidental?” It catches the drift you skim past.

Dynamic inventory for the cloud

Static .ini files are fine for stable fleets. In a cloud where instances come and go, use a dynamic inventory plugin (aws_ec2, azure_rm, gcp_compute) that queries the provider and builds groups from tags.

# inventory/aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - us-east-1
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: tags.Env
    prefix: env

Now an instance tagged Role=web, Env=prod lands in role_web and env_prod automatically. Tag discipline becomes inventory discipline.

Sharing roles without copy-paste

Once roles are clean, stop copying them between repos. Pull community roles and your own shared roles via a requirements.yml:

# requirements.yml
roles:
  - name: geerlingguy.nginx
    version: "3.1.4"
collections:
  - name: community.general

ansible-galaxy install -r requirements.yml pulls them, pinned to versions. Pinning matters — an unpinned role update is config drift waiting to happen.

Where AI earns its keep here

  • Scaffolding roles from an existing playbook — the single biggest time saver.
  • Generating keyed_groups for dynamic inventory from a description of your tagging scheme.
  • Auditing inventory for hosts that belong to no meaningful group, or vars defined at the wrong level.
  • Explaining precedence when a variable resolves to a value you didn’t expect — paste the layers, ask which one wins.

Keep these prompts in a prompt library so the model always knows your layout conventions, and browse the rest of the IaC guides for the testing and promotion side of this.

Structure is leverage. The roles and inventory you design today are the constraints — or the freedom — your team works inside for years. Spend the afternoon getting it right.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.