amacleod
amacleod

Reputation: 1560

Can I use Jinja2 `map` filter in an Ansible play to get values from an array of objects?

I have a playbook for creating some EC2 instances and then doing some stuff with them. The relevant pieces are approximately like:

- name: create ec2 instances
  ec2:
    id: '{{ item.name }}'
    instance_type: '{{ item.type }}'
  register: ec2
  with_items: '{{ my_instance_defs }}'
- name: wait for SSH
  wait_for:
    host: '{{ item.instances[0].private_ip }}'
    port: 22
  with_items: '{{ ec2.results }}'

This works as intended, but I am not especially happy with the item.instances[0].private_ip expression, partly because it shows really large objects in the play summary. I would love to have the with_items part just be an array of IP addresses, rather than an array of objects with arrays of objects inside them. In Python, I would just do something like:

ips = [r['instances'][0]['private_ip'] for r in ec2['results']]

And then I would use with_items: '{{ ips }}' in the second task.

Is there a way I can do the same thing using a J2 filter in the YAML of the play? Seems like http://docs.ansible.com/ansible/playbooks_filters.html#extracting-values-from-containers might be helpful, but I think that presupposes I have an array of keys/indices/whatever.

Upvotes: 17

Views: 33594

Answers (2)

Konstantin Suvorov
Konstantin Suvorov

Reputation: 68269

map filter it your friend here.

Something like this:

with_items: "{{ ec2.results | map(attribute='instances') | map('first') | map(attribute='private_ip') | list }}"

The code above is not tested.
You may want to try with debug first and gradually add maps to get required result.
Don't forget to put | list at the end to make your map readable.

Upvotes: 20

mpechner
mpechner

Reputation: 139

My example is pulled from my playbook removing a autoscaling ecs cluster. I modified the above answer to get mine working.

- name: get list of instances in ASG
  ec2_instance_facts:
    filters:
      "tag:aws:autoscaling:groupName": "{{item.name}}-{{stack}}-scalinggroup"
  register: asg_host_list

- name: list ecs info
  debug:
    msg: "{{asg_host_list}}"

- name: get just hosts id's
  set_fact:
    hostlist: "{{ asg_host_list.instances | map(attribute='instance_id') | list }}"

For my use hostlist can be fed directly into ecs_instance since it takes a list of instance ids to process.

So, this is tested and works.

Upvotes: 0

Related Questions