Setting dynamic inventory hostnames from Ansible

amazon ec2amazon-route53ansible

I'm setting up a bunch of up to 150 temporary EC2 servers for a hands-on tutorial session.

I'm successfully creating EC2 inventory dynamically and running roles against the instances that are created to configure everything, but I need to set a straightforward hostname for each one. To that end I have a file that contains a simple list of names that I want to use for the hostnames. This is in my playbook:

---
- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: Provision a set of instances
      ec2:
        key_name: ubuntu
        instance_type: t2.micro
        image: "{{ ami_id }}"
        wait: true
        exact_count: {{ server_count }}
        count_tag:
          Name: Tutorial
        instance_tags:
          Name: Tutorial
        groups: ['SSH', 'Web']
      register: ec2

    - name: Add all instance public IPs to host group
      add_host: hostname={{ item.public_ip }} groups=ec2hosts
      loop: "{{ ec2.instances }}"

    - name: Set a host name for each instance in DNS
      route53:
        zone: {{ tutorial_domain }}
        record: "name.{{ tutorial_domain }}"
        state: present
        type: A
        ttl: 120
        value: {{ item.public_ip }}
        wait: yes
      loop: "{{ ec2.instances }}"

It really comes down to that record: "name.{{ tutorial_domain }}" line – how can I look up a name in my list of names and use it as a hostname, turning name into {{ some_dynamic_name }}?

I have seen the lookup plugins, but they all seem to be focused on looping over the entire contents of some external file – but I'm already looping over the list of servers, and that list may be shorter than the list of names (e.g. I may have only 10 servers). Ideally I want to read the list of names into an array once, then use the index from the server loop to pick the name (i.e. so the 3rd server would get the 3rd name). How do I do that in ansible? Or is there a better approach?

Best Answer

You can use the zip filter to combine your list of instances with a list of names, like this:

---
- hosts: localhost
  gather_facts: false
  vars:
    tutorial_domain: example.com
    ec2:
      instances:
        - public_ip: 1.2.3.4
        - public_ip: 2.3.4.5

    names:
      - blue-duck
      - red-panda

  tasks:
    - debug:
        msg:
          route53:
            zone: "{{ tutorial_domain }}"
            record: "{{ item.1 }}.{{tutorial_domain}}"
            state: present
            type: A
            ttl: 120
            value: "{{ item.0.public_ip }}"
            wait: yes
      loop: "{{ ec2.instances|zip(names)|list }}"

In older versions of Ansible, you would accomplish the same thing with a with_together loop.