I've got an existing set of VPC's in AWS, each with their own CIDR blocks:
vpc1: 20.0.0.0/16 vpc2: 20.10.0.0/16
Now, I'm trying to write a playbook, which pulls in the existing list of VPC's and then finds an available CIDR (e.g. in the above example, the next available one would be 20.20.0.0/16, though I'm not too concerned about keeping it sequential).
I know that doing this works with two lists:
- name: Loop over all possible CIDRs
debug: msg="Found a free CIDR {{ item }}"
with_items: all_potential_cidrs
when: "\"{{ item }}\" not in {{ currently_used_cidrs }}"
However, the way I'm getting the list of existing CIDRs is:
- name: Get list of VPCs and their CIDR blocks
command: aws ec2 describe-vpcs --output json
register: cli_output
- name: Register variables
set_fact:
current_vpcs: "{{ cli_output.stdout | from_json }}"
The command gives the following data back (in JSON format):
{
"Vpcs": [
{
"VpcId": "vpc-4aad0c23",
"InstanceTenancy": "default",
"Tags": [
{
"Value": "vpc1",
"Key": "Name"
}
],
"State": "available",
"DhcpOptionsId": "dopt-ff6b238f",
"CidrBlock": "20.0.0.0/16",
"IsDefault": false
},
{
"VpcId": "vpc-d4101abc",
"InstanceTenancy": "default",
"Tags": [
{
"Value": "vpc2",
"Key": "Name"
}
],
"State": "available",
"DhcpOptionsId": "dopt-eaaab38c",
"CidrBlock": "20.10.0.0/16",
"IsDefault": false
}
]
}
Which allows getting all CIDR blocks as follows:
- name: Print filtered VPCs and their subnets
debug: msg="VPC ID {{ item.VpcId }}, VPC CIDR block {{ item.CidrBlock }}"
with_items: current_vpcs.Vpcs
However, as "CidrBlock" is a property of a list item, I'm unable to use it in the "when" statement, which requires a List:
when: "{{ item }}" not in {{ list_of_cidrs }}"
How can I take the "CidrBlock" property of each of the "Vpcs" items and turn it into it's own list, in order to pass it to the "when: ... not in ..." statement?
You can turn the list of VPC item
s into a list of just the CIDR blocks using Jinja's map filter.
I'm not exactly sure what you're trying to accomplish what this playbook, but here's an example that allows you to convert the list of "Vpcs" items into a list of strings that are the CidrBlock
of each vpc item.
You can see it working in:
- name: Print just the CIDRS
debug: msg='{{ current_vpcs.Vpcs|map(attribute="CidrBlock")|list }}'
Assuming you have current_vpcs
set, the line above outputs
TASK: [Print just the CIDRS] **************************************************
ok: [localhost] => {
"msg": "[u'20.0.0.0/16', u'20.10.0.0/16']"
}
Full working example:
---
- name: ok
hosts: localhost
gather_facts: no
vars:
all_available_cidrs:
- "20.0.0.0/16"
- "20.10.0.0/16"
- "20.20.0.0/16"
tasks:
- name: Get list of VPCs and their CIDR blocks
command: aws ec2 describe-vpcs --output json
register: cli_output
- name: Register variables
set_fact:
current_vpcs: "{{ cli_output.stdout | from_json }}"
- name: Print VPCs and their subnets
debug: msg="VPC ID {{ item.VpcId }}, VPC CIDR block {{ item.CidrBlock }}"
with_items: current_vpcs.Vpcs
- name: extract list_of_cidrs into fact
set_fact:
list_of_cidrs: '{{ current_vpcs.Vpcs|map(attribute="CidrBlock")|list|to_json }}'
- name: Print just the CIDRS
debug: var=list_of_cidrs
- name: Print available unused cidrs
debug: msg="available unused VPC CIDR block {{ item }}"
with_items: all_available_cidrs
when: '"{{ item }}" not in {{ list_of_cidrs }}'
The line that turns current_vpcs.Vpcs
into the list ["20.0.0.0/16", "20.10.0.0/16"]
is:
set_fact:
list_of_cidrs: '{{ current_vpcs.Vpcs|map(attribute="CidrBlock")|list|to_json }}'
Note: it is necessary to use |list|to_json
to get Jinja to template the in
statement properly. map
returns a generator whose representation can't be used as the argument of a Jinja in
statement. There may be a cleaner way to do this, but using |list|to_json
is the solution people found in This ticket
Edit: add variable all_available_cidrs
to make the full example closer match the original question