如何使用 Ansible 找到免费的 VPC CIDR 块?

How to find a free VPC CIDR block with Ansible?

我在 AWS 中有一组现有的 VPC,每个都有自己的 CIDR 块:

vpc1: 20.0.0.0/16 vpc2: 20.10.0.0/16

现在,我正在尝试编写一个剧本,它提取现有的 VPC 列表,然后找到一个可用的 CIDR(例如,在上面的示例中,下一个可用的是 20.20.0.0/16,尽管我不太关心保持顺序)。

我知道这样做适用于两个列表:

- 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 }}"

但是,我获取现有 CIDR 列表的方式是:

- 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 }}"

命令返回以下数据(JSON格式):

{
    "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
        }
    ]
}

允许按如下方式获取所有 CIDR 块:

- name: Print filtered VPCs and their subnets
  debug: msg="VPC ID {{ item.VpcId }}, VPC CIDR block {{ item.CidrBlock }}"
  with_items: current_vpcs.Vpcs

但是,由于 "CidrBlock" 是列表项的 属性,我无法在 "when" 语句中使用它,这需要一个列表:

when: "{{ item }}" not in {{ list_of_cidrs }}"

我如何获取每个 "Vpcs" 项目的 "CidrBlock" 属性 并将其变成它自己的列表,以便将其传递给 "when: ... not in ..."声明?

您可以使用 Jinja's map filter.

将 VPC item 的列表变成仅包含 CIDR 块的列表

我不太确定您要完成此剧本的内容,但这里有一个示例,可让您将 "Vpcs" 项列表转换为 [=每个 vpc 项目的 15=]。

您可以看到它正在运行:

- name: Print just the CIDRS
  debug: msg='{{ current_vpcs.Vpcs|map(attribute="CidrBlock")|list }}'

假设您设置了 current_vpcs,上面的行输出

TASK: [Print just the CIDRS] **************************************************
ok: [localhost] => {
    "msg": "[u'20.0.0.0/16', u'20.10.0.0/16']"
 }

完整的工作示例:

---
- 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 }}'

current_vpcs.Vpcs变成列表["20.0.0.0/16", "20.10.0.0/16"]的行是:

set_fact: 
  list_of_cidrs: '{{ current_vpcs.Vpcs|map(attribute="CidrBlock")|list|to_json }}'

注意:有必要使用|list|to_json让Jinja正确地模板化in语句。 map returns 一个生成器,其表示不能用作 Jinja in 语句的参数。可能有更简洁的方法来执行此操作,但使用 |list|to_json 是人们在 This ticket

中找到的解决方案

编辑:添加变量all_available_cidrs以使完整示例更接近原始问题