CIQ

Ascender Custom Dynamic Inventory Scripts

Ascender Custom Dynamic Inventory Scripts
Greg SowellNovember 14, 2023

When using inventories in Ascender and AWX, it’s often advantageous to take advantage of dynamic inventories. This really just means a way to query a Configuration Management Database (CMDB) for a list of hosts and their variables. There are many ways baked into Ascender to do this with services like Amazon, Google, or VMWare, but what about when it’s a CMDB that’s not on that list; that’s where custom dynamic inventory scripts come in! They allow you to query any system or digest any file to build an inventory dynamically.

Video

https://www.youtube.com/watch?v=kOa1sNfFmW4

Scripts

You can find my scripts here in my git repository.

First is my sample JSON data file [json-payload]:

{
"hosts": [
    {
      "id": 1,
      "hostname": "host1.example.com",
      "ip_address": "192.168.1.101",
      "status": "active",
      "os": "Linux",
      "owner": "Admin User"
    },
    {
      "id": 2,
      "hostname": "host2.example.com",
      "ip_address": "192.168.1.102",
      "status": "inactive",
      "os": "Windows",
      "owner": "User 1"
    },
    {
      "id": 3,
      "hostname": "host3.example.com",
      "ip_address": "192.168.1.103",
      "status": "active",
      "os": "Linux",
      "owner": "User 2"
    },
    {
      "id": 4,
      "hostname": "host4.example.com",
      "ip_address": "192.168.1.104",
      "status": "active",
      "os": "Windows",
      "owner": "User 3"
    },
    {
      "id": 5,
      "hostname": "host5.example.com",
      "ip_address": "192.168.1.105",
      "status": "inactive",
      "os": "Linux",
      "owner": "User 4"
    }
  ]
}

As you can see, this is a simple set of JSON data that could be returned via my CMDB. Remember, this can be live querying or consumed via file. This list is five different hosts with a mix of variables.

Now for the custom dynamic inventory script [custom_inventory.py].

#!/usr/bin/env python
#test import script
import json

# Specify the path to the JSON payload file
json_payload_file = 'json-payload'

# Read the JSON payload from the file
with open(json_payload_file, 'r') as file:
    json_payload = file.read()

# Parse the JSON payload
inventory_data = json.loads(json_payload)

# Initialize inventory data structures
ansible_inventory = {
    '_meta': {
        'hostvars': {}
    },
    'all': {
        'hosts': [],
        'vars': {
            # You can define global variables here
        }
    }
}

# Initialize group dictionaries for each OS
os_groups = {}

# Process each host in the JSON payload
for host in inventory_data['hosts']:
    host_id = host['id']
    hostname = host['hostname']
    ip_address = host['ip_address']
    status = host['status']
    os = host['os']
    owner = host['owner']

    # Add the host to the 'all' group
    ansible_inventory['all']['hosts'].append(hostname)

    # Create host-specific variables
    host_vars = {
        'ansible_host': ip_address,
        'status': status,
        'os': os,
        'owner': owner
        # Add more variables as needed
    }

    # Add the host variables to the '_meta' dictionary
    ansible_inventory['_meta']['hostvars'][hostname] = host_vars
    # Add the host to the corresponding OS group
    if os not in os_groups:
        os_groups[os] = {
            'hosts': []
        }
    os_groups[os]['hosts'].append(hostname)

# Add the OS groups to the inventory
ansible_inventory.update(os_groups)

# Print the inventory in JSON format
print(json.dumps(ansible_inventory, indent=4))

Note: make sure to make the file executable before uploading to your git repository or you will kick a permissions error when running the script on Ascender.

The whole first half of the script is pretty standard across all scripts. It begins to vary when I setup the variable for dynamic group mapping.

# Initialize group dictionaries for each OS
os_groups = {}

This isn’t a requirement, but is certainly a big value add. I’m going to be grouping the hosts based on OS type, so I create the os_groups variable to store that info.

# Process each host in the JSON payload
for host in inventory_data['hosts']:
    host_id = host['id']
    hostname = host['hostname']
    ip_address = host['ip_address']
    status = host['status']
    os = host['os']
    owner = host['owner']

    # Add the host to the 'all' group
    ansible_inventory['all']['hosts'].append(hostname)

This section processes the info from the JSON payload and maps it to some standard variables for later use. I last add each host to the “all” inventory group.

    # Create host-specific variables
host_vars = {
        'ansible_host': ip_address,
        'status': status,
        'os': os,
        'owner': owner
        # Add more variables as needed
    }

    # Add the host variables to the '_meta' dictionary
    ansible_inventory['_meta']['hostvars'][hostname] = host_vars

Here, I’m creating the host_vars section (all of the variables saved for each host in the inventory).

   # Add the host to the corresponding OS group
if os not in os_groups:
        os_groups[os] = {
            'hosts': []
        }
    os_groups[os]['hosts'].append(hostname)

# Add the OS groups to the inventory
ansible_inventory.update(os_groups)

Last, I’m creating the os groups and then adding each host to its corresponding group.

Ascender Configuration

The Ascender config is pretty straightforward. Start by adding a project in pointing to the script repository:

Now I attach the script to an inventory. I first either create or enter an inventory:

Once in the inventory, I click the “Sources” tab and click “Add”:

From here, give the source a name, choose “Sourced from a Project” under Source, select out newly created project, and last choose the new inventory script:

Some notes in this section. At the bottom, there are some checkbox options:  

  • Overwrite will wipe everything (hosts, groups, variables) and replace it with what is returned from the script.

  • Overwrite variables will wipe all variables and replace them with script return data.

  • Update on launch will force this script to rerun each time the inventory is used (this is often a desired option).

To manually synchronize, simply click the “Sync” button.  

To view status or debug what is happening or happened in your sync, click Last Job Status:

You can check out the results of the sync by going to the host section in your inventory:

Conclusion

The dynamic inventory sources are a powerful way to extend the usefulness of Ascender. Dynamic is the name of the game, as it keeps your automations as flexible as possible.

If you have any questions or concerns, if there are any tweaks or tunes you’d make, please reach out; I’d love to hear from you.

Thanks and happy automating!

Related posts

Ascender Config as Code with Network Backups to Git and Rollback

Ascender Config as Code with Network Backups to Git and Rollback

Aug 23, 2023

Ascender Automation

Ascender Custom Dynamic Inventory Scripts

Ascender Custom Dynamic Inventory Scripts

Nov 14, 2023

Ascender Automation

Ascender Migrates Host From CentOS 7 To Rocky 8

Ascender Migrates Host From CentOS 7 To Rocky 8

Oct 11, 2023

Ascender Automation

123
7
>>>