ansibleansible-2.x

How to run a role on a host dynamically determined at runtime


I'm looking to migrate app config from one host to another. The tasks to be ran on either host differ significantly that they warrant their own roles. Ideally, I'm looking for the target host to be determined dynamically from a web call rather than supplied to the playbook.

Unfortunately, delegate_to has been restricted due to company policy, so that's not an option. Instead, I'm looking to provide the target host as a variable to hosts: but either it doesn't match or it complaints that the variable is undefined.

Simplified playbook

- hosts: all
  tasks:
    - name: Get target host
      uri:        
        url: http://url_link.com/get_gateways.cgi?name={{ target_gateway }}
        timeout: 5
        return_content: true
      register: connection_string

    - name: store target host as fact
      set_fact:
        target_host: "{{ connection_string.content }}"

    - name: debug connection string
      debug:
        var: target_host

    - debug:
        var=hostvars[inventory_hostname]["connection_string"].content


- hosts: target_host  # Case 1
- hosts: "{{ hostvars[inventory_hostname]['connection_string'].content }}" #Case 2
  tasks:
    - import_role:
        name: gateway_migration

Output when using target_host:

PLAY [all] ************************************************************************************************************************************************************************************************************************************************************************************************************************************************

TASK [Initialising the host specific facts only.] *********************************************************************************************************************************************************************************************************************************************************************************************************
ok: [myhost]

TASK [Get target host] ************************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [myhost]

TASK [store target host as fact] **************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [myhost]

TASK [debug] **********************************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [myhost] => {
    "target_host": [
        "target-1"
    ]
}

TASK [debug] **********************************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [sd-a6l1-kupa.nam.nsroot.net] => {
    "hostvars[inventory_hostname][\"connection_string\"].content": "target-1"
}

[WARNING]: Could not match supplied host pattern, ignoring: target_host

PLAY [target_host] ****************************************************************************************************************************************************************************************************************************************************************************************************************************************
skipping: no hosts matched

And for case 2 (It's pretty much the same error msg when substituting the hostvars dictionary with target_host). In both cases it claims the variable is undefined:

ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: 'inventory_hostname' is undefined. 'inventory_hostname' is undefined

In reality I'm running a regex on the returned string from the url, and when trying without the "jinja syntax", so removing the quotes and curly braces I get the following:

[WARNING]: Could not match supplied host pattern, ignoring: hostvars[inventory_hostname]['connection_string'].content | regex_search('(.*?)~(.*?)~(.*?)~(.*?)'
[WARNING]: Could not match supplied host pattern, ignoring: '\\1')

Solution

  • Q: "... looking for the target host to be determined dynamically from a web call ..."

    For an inventory example.ini

    [roles:children]
    control
    managed
    
    [control]
    controlnode.example.com
    
    [managed]
    remotenode.example.com
    managednode.example.com
    

    a minimal example playbook example.yml

    ---
    - hosts: control
      become: false
      gather_facts: false
    
      tasks:
    
      - name: Get target host
        uri:
          url: "{{ URL }}"
          return_content: true
        register: response
        vars:
          URL: http://www.example.com
    
      - name: Determined dynamically from a web call
        add_host:
          hostname: "{{ response.content | regex_search('www(.*?)org') }}"
          group: managed
    
    - hosts: managed
      become: false
      gather_facts: false
    
      tasks:
    
      - name: Target hosts
        debug:
          msg: "{{ inventory_hostname }}"
    

    called via

    ansible-playbook --inventory example.ini example.yml
    

    will result into an output of

    PLAY [control] **********************************
    
    TASK [Get target host] **************************
    ok: [controlnode.example.com]
    
    TASK [Determined dynamically from a web call] ***
    changed: [controlnode.example.com]
    
    PLAY [managed] **********************************
    
    TASK [Target hosts] *****************************
    ok: [remotenode.example.com] =>
      msg: remotenode.example.com
    ok: [managednode.example.com] =>
      msg: managednode.example.com
    ok: [www.iana.org] =>
      msg: www.iana.org