Terraform如何获取aws_lb的IP地址

Posted

技术标签:

【中文标题】Terraform如何获取aws_lb的IP地址【英文标题】:Terraform how to get IP address of aws_lb 【发布时间】:2019-11-04 21:33:42 【问题描述】:

问题

是否有办法在 Terraform 创建 aws_lb 时获取 aws_lb 资源的分配 IP 地址?

和AWS documentation - NLB - To find the private IP addresses to whitelist一样,我们可以找到ELB关联的IP地址。

    打开 Amazon EC2 控制台https://console.aws.amazon.com/ec2/。 在导航窗格中,选择网络接口。 在搜索字段中,输入您的网络负载均衡器的名称。 每个负载平衡器子网有一个网络接口。 在每个网络接口的详细信息选项卡上,复制地址 主要私有 IPv4 IP

背景

为了能够设置安全组以将 ELB IP 地址列入白名单,因为网络负载均衡器不能没有安全组,如 Network Load Balancers don't have Security Groups。

考虑过aws_network_interface,但由于错误而无法使用。

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

另外我认为数据源假设资源已经存在,不能用于 Terraform 创建的资源。

【问题讨论】:

【参考方案1】:

在 Terraform 中仅使用 HCL 的更优雅的解决方案:

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

希望这会有所帮助。

【讨论】:

这真是太棒了!我非常喜欢这个。 这是高级别的!【参考方案2】:

使用外部提供商

使用 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])"

【讨论】:

只是检查,但如果负载均衡器是在创建这些数据源的同时进行的,那将不起作用,对吧?【参考方案3】:

@user1297406 的解决方案导致异常。 data.aws_network_interface.lb is tuple with 2 elements.正确的语法是:

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)

【讨论】:

以上是关于Terraform如何获取aws_lb的IP地址的主要内容,如果未能解决你的问题,请参考以下文章

Terraform 获取 api 网关的 ip 地址

如何使用 Terraform 公开具有公共 IP 地址的 Azure Kubernetes 集群

引用 IP 地址生成配置文件时的 Terraform 循环依赖问题

Terraform 使用数据源输出动态生成字符串

在 terraform IPSec 隧道中使用的 AWS IP 地址(通过 Transit Gateway)

如何用javascript获取客户端的IP地址?