Three ways of sharing ansible inventories
In order for Ansible to run against your infrastructure you need an inventory. Define your hosts put them into groups and add some variables. Playbooks runs are then bound to these groups, hosts or patterns.
When starting things of, it may be sufficient to store your inventory in /etc/ansible/hosts
and write some simple playbooks.
Without any intentions of sharing this is fine.
One repository¶
As soon as your playbooks and/or the number of hosts to manage starts growing or even when you want to run your playbooks on multiple machines you have to start organizing thinks. Ansible's user guide provides nice examples of how to structure your directory layout. I like the alternative layout a bit more, since it keeps you base directory a bit cleaner. But nothing is carved in stone. So this allows you to have one directory to to rule them all. Whilst organizing this way, feel free to start using version control as well.
Summary:
- flexible in regards of running from multiple hosts
- wildly accepted and easy to use directory layout
git submodules¶
At some point you might face following situation. You have a repository following best practices and your playbooks have become thematically distinguishable i.e. bunch of plays only for maintenance, bunch of plays for deployments, etc. It's perfectly fine to move them to separate repositories. But it's likely there is a overlaps in your inventory i.e. you want to maintain the same hosts as deploying them. Duplicating the inventory is a possibility but this would increase your effort keeping everything in sync and thus lower productivity to some extend.
Now there is a way of avoiding duplication by using git submodules. Git allows to bind a repository as subdirectory to your main repository, whilst keeping commits separate. Which, apart from ansible inventories, comes in handy for things like libraries.
In this example I'll split a repository called ansible/playbooks
.
Create 3 repositories: ansible/maintenance
, ansible/deployments
and ansible/inventories
. Move everything in ansible/playbooks/inventories
into ansible/inventories
. Move all roles, libraries and plays dedicated for maintenance into ansible/maintenance
and everything dedicated to deployments into ansible/deployments
.
Info
It is important that ansible/maintenance
and ansible/deployments
contains no directory called inventories
.
Now let's add ansible/inventories
as submodule.
cd ansible/inventories
git submodule add ../inventories.git inventories
Info
By specifying a relative git url, git submodule will resolve paths relative to the main repository.
Using git submodule add https://example-gitlab.com/ansible/inventories.git inventories
instead is perfectly fine.
I'm using relative urls because GitLab's CI/CD wants it that way.
This will add 2 new files to your main repository .gitmodules
and inventories
. Commit and push them and you're done.
Using submodules requires a slight adjustment to your git-commands usage. For cloning and pulling you should add the argument --recurse-submodules
.
Instead of git clone git@example-gitlab.com:ansible/maintenance.git
you should use git clone --recurse-submodules git@example-gitlab.com:ansible/maintenance.git
You can read further details, like "what do I do, when I forgot the --recuse-submodules
argument", in the git submodule book.
Summary:
- flexible in regards of running from multiple hosts
- wildly accepted and easy to use directory layout
- thematic playbook separation for better usability
- no duplication of inventory code
I personally avoid submodules, whenever I can
To be honest, I don't like submodules at all. I guess I don't use them right aka it's my fault/incompetence they break all the time. With submodules there is a ridiculously high tendency of unexpected behavior. All of a sudden you have to tread you main repository like a raw egg. A 5min change suddenly takes 1 hour, with 55min repairing the repository.
http inventory plugin¶
Ansible allows you to use dynamic inventories.
Dynamic inventories
are scripts and plugins that are using external resources like a CMDB, your CloudProvider's API or other application APIs as inventory source.
All these dynamic inventories are highly specific:
- gitlab_runners - inventory gitlab runners
- cobbler - inventory cobbler instances
- aws ec2 - inventory ec2 instances
- proxmox - inventory proxmox instances
- [...] - the list goes on and on
There was no plugin to generically fetch an inventory from a http(s) source, so I wrote a plugin my self and shared it on ansible-galaxy.
Take your inventory, convert it into json with ansible-inventory -i <your_inventory> --list --output <your_json_file>
and push <your_json_file>
to a webserver.
The derjd.general.html plugin fetches that file as inventory.
Example:
Generate a small inventory.
hosts.yaml
---
test_group:
vars:
test_var1: test
hosts:
test1.example.com:
test2.example.com:
test_var2: foo
Convert the inventory into json.
ansible-inventory -i hosts.yaml --list --output example.json
{
"_meta": {
"hostvars": {
"test1.example.com": {
"test_var1": "test"
},
"test2.example.com": {
"test_var1": "test",
"test_var2": "foo"
}
}
},
"all": {
"children": [
"test_group",
"ungrouped"
]
},
"test_group": {
"hosts": [
"test1.example.com",
"test2.example.com"
]
}
}
Start a webserver serving example.json
docker run -it --rm -d -v ./example.json:/usr/share/nginx/html/example.json -p 8080:80 -n webserv nginx:latest
Install derjd.general collection
ansible-galaxy collection install derjd.general
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Installing 'derjd.general:1.0.1' to '/home/jd/.ansible/collections/ansible_collections/derjd/general'
Downloading https://galaxy.ansible.com/download/derjd-general-1.0.1.tar.gz to /home/jd/.ansible/tmp/ansible-local-356547qe2hcbn/tmpsdkv2ujk
derjd.general (1.0.1) was installed successfully
Add inventory plugin config.
cat > test.http_inventory.yml <<EOF
---
plugin: derjd.general.http
url: http://localhost:8080/example.json
EOF
Check if ansible can parse the webserved inventory.
ansible-inventory -i test.http_inventory.yml --graph
@all:
|--@test_group:
| |--test1.example.com
| |--test2.example.com
|--@ungrouped:
Using GitLab CI/CD you can generate artifacts.
.gitlab-ci.yml
---
convert:
image: ${CI_REGISTRY}/docker/ansible:latest
artifacts: { untracked: true }
script: [ 'ansible-inventory -i . --list --output inventory.json' ]
This would be accessible via:
https://<your_gitlab_instance>/ansible/inventories/-/jobs/artifacts/master/file/inventory.json?job=convert`.
More details about Downloading artifacts.
Summary:
- flexible in regards of running from multiple hosts
- wildly accepted and easy to use directory layout
- thematic playbook separation for better usability
- no duplication of inventory code
- no add_host shenanigans
- no git submodules