# Ansible Inventory > Define which hosts to manage, how to group them, and where to find them. ## Inventory Formats ### INI Format ```ini # inventory/hosts.ini [webservers] web1.example.com web2.example.com ansible_port=2222 [dbservers] db1.example.com ansible_user=postgres db[1:3].example.com # db1, db2, db3 [loadbalancers] lb1.example.com # Group of groups [production:children] webservers dbservers loadbalancers # Group variables [webservers:vars] http_port=8080 app_env=production [all:vars] ansible_python_interpreter=/usr/bin/python3 ``` ### YAML Format (preferred) ```yaml # inventory/hosts.yml all: vars: ansible_python_interpreter: /usr/bin/python3 children: production: children: webservers: hosts: web1.example.com: web2.example.com: ansible_port: 2222 vars: http_port: 8080 dbservers: hosts: db1.example.com: ansible_user: postgres loadbalancers: hosts: lb1.example.com: ``` ## Directory Layout ```text inventory/ ├── hosts.yml # Host definitions ├── group_vars/ │ ├── all.yml # Vars for every host │ ├── all/ # Dir form (merged, alphabetical) │ │ ├── common.yml │ │ └── vault.yml # Encrypted secrets │ ├── webservers.yml # Vars for webservers group │ └── production.yml # Vars for production parent group └── host_vars/ ├── web1.example.com.yml └── db1.example.com/ # Dir form ├── main.yml └── vault.yml ``` ### Multiple Environments ```text inventories/ ├── staging/ │ ├── hosts.yml │ ├── group_vars/ │ └── host_vars/ └── production/ ├── hosts.yml ├── group_vars/ └── host_vars/ ``` ```bash ansible-playbook site.yml -i inventories/staging/ ansible-playbook site.yml -i inventories/production/ ``` ## Dynamic Inventories ### Script-Based (legacy) A script that outputs JSON when called with `--list` or `--host `. ```bash #!/usr/bin/env python3 # inventory/dynamic.py — must be executable (chmod +x) import json, sys def get_inventory(): return { "webservers": { "hosts": ["web1.example.com", "web2.example.com"], "vars": {"http_port": 8080} }, "dbservers": { "hosts": ["db1.example.com"] }, "_meta": { "hostvars": { "web1.example.com": {"ansible_port": 22}, "db1.example.com": {"ansible_user": "postgres"} } } } if "--list" in sys.argv: print(json.dumps(get_inventory(), indent=2)) elif "--host" in sys.argv: host = sys.argv[sys.argv.index("--host") + 1] hostvars = get_inventory().get("_meta", {}).get("hostvars", {}) print(json.dumps(hostvars.get(host, {}))) ``` The `_meta.hostvars` key avoids per-host `--host` calls (efficiency). ### Plugin-Based (modern, preferred) ```yaml # inventory/aws_ec2.yml — filename must end with the plugin suffix plugin: amazon.aws.aws_ec2 regions: - eu-west-1 keyed_groups: - key: tags.Environment prefix: env - key: instance_type prefix: type filters: tag:Managed: ansible compose: ansible_host: private_ip_address ``` Common inventory plugins: | Plugin | Source | Suffix | |-------------------------------|------------|-----------------| | `amazon.aws.aws_ec2` | AWS EC2 | `aws_ec2.yml` | | `azure.azcollection.azure_rm`| Azure | `azure_rm.yml` | | `google.cloud.gcp_compute` | GCP | `gcp.yml` | | `community.general.proxmox` | Proxmox | `proxmox.yml` | | `community.docker.docker_containers` | Docker | `docker.yml` | | `ansible.builtin.constructed` | Derived | `constructed.yml`| ```bash # Verify dynamic inventory output ansible-inventory -i inventory/aws_ec2.yml --graph ansible-inventory -i inventory/aws_ec2.yml --list ``` ### Constructed Inventory (compose groups from existing data) ```yaml # inventory/constructed.yml plugin: ansible.builtin.constructed strict: false groups: is_debian: ansible_os_family == "Debian" is_large: ansible_memtotal_mb > 8192 keyed_groups: - key: ansible_distribution | lower prefix: os ``` ## Special Variables | Variable | Purpose | |-----------------------|------------------------------------| | `ansible_host` | IP/hostname to connect to | | `ansible_port` | SSH port (default: 22) | | `ansible_user` | SSH user | | `ansible_ssh_private_key_file` | SSH key path | | `ansible_become` | Enable privilege escalation | | `ansible_connection` | Connection type (`ssh`, `local`) | | `ansible_python_interpreter` | Python path on target | ## Gotchas - Script inventories must be executable (`chmod +x`) and have a shebang - Plugin inventory files must end with the correct suffix or be listed in `enable_plugins` - `group_vars/` directory must sit next to the inventory file, or at playbook level - YAML inventory: hosts need trailing colon even with no vars (`web1.example.com:`) - Groups named `all` and `ungrouped` are implicit — don't redefine them - Directory form `group_vars/all/` merges files alphabetically — name carefully ## See Also - `ansible` — core commands and ansible.cfg - `ansible-variables` — variable precedence across inventory layers - `ansible-roles` — role structure and organization