How to Use Ansible Gcp compute inventory plugin

If you use Ansible to manage your infrastructure configuration, you have to maintain an inventory of all the target hosts and host groups you want to manage. Ansible inventories are often written in yaml or ini files.

But sometimes, your infrastructure is already managed by an external system (CMDB, cloud platform, …), so you’ll have to to pull your ansible inventory from these external sources.

Ansible let’s you manage that with dynamic inventory

Work with Ansible dynamic inventories

A dynamic inventory is a python script that should accept --list and --host <hostname> arguments. It will compute your inventory by outputing a json dictionnary hots groups, hosts and variables.

Here is an example of an empty expected json response :

{
    "_meta": {
        "hostvars": {}
    },
    "all": {
        "children": [
            "ungrouped"
        ]
    },
    "ungrouped": {}
}

You can call it this way :

# Ping all hosts from the dynamic inventory
ansible -i <my_dynamic_inventory>.py all -m ping

As dynamic inventory is a python script, you have full flexibility on how to organize your inventory. It is very handy when you need to call apis to retrieve hosts data from external sources.

Inventory plugins

Dynamic inventory can be a bit tedious to write from scratch. So since ansible 2.4, there is an existing plugin type to standardize the way to implement dynamic inventories : inventory plugins

There is a lot of already existing inventory plugins to interface with most common vendors : gcp, aws, azure, ovh, scaleway…, you can list them all using the following command :

# List existing inventory plugins 
ansible-doc -t inventory -l

Reading existing lugins code is a great way to demistify how you shoud write a custom one if you need. If you really need to develop a custom one, you can follow this documentation

Example with GCP inventory plugin

For the purpose of this article, let’s say you have a gcp project with id myproject with 3 compute vms in it (2 web servers and 1 db server):

Vm instance name Region Zone
euw4-gcp-web-1 europe-west4 europe-west4-a
euw4-gcp-web-2 europe-west4 europe-west4-b
euw4-gcp-db europe-west4 europe-west4-a

We will use the existing gcp_compute inventory plugin to manage your gcp compute vms with ansible.

This plugin is embedded in your ansible install, so you are ready to use it.

Prerequisistes

Ansible gcp_compute plugin use gcp apis to authenticate, and rely on the following python libraries for that : requests and google-auth. If you have installed pip, you can install these libraries with these commands :

pip install requests
pip install google-auth

Ansible gcp_compute propose several authentication methods, but on this article we will use the serviceaccount method. So you will need to create a serviceaccount in your project with permissions to read compute at minima. Then generate and download the json service account key, we will use it for the rest of this article.

Setup

Ansible gcp_compute plugin can be configured with a yaml file. Here is a minimal yaml configuration file example:

# inventory.compute.gcp.yml
plugin: gcp_compute             # name the plugin you want to use (use `ansible-doc -t inventory -l` to list available plugins)
projects:
  - <gcp_project_id>            # Id of your gcp project
regions:                        # regions from your project you want to fetch inventory from (you can also use zones instead of regions if you target one or several specific zones)        
  - <gcp_project_region>
filters: []
auth_kind: serviceaccount       # gcp authentication kind. with service account you should provide the service account json key file to authenticate
service_account_file: <service_account_file>.json   # Service account json keyfile

Basic usage

You can test the plugin to see the computed inventory with the ansible-inventory command, and you should see the following output :

➜  ansible-inventory -i inventory.compute.gcp.yml --graph
@all:
  |--@ungrouped:
  |  |--<ip_euw4-gcp-web-1>
  |  |--<ip_euw4-gcp-web-2>
  |  |--<ip_euw4-gcp-db>

If you want to see compute vm names instead of ip, you can add hostnames attribute in you yaml inventory :

# inventory.compute.gcp.yml
plugin: gcp_compute
projects:
  - <gcp_project_id>
regions:
  - <gcp_project_region>
hostnames:                # A list of options that describe the ordering for which hostnames should be assigned. Currently supported hostnames are 'public_ip', 'private_ip', or 'name'.
  - name
filters: []
auth_kind: serviceaccount
service_account_file: <service_account_file>.json

Now, you’ll see the following ouput :

➜  ansible-inventory -i inventory.compute.gcp.yml --graph
@all:
  |--@ungrouped:
  |  |--euw4-gcp-web-1
  |  |--euw4-gcp-web-2
  |  |--euw4-gcp-db

If you want full details about your inventory :

➜  ansible-inventory -i inventory.compute.gcp.yml --list

It will output your inventory hosts, groups and associated vars as json.

You are now ready to use this inventory (here we launch a ping command on all hosts) :

# Launch ping command on all hosts matched by the inventory
ansible -i inventory.compute.gcp.yml all -m ping

Group your hosts

We see in previous examples that retrieve hosts are ungrouped. Let’s say I want to group them by zones. You can specify the keyed_groups to do so. keyed_groups can be seen as facets : it will create host groups dynamically based on the content of your hosts.

# inventory.compute.gcp.yml
---
plugin: gcp_compute
projects:
  - <gcp_project_id>
regions:
  - <gcp_project_region>
keyed_groups:
  - key: zone
hostnames:
  - name
filters: []
auth_kind: serviceaccount
service_account_file: <service_account_file>.json

If you test now your inventory, you will see that your hosts will be grouped by zones :

➜  ansible-inventory -i inventory.compute.gcp.yml --graph
@all:
  |--@_europe_west4_a:
  |  |--euw4-gcp-web-1
  |  |--euw4-gcp-db
  |--@_europe_west4_b:
  |  |--euw4-gcp-web-2
  |--@ungrouped:
  |  |--all:
  |  |--vars:

Ok, I am now able to execute different actions for specific zones.

If I want to execute specific actions for web servers, I need to group my web servers in a host group named web-servers. I can use the groups attribute in my inventory for that. Here I will group the hosts based on their names (if a host is named with the pattern -web- then it will belong to the web-servers group) :

# inventory.compute.gcp.yml
---
plugin: gcp_compute
projects:
  - <gcp_project_id>
regions:
  - <gcp_project_region>
keyed_groups:
  - key: zone
groups:
  web-servers: "'-web-' in name"
hostnames:
  - name
filters: []
auth_kind: serviceaccount
service_account_file: <service_account_file>.json

The graph ouput :

➜  ansible-inventory -i inventory.compute.gcp.yml --graph
@all:
  |--@_europe_west4_a:
  |  |--euw4-gcp-web-1
  |  |--euw4-gcp-db
  |--@_europe_west4_b:
  |  |--euw4-gcp-web-2
  |--@web-servers:
  |  |--euw4-gcp-web-1
  |  |--euw4-gcp-web-2
  |--@ungrouped:
  |  |--all:
  |  |--vars:

You can group also group hosts based on host labels instead of vm name if you prefer.

Combine dynamic inventory with static inventory

If I have to apply variables to my groups and hosts, I can do that in a static inventory and combine this static inventory with my gcp dynamic inventory.

For example, let’s say we specify ansible_become and ansible_ssh_user variables to apply to all hosts.

You can create this static inventory

# inventory.yml
all:
  vars:
    ansible_become: true
    ansible_ssh_user: <your_custom_user>

Create a directory inventory and place your 2 inventories in it :

➜  tree inventory/
inventory/
├── inventory.gcp.yml
└── inventory.yml

Then execute ansible by using inventory directory :

# Launch ping command on all hosts matched by the inventory
ansible -i inventory all -m ping

Ansible will combine all inventories combined in this directory.

Conclusion

With Ansible inventory plugins you can interface with external systems to retrieve your inventories. It is good for maintenance as you maintains only one repository for your inventory data.

A lot of inventory plugins are provided ootb by Ansible but you can build your own if you need to.

If you use gcp, you can use gcp_compute inventory plugin to retrieve all your compute vms data.

You can combine multiple inventories (static and dynamic ones), for example if you need to combine custom variables not directly managed in gcp.