So I had a unique scenario where we built a solution for users using Ansible but wanted to make it simple so that it didn’t require the end users to understand how to build an inventory or playbooks.

So the solution we came up with was to build Ansible playbooks and inventories with Ansible itself. The users just need a single YAML file and then run a make command and Ansible builds itself the inventory, playbooks, and group_vars at run time. It’s also transient, so the playbooks only exist for the run itself. This is because we are running in a virtualenv on different build boxes and didn’t want to persisit any data. The parameterized pipeline is run in Jenkins which just asks for a couple parameters to define which YAML file they want from their repository.

So let’s take a simple example. Here’s the YAML file that needs defined, along with the playbook template, the inventory template, and the playbook that will generate everything:

app1:

app_name: the-app
version: 1
database_servers:
  - 10.1.1.10
  - 10.1.1.11
app_servers:
  - 10.1.1.30
  - 10.1.1.31
max_connections: 30

generate_inventory:

---
- name: Generate Inventory
  hosts: localhost

  tasks:
    - name: Get app data
      include_vars:
        dir: apps/
        files_matching: "{{ app_name }}.yml"

    - name: Create inventory
      template:
        src: inventory.j2
        dest: "inventory/{{ app_name }}"

    - name: Create group_vars
      template:
        src: group_vars.j2
        dest: "group_vars/{{ app_name }}"
 
    - name: Create playbook
      template:
        src: playbook-template.yml.j2
        dest: {{ app_name }}.yml

playbook:

#jinja2:variable_start_string:'[%' , variable_end_string: '%]'
---
- name: Deploy App
  hosts: [% app_name %]
  user: [% app_name %]

  tasks:
    - name: Generate Playbook
      debug:
        msg: "I'm {{app_name }} {{ version }} running on {{ ansible_host }}"
<!-- endtab -->
<!-- tab inventory -->
all:
  children:
    {{ app_name }}:
      children:
        app_servers:
          hosts:
{% for app_server in app_servers %}
            appServer{{ loop.index }}:
            ansible_host: {{ app_server }}
{% endfor %}
        db_servers:
{% for db_server in database_servers %}
            dbServer{{ loop.index }}:
            ansible_host: {{ db_server }}
{% endfor %}

The group_vars files were setup with a template in the same fashion. We found it easier to have the inventory in YAML format. The inventory we set up was more complex than this one and the looping was easier to append values to the end of names.

Now during the build, the Makefile runs the “Generate Inventory” playbook and in this case will create a playbook called app1.yml and an inventory of inventory/app1.

Here’s the inventory rule for the Makefile:

.PHONY inventory
inventory:
	git clean -fdx inventory/
	git clean -fdx group_vars
	ansible-playbook generate_inventory.yml -e app_name=$(app)

Then $(app) is passed along from the Jenkins parameter to the rule so that Ansible knows which playbook to build.

All in all this has worked well so far to help general users who didn’t necessarily need to understand how Ansible works deploy a specific piece of software.