How to Create Dynamic Inventory in YAML Format for Ansible

ansibleyaml

Background

I have domain managed via Terraform and it outputs server IP address. Then I have a script which outputs YAML inventory (outputs follows later).

Problem

When I run ansible-inventory --inventory inventory.sh --graph it always end up with

@all:
  |--@stage:
  |--@ungrouped:
  |  |--18.66.1.28

Were I would expect the machine to be in group stage.

I found out there is different behaviour for static inventory and for inventory as a script output.

Static file

If You save this as a static file and use it as an inventory, it will work.

all:
  hosts:
    18.66.1.28
  children:
    stage:
      hosts:
        18.66.1.28:

Output:

  @all:
  |--@stage:
  |  |--18.66.1.28
  |--@ungrouped:

Script

but if You specify shell script like follows, it won't work

#!/bin/bash

echo "all:
  hosts:
    18.66.1.28
  children:
    stage:
      hosts:
        18.66.1.28:"

Output:

 [WARNING]:  * Failed to parse /tmp/inventory.sh with script plugin: You defined a group 'all' with bad data for the host list:  {u'hosts':
u'18.66.1.28', u'children': {u'stage': {u'hosts': {u'18.66.1.28': None}}}}

 [WARNING]:  * Failed to parse /tmp/inventory.sh with ini plugin: /tmp/inventory.sh:3: Error parsing host definition 'echo "all:': No closing
quotation

 [WARNING]: Unable to parse /tmp/inventory.sh as an inventory source

 [WARNING]: No inventory was parsed, only implicit localhost is available

YAML outputs

Variant 1

all:
  hosts:
    - 18.66.1.28
  children:
    stage:
      hosts:
        - 18.66.1.28:

Variant 2

all:
  hosts:
    - 18.66.1.28
  children:
    stage:
      hosts:
        18.66.1.28:

Variant 3

all:
  hosts:
    - 18.66.1.28
  children:
    stage:
      hosts:
        - 18.66.1.28

Best Answer

Short answer

Don't look at https://docs.ansible.com/ansible/2.6/user_guide/intro_inventory.html but at https://docs.ansible.com/ansible/2.6/dev_guide/developing_inventory.html.

Long answer

Ansible is using at least three formats for inventories. YAML, INI and JSON. INI format is completely separate format with its own structure. YAML and JSON per-se allows to create pretty much same data structures. When I look at https://docs.ansible.com/ansible/2.6/user_guide/intro_inventory.html full of YAML examples, using JSON I would just change the notation to produce same data structure as with YAML. The thing is, script plugin is using different different data structure, but it allows you to use both YAML and JSON format.

Examples

Static inventory

Input:

$ cat /tmp/inventory-static 
all:
  hosts:
    18.66.1.28
  children:
    stage:
      hosts:
        18.66.1.28:

Output

$ ansible-inventory --inventory /tmp/inventory-static --graph 
@all:
  |--@stage:
  |  |--18.66.1.28
  |--@ungrouped:

This is correct format and behaviour according to https://docs.ansible.com/ansible/2.6/user_guide/intro_inventory.html .

Script

Input:

$ cat /tmp/inventory.sh 
#!/bin/bash

echo "all:
  hosts:
    18.66.1.28
  children:
    stage:
      hosts:
        18.66.1.28:"

Output:

 $ ansible-inventory --inventory /tmp/inventory.sh --graph    
 [WARNING]:  * Failed to parse /tmp/inventory.sh with script plugin: You defined a group 'all' with bad data for the host list:  {u'hosts':
u'18.66.1.28', u'children': {u'stage': {u'hosts': {u'18.66.1.28': None}}}}

 [WARNING]:  * Failed to parse /tmp/inventory.sh with ini plugin: /tmp/inventory.sh:3: Error parsing host definition 'echo "all:': No closing
quotation

 [WARNING]: Unable to parse /tmp/inventory.sh as an inventory source

 [WARNING]: No inventory was parsed, only implicit localhost is available

This is not what I would expect. Problem is the script plugin expects different data structure. Let’s try this with data structure from https://docs.ansible.com/ansible/2.6/dev_guide/developing_inventory.html.

Input:

$ cat /tmp/inventory.sh                                  
#!/bin/bash
echo "stage:
  hosts:
    - 18.66.1.28”

Output

$ ansible-inventory --inventory /tmp/inventory.sh --graph
@all:
  |--@stage:
  |  |--18.66.1.28
  |--@ungrouped:

Yes this is the result I expected. The machine is in the right group and the ingesting plugin is not complaining about anything. Of course according to the “Dynamic Inventory Sources”, the script should do a bit more than just echo the inventory and it should probably output JSON, this is just demonstration of how to structure the output of a scripted inventory.

This took me quite a lot of time to figure out, so I hope by writing this I'll save it to somebody else.

Related Topic