Will1v
Will1v

Reputation: 196

Can I build or update hostvars dynamically?

I'd like to define my hosts by their aws_instance_id only and pull data relative to the hosts (private/public IP, DNS hostname, instance type etc) from aws but store it in hostvars as if I'd manually declared all that stuff in the hosts file.

I've tried a number of ways:

none of these seem to work. Whenever I debug print hostvars, I see: "hostname": "{{ ec2_info.results[0].instances[0].public_dns_name }}"

Is there a way to do this? Thanks,

Edit1

I should specify the reason behind trying to do this: I want to declare some hosts in the Ansible repo using their human readable names (eg: DEV-BATCH_1). But tasks which need to remote onto the hosts fail because they try to SSH onto DEV-BATCH_1 instead of the IP or the DNS name. So, I want to dynamically pull the IP from EC2 and use that information to connect.

Example

hosts file:

my_hosts:
  hosts:
    DEV-BATCH_1:
      aws_instance_id: i-xyz
    DEV-QA_1:
      aws_instance_id: i-yzx

host_vars file for each host:

hostname: ""

EC2 info gathering playbook:

  1. For each host, get the aws instance ID and use them to pull EC2 info. Store the results in ec2_info
  2. Loop over ec2_info and build ec2_host_data with fields such as the private IP, instance type etc. This is what I was using so far, but I'd like to inject it back into hostvars
  3. Use set_fact to set the information into hostvars
  4. Print hostvars.
- name: Gather EC2 information and set hostvars
  hosts: localhost
  gather_facts: false
  vars:
    ec2_host_data: {}
  tasks:
    - name: Get EC2 instance info for each host
      amazon.aws.ec2_instance_info:
        instance_ids: "{{ hostvars[item]['aws_instance_id'] }}"
        region: us-east-1
      loop: "{{ groups['all'] }}"
      loop_control:
        loop_var: item
      register: ec2_info
      no_log: false

    - name: Build a dictionary of EC2 instance details
      set_fact:
        ec2_host_data: >-
          {{
            ec2_host_data | combine({
              ec2_result.instances[0].instance_id: {
                'private_ip': ec2_result.instances[0].private_ip_address,
                'private_dns_name': ec2_result.instances[0].private_dns_name,
                'instance_type': ec2_result.instances[0].instance_type,
                'availability_zone': ec2_result.instances[0].placement.availability_zone
              }
            })
          }}
      loop: "{{ ec2_info.results }}"
      loop_control:
        loop_var: ec2_result
      no_log: false

    - name: Add EC2 instances as inventory hosts
      add_host:
        name: "{{ item }}"
        groups: dynamic_ec2_hosts
        ansible_host: "{{ ec2_host_data[item].private_ip }}"
        hostname: "{{ ec2_host_data[item].private_dns_name }}"
        instance_id: "{{ item }}"
        instance_type: "{{ ec2_host_data[item].instance_type }}"
        availability_zone: "{{ ec2_host_data[item].availability_zone }}"
      loop: "{{ ec2_host_data.keys() }}"
      loop_control:
        loop_var: item

    - name: Debug updated hostvars
      debug:
        var: hostvars

The final debug print shows that hostname is still empty:

[...]
            "hostname": "",
            "iam_instance_profile": "SomeRole",
[...]
            "inventory_hostname": "DEV-QA_2",
[...]

Note: I've also tried to add a debug print for ansible facts:

    - name: Print all available facts
      ansible.builtin.debug:
        var: ansible_facts 

but it shows as empty:

TASK [Print all available facts] ***********************************************************************************************************************************************
ok: [localhost] => {
    "ansible_facts": {}
}

Upvotes: 0

Views: 52

Answers (0)

Related Questions