Getting `host_vars` From The Inventory For A Host That Doesn't Yet Exist (But Which Is Listed In The Inventory File)

Hi All,

I’m looking for some advice / pointers / ideas on the following situation:

Let me outline what I’ve got / what I’m thinking and then what I’m trying to achieve with that thinking.

My Inventory file has a number of groups, sub-groups, and (obviously) hosts.

I use the FQDN as the inverntory_hostname.

Each group, sub-group, and host has a number of group_vars or host_vars associated with it, and the whole Inventory file is structured in such a way that:

  • Only the unique host_vars for a host appear in that host’s Var file
  • Only those group_vars that are common to a sub-group’s host(s) (ie unique to that sub-group) appear in that sub-group’s Var file
  • Only only those group_vars that are common to a group’s sub-group(s) and/or host(s) (ie unique to that group) appear in that group’s Var file

The idea here is that (as I understand things - and I could be wrong) the group-vars “cascade down” to the individual hosts and are merged into the set of host_vars.

One of the set of host_vars/group_vars for (VM) hosts are the specifications for that host ie Number of CPUs, RAM, VLAN, IPv4 & IPv6 Addresses, MAC Address(s), etc. I have a “Golden Image” VM set up with the most commonly used (ie default) set of these settings which I currently clone and (as required) modify when I need a new VM - which, to be honest, isn’t all that often.

What I’m thinking is that I can use Ansible to perform the “clone & modify” by providing the playbook with the hostname (or FQDN) of the new soon-to-exist VM and (somehow) how Ansible extract the relevant CPUs, RAM, etc, variables from the Inventory file, even though the actual host won’t exist until the end of the playbook run.

The command to start the playbook would be something like: ansible-playbook -i localhost clone_vm.yml, and inside the play there is a vars_prompt: (say vm_name) asking for the new VM’s hostname (or FQDN).

Is something like this viable (I’d like to think that is would be, as I already have a bash script which does all this - although I’ve got to provide all the values manually when I call the script) and if so, how do I take the vars_prompt: vm_name and use that as a key to lookup the other host_vars specific to the soon-to-be new host (and, of course, the corresponding group_vars in the new host’s sub-group(s) and group)?

Thanks in advance for the help

Cheers

Dulux-Oz

Hello @Dulux-Oz

As I understand, you want to use already defined facts/variables in an Ansible group for a not-existing VM. That is possible using the add_host module. Once you add the vm to the groups which has the required facts and variables, you can use any of them.

Here is an example clone_vm.yml

- hosts: localhost
  vars_prompt:
    - name: vm_name
      prompt: What's VM hostname ?
    - name: vm_groups
      prompt: Ansible groups to add the new host # <- list of groups
    # Any other prompt you need

  pre_tasks: # run before any roles, if you have any
    - name: Add host to in-memory inventory
      add_host:
        name: "{{ vm_name }}" # defined in vars_prompt
        groups: "{{ vm_groups }}" # defined in vars_prompt

  tasks:
    - name: Creating "{{ vm_name }}"
      [... your tasks ...]

You can also define an extra-vars.yml file intead of prompting all the variables and call it from the ansible command: ansible-playbook -i [inventory_file] clone_vm.yml -e @extra-vars.yml

I think the simplest tweak would be to simply change this

to ansible-playbook -i <inventory file with hosts defined> --limit <hosts, or group you want to build> clone_vm.yml and then the tasks that need to run on your localhost get delegate_to: localhost added. Then you’re iterating over each host, and all your host_vars are accessible like normal.

If you want to access variables for a specific host before targeting it (assuming it’s defined in your inventory file or added dynamically using add_host), you can use the hostvars variable. There are some examples here Discovering variables: facts and magic variables — Ansible Community Documentation.

With your inventory, group and host vars as they are, this can be easily done like this:

# Provisioning play
- hosts: all
  gather_facts: false # important

  tasks:
  - name: task 1 for provisioning host
    ... # all your vars accessible here
    delegate_to: localhost

  - name: task 2 for provisioning host
    ... # all your vars accessible here
    delegate_to: localhost

...

# Configuration play
- hosts: all
  # gather_facts: true # the default

  - name: task 1 for configuring host
    ...

  - name: task 2 for configuring host
    ...

So the idea is to use the inventory as it is even thou hosts (VMs) do not exist yet. The first play is used to provision the hosts by using Ansible modules for provisioning the VMs. These modules usually require delegate_to: localhost so Ansible will not try to connect to the hosts or run anything on them (as they don’t exist yet). gather_facts: false in the first play is likewise to prevent Ansible to gather facts on non existing hosts. The second play is regular play used to configure the hosts that are now provisioned by this point. It will also gather the facts of newly provisioned hosts. All your variables are available in both of your plays.

Since the first play will run in parallel, you can use throttle: parameter to limit the number of hosts that will be provisioned in parallel if your VM platform cannot handle huge load.

Update: Just to be clear, what my example shows is one playbook (file) with two plays, not two playbooks… but they can be separate if you really want.

1 Like

@bvitnik good call on the gather_facts: false bit. I’d forgotten about that in my initial answer. And your explanation is really spot on.

1 Like

Thank you All for replying.

There are some great techniques within the answers provided - some that I didn’t know/consider (and will no doubt end up using elsewhere) and some that I did know but had forgotten about.

In the end, because all of the facts for the soon-to-exist VM already exist in my Inventory file, the simplest solution to obtaining those fact was with {{hostvars[New_VM_FQDN]...}} statements and a vars_prompt: New_VM_FQDN for the new VM’s FQDN at the start of the Playbook.

Again, thanks to all of you who took the time to reply - I really appreciate it.

Cheers

Dulux-Oz