How to properly iterate over an array and set_fact when the array can be variable length?

I’m trying to use the efs module to create EFS volumes dynamically in AWS/EC2 by getting the list of subnets/security groups for a VPC. My problem is some VPCs have 2 subnets, some have 3, etc - so the tasks below fail.

I’m struggling on how to get the count of subnets and looping over it to set the “efs_targets” fact properly to send to the efs module

`

  • name: Gather subnet facts
    ec2_vpc_subnet_facts:
    profile: “{{ aws_account_name }}”
    region: “{{ region }}”
    filters:
    vpc-id: “{{ vpc_id }}”
    “tag:Public”: “True”
    register: subnet_facts

  • name: Generate EFS Targets
    ec2_group_facts:
    region: “{{ region }}”
    profile: “{{ aws_account_name }}”
    filters:
    group-name:

  • group1

  • group2
    vpc-id: “{{ vpc_id }}”
    register: sgfacts

  • set_fact:
    efs_security_groups: ‘[ “{{ sgfacts.security_groups.0.group_id }}”, “{{ sgfacts.security_groups.1.group_id }}” ]’

  • set_fact:
    #This works only if there are 3 subnets
    efs_targets:

  • subnet_id: “{{ subnet_facts.subnets.0.id }}”
    security_groups: “{{ efs_security_groups }}”

  • subnet_id: “{{ subnet_facts.subnets.1.id }}”
    security_groups: “{{ efs_security_groups }}”

  • subnet_id: “{{ subnet_facts.subnets.2.id }}”
    security_groups: “{{ efs_security_groups }}”

`

I tried permutations of:

`

  • set_fact:
    efs_targets: "{% for i in range(0,(subnet_facts.subnets)| length) %}
  • subnet_id: {{ subnet_facts.subnets.i.id }}
    security_groups: {{ efs_security_groups }}
    {% endfor %}"

`

But it fails with ‘list object’ has no attribute ‘i’

When you write it like that it means literately i not the variable i, you need to enclose the variable in square brackets.

  {{ subnet_facts.subnets[i].id }}

Ah…it’s always the little things that get me. Thanks for for that, but there is still something missing/wrong…

Doesn’t work:

`

  • set_fact:

efs_targets: '{% for i in range(0,(subnet_facts.subnets)| length) %}

  • subnet_id: {{ subnet_facts.subnets[i].id }}
    security_groups: {{ efs_security_groups }}{% endfor %}’
  • debug:
    var: efs_targets

TASK [set_fact] ************************************************************************************
ok: [111.00.222.14] => {
“ansible_facts”: {},
“changed”: false
}

TASK [debug] ***************************************************************************************
ok: [111.00.222.14] => {
“efs_targets”: “VARIABLE IS NOT DEFINED!”
}

`

But this does (but only when there are 3 subnets, of course) so I know subnet_facts is being generated correctly…

`

  • set_fact:
    efs_targets:

  • subnet_id: “{{ subnet_facts.subnets.0.id }}”
    security_groups: “{{ efs_security_groups }}”

  • subnet_id: “{{ subnet_facts.subnets.1.id }}”
    security_groups: “{{ efs_security_groups }}”

  • subnet_id: “{{ subnet_facts.subnets.2.id }}”
    security_groups: “{{ efs_security_groups }}”

  • debug:
    var: efs_targets

TASK [set_fact] ************************************************************************************
ok: [111.00.222.14] => {
“ansible_facts”: {
“efs_targets”: [
{
“security_groups”: [
“sg-VwysuqEM”,
“sg-xXUBURyp”
],
“subnet_id”: “subnet-bETYLaZY”
},
{
“security_groups”: [
“sg-VwysuqEM”,
“sg-xXUBURyp”
],
“subnet_id”: “subnet-werigAdE”
},
{
“security_groups”: [
“sg-VwysuqEM”,
“sg-xXUBURyp”
],
“subnet_id”: “subnet-qXeWEbEq”
}
]
},
“changed”: false
}

TASK [debug] ***************************************************************************************
ok: [111.00.222.14] => {
“efs_targets”: [
{
“security_groups”: [
“sg-VwysuqEM”,
“sg-xXUBURyp”
],
“subnet_id”: “subnet-bETYLaZY”
},
{
“security_groups”: [
“sg-VwysuqEM”,
“sg-xXUBURyp”
],
“subnet_id”: “subnet-werigAdE”
},
{
“security_groups”: [
“sg-VwysuqEM”,
“sg-xXUBURyp”
],
“subnet_id”: “subnet-qXeWEbEq”
}
]
}

`

Ok… sorted this out in a different way after much trial and error (and an upgrade from ansible 2.4 to 2.5):

`

  • set_fact:
    my_security_groups:
    ‘{{ sgfacts.security_groups | map(attribute=“group_id”) | list }}’

  • set_fact:
    my_subnets:
    ‘{{ subnet_facts.subnets | map(attribute=“id”) | list }}’

  • name: Create loop fact
    set_fact:
    loop_item:
    subnet_id: “{{ item }}”
    security_groups: “{{ my_security_groups }}”
    loop: “{{ my_subnets }}”
    register: loop_list

  • name: Create EFS target list
    set_fact:
    efs_targets: “{{ loop_list.results | map(attribute=‘ansible_facts.loop_item’) | list }}”
    `