如何使用Terraform获取AWS负载均衡器(aws_lb)的IP地址

23

问题

在Terraform创建aws_lb时,有没有一种方法可以获取分配的IP地址?

就像AWS文档 - NLB - 查找要放入白名单的私有IP地址中所述,我们可以找到与ELB关联的IP地址。

  1. 打开Amazon EC2控制台
  2. 在导航窗格中,选择“网络接口”。
  3. 在搜索字段中,输入网络负载均衡器的名称。 每个负载均衡器子网都有一个网络接口。
  4. 对于每个网络接口的详细信息选项卡,从“主要专用IPv4 IP”中复制地址。

背景

为了设置安全组以将ELB IP地址添加到白名单中,因为网络负载均衡器没有安全组

考虑使用aws_network_interface,但是它会出现错误。

错误:找不到匹配的网络接口

我认为数据源(datasource)假定资源已经存在,不能用于由Terraform创建的资源。

4个回答

25

仅使用 HCL 实现更优雅的 Terraform 解决方案:

data "aws_network_interface" "lb" {
  for_each = var.subnets

  filter {
    name   = "description"
    values = ["ELB ${aws_lb.example_lb.arn_suffix}"]
  }

  filter {
    name   = "subnet-id"
    values = [each.value]
  }
}

resource "aws_security_group" "lb_sg" {
  vpc_id = var.vpc_id

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "tcp"
    cidr_blocks = formatlist("%s/32", [for eni in data.aws_network_interface.lb : eni.private_ip])
    description = "Allow connection from NLB"
  }
}

来源: https://github.com/terraform-providers/terraform-provider-aws/issues/3007

希望这可以帮助到你。


3
太棒了!我非常喜欢这个。 - Alain O'Dea
2
这是高级的! - Max Lobur
for_each有时可能会导致问题,如果遇到麻烦,请查看@awiechert的解决方案。 - Chiel

9
@user1297406 提供的解决方案会导致异常。 data.aws_network_interface.lb 是包含2个元素的元组。正确的语法是:
data "aws_network_interface" "lb" {
count = length(var.vpc_private_subnets)

  filter {
    name   = "description"
    values = ["ELB ${aws_alb.lb.arn_suffix}"]
  }
  filter {
    name   = "subnet-id"
    values = [var.vpc_private_subnets[count.index]]
  }
}


resource "aws_security_group_rule" "lb_sg" {
  from_port         = 0
  protocol          = "TCP"
  to_port           = 0
  type              = "ingress"
  cidr_blocks = formatlist("%s/32", data.aws_network_interface.lb.*.private_ip)
}

6

使用外部提供商

使用 Python/boto3 从外部提供商调用以获取 NLB IP。

nlb_private_ips.tf

variable "nlb_name" {
}
variable "vpc_id" {
}
variable "region" {
}

data "external" "get_nlb_ips" {
  program = ["python", "${path.module}/get_nlb_private_ips.py"]
  query = {
    aws_nlb_name  = "${var.nlb_name}"
    aws_vpc_id    = "${var.vpc_id}"
    aws_region    = "${var.region}"
  }
}

output "aws_nlb_ip_decoded" {
  value = "${jsondecode(data.external.get_nlb_ips.result.private_ips)}"
}

output "aws_nlb_ip_encoded" {
  value = "${data.external.get_nlb_ips.result.private_ips}"
}

get_nlb_private_ips.py

import boto3
import json
import sys


def json_serial(obj):
    """JSON serializer for objects not serializable by default json code
        Args:
            obj: object to serialize into JSON
    """
    _serialize = {
        "int": lambda o: int(o),
        "float": lambda o: float(o),
        "decimal": lambda o: float(o) if o % 1 > 0 else int(o),
        "date": lambda o: o.isoformat(),
        "datetime": lambda o: o.isoformat(),
        "str": lambda o: o,
    }
    return _serialize[type(obj).__name__.lower()](obj)


def pretty_json(dict):
    """
    Pretty print Python dictionary
    Args:
        dict: Python dictionary
    Returns:
        Pretty JSON
    """
    return json.dumps(dict, indent=2, default=json_serial, sort_keys=True, )


def get_nlb_private_ips(data):
    ec2 = boto3.client('ec2', region_name=data['aws_region'])
    response = ec2.describe_network_interfaces(
        Filters=[
            {
                'Name': 'description',
                'Values': [
                    "ELB net/{AWS_NLB_NAME}/*".format(
                        AWS_NLB_NAME=data['aws_nlb_name'])
                ]
            },
            {
                'Name': 'vpc-id',
                'Values': [
                    data['aws_vpc_id']
                ]
            },
            {
                'Name': 'status',
                'Values': [
                    "in-use"
                ]
            },
            {
                'Name': 'attachment.status',
                'Values': [
                    "attached"
                ]
            }
        ]
    )

    # print(pretty_json(response))
    interfaces = response['NetworkInterfaces']

    # ifs = list(map(lamba index: interfaces[index]['PrivateIpAddresses'], xrange(len(interfaces))))
    # --------------------------------------------------------------------------------
    # Private IP addresses associated to an interface (ENI)
    # Each association has the format:
    #   {
    #     "Association": {
    #       "IpOwnerId": "693054447076",
    #       "PublicDnsName": "ec2-52-88-47-177.us-west-2.compute.amazonaws.com",
    #       "PublicIp": "52.88.47.177"
    #     },
    #     "Primary": true,
    #     "PrivateDnsName": "ip-10-5-1-205.us-west-2.compute.internal",
    #     "PrivateIpAddress": "10.5.1.205"
    #   },
    # --------------------------------------------------------------------------------
    associations = [
        association for interface in interfaces
        for association in interface['PrivateIpAddresses']
    ]

    # --------------------------------------------------------------------------------
    # Get IP from each IP association
    # --------------------------------------------------------------------------------
    private_ips = [
        association['PrivateIpAddress'] for association in associations
    ]

    return private_ips


def load_json():
    data = json.load(sys.stdin)
    return data


def main():
    data = load_json()
    """
    print(data['aws_region'])
    print(data['aws_vpc_id'])
    print(data['aws_nlb_name'])
    """
    ips = get_nlb_private_ips(data)
    print(json.dumps({"private_ips": json.dumps(ips)}))


if __name__ == '__main__':
    main()

使用aws_network_interfaces数据源

在创建了aws_lb之后。

data "aws_network_interfaces" "this" {
  filter {
    name = "description"
    values = ["ELB net/${aws_lb.this.name}/*"]
  }
  filter {
    name = "vpc-id"
    values = ["${var.vpc_id}"]
  }
  filter {
    name = "status"
    values = ["in-use"]
  }
  filter {
    name = "attachment.status"
    values = ["attached"]
  }
}

locals {
  nlb_interface_ids = "${flatten(["${data.aws_network_interfaces.this.ids}"])}"
}

data "aws_network_interface" "ifs" {
  count = "${length(local.nlb_interface_ids)}"
  id = "${local.nlb_interface_ids[count.index]}"
}

output "aws_lb_network_interface_ips" {
  value = "${flatten([data.aws_network_interface.ifs.*.private_ips])}"
}

2
只是进行检查,但如果负载均衡器与这些数据源同时创建,这种做法就行不通了,对吧? - Cícero Neves

1

谢谢,这个像魔法一样奏效! - ashraf minhaj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接