AWS:如何使用 CloudFormation 创建网络负载均衡器目标组?
AWS: how do I create a Network Load Balancer Target Group using CloudFormation?
我们正在使用 CloudFormation 创建网络负载平衡器。目标类型为IP,需要指向VPC端点的IP地址。
因此我们需要创建一个“ip”类型的目标组,并指定一个目标列表:
NetworkLoadBalancerTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub '${AWS::StackName}-NLT'
Port: 443
Protocol: TLS
VpcId: !Ref 'VPC'
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 300
TargetType: ip
Targets: # list of the primary IP addresses of the Network interface(s) associated with the VPC endpoint
- ?????
目标需要是 VPC 端点的 IP 地址。我如何引用这些?
VPC 端点是这样创建的:
VpcEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
SubnetIds:
- !Ref ProtectedSubnetA
- !If [IsProd, !Ref ProtectedSubnetB, !Ref 'AWS::NoValue']
- !If [IsProd, !Ref ProtectedSubnetC, !Ref 'AWS::NoValue']
SecurityGroupIds:
- !Ref SecurityGroupHttpsInInternal
- !Ref SecurityGroupHttpsOutInternal
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api
VpcId: !Ref VPC
PolicyDocument: '{
"Statement": [
{
"Action": "*",
"Effect": "Allow",
"Resource": "*",
"Principal": "*"
}
]
}'
我可以获得这样的网络接口 ID 列表:
!GetAtt VpcEndpoint.NetworkInterfaceIds
但是,这是一个字符串列表。如何获取 ID 列表中每个网络接口的 PrimaryPrivateIpAddress 属性?
为了完整起见,这里是网络负载均衡器和关联侦听器的定义:
NetworkLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: load_balancing.cross_zone.enabled
Value: true
Name: !Sub '${AWS::StackName}-NLB-Protected'
Scheme: internal
Subnets:
- !Ref ProtectedSubnetA
- !If [IsProd, !Ref ProtectedSubnetB, !Ref 'AWS::NoValue']
- !If [IsProd, !Ref ProtectedSubnetC, !Ref 'AWS::NoValue']
Type: network
NetworkLoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref NetworkLoadBalancerTargetGroup
LoadBalancerArn: !Ref NetworkLoadBalancer
Port: '443'
Protocol: TLS
SslPolicy: ELBSecurityPolicy-TLS-1-2-Ext-2018-06
Certificates:
- CertificateArn: !Ref ACMCertificate
However, that's a list of strings. How do I get the PrimaryPrivateIpAddress attribute for each network interface in the list of ids?
如您所述,AWS::EC2::VPCEndpoint
returns NetworkInterfaceIds
而不是 IP 地址。因此,要获得实际的 IP 地址,您必须开发 custom resource.
这将采用 lambda 函数 的形式,您将向其传递 ENI ID。该函数将使用 AWS SDK(例如 boto3)来获取相应的 IP 地址。该函数将 return CFN 的 IP 地址,您将在目标组中使用该地址。
我发现您 post 正在寻找完全相同问题的解决方案。让我分享我的结论
一旦GetAtt
我们无法访问IP列表,那么我们需要编写一个CustomResource来获取它们。
正如 AWS 在此建议的那样 link 我实施了一个 CustomResource 来获取这些 IP。
还有一个相关的 issue.
首先我们需要将 IAM 角色设置为 lambda。请注意,如果没有这些角色,我们将无法在 CloudWatch
上查看日志
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
LambdaPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: LambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:*
- logs:*
Resource: '*'
Roles:
- !Ref LambdaRole
并创建将接收 NetworkId 列表并搜索 NetworkInterface IP 的 lambda 函数:
LambdaFunction:
Type: AWS::Lambda::Function
Condition: IsNotProd
DeletionPolicy: 'Delete'
Properties:
Code:
ZipFile: !Sub |
import cfnresponse
import json
import boto3
def lambda_handler(event, context):
print('REQUEST RECEIVED:\n' + json.dumps(event))
responseData = {}
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
return
if event['RequestType'] == 'Create':
try:
ec2 = boto3.resource('ec2')
enis = event['ResourceProperties']['NetworkInterfaceIds']
for index, eni in enumerate(enis):
network_interface = ec2.NetworkInterface(eni)
responseData['IP' + str(index)] = network_interface.private_ip_address
print(responseData)
except Exception as e:
responseData = {'error': str(e)}
cfnresponse.send(event, context, cfnresponse.FAILED, responseData)
return
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
Handler: index.lambda_handler
Role: !GetAtt LambdaRole.Arn
Runtime: python3.7
Timeout: 10
然后创建自定义资源
GetPrivateIPs:
DependsOn:
- VPCEndpoint
Type: AWS::CloudFormation::CustomResource
Condition: IsNotProd
Properties:
ServiceToken: !GetAtt LambdaFunction.Arn
NetworkInterfaceIds: !GetAtt VPCEndpoint.NetworkInterfaceIds
您终于可以访问这些 IP
Targets:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 10
HealthCheckPort: 443
HealthCheckProtocol: TCP
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 3
UnhealthyThresholdCount: 3
Port: 443
Protocol: TLS
Targets:
- Id: !GetAtt GetPrivateIPs.IP0
Port: 443
- Id: !GetAtt GetPrivateIPs.IP1
Port: 443
TargetType: ip
VpcId: !Ref VPC
我们正在使用 CloudFormation 创建网络负载平衡器。目标类型为IP,需要指向VPC端点的IP地址。
因此我们需要创建一个“ip”类型的目标组,并指定一个目标列表:
NetworkLoadBalancerTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub '${AWS::StackName}-NLT'
Port: 443
Protocol: TLS
VpcId: !Ref 'VPC'
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 300
TargetType: ip
Targets: # list of the primary IP addresses of the Network interface(s) associated with the VPC endpoint
- ?????
目标需要是 VPC 端点的 IP 地址。我如何引用这些?
VPC 端点是这样创建的:
VpcEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
SubnetIds:
- !Ref ProtectedSubnetA
- !If [IsProd, !Ref ProtectedSubnetB, !Ref 'AWS::NoValue']
- !If [IsProd, !Ref ProtectedSubnetC, !Ref 'AWS::NoValue']
SecurityGroupIds:
- !Ref SecurityGroupHttpsInInternal
- !Ref SecurityGroupHttpsOutInternal
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api
VpcId: !Ref VPC
PolicyDocument: '{
"Statement": [
{
"Action": "*",
"Effect": "Allow",
"Resource": "*",
"Principal": "*"
}
]
}'
我可以获得这样的网络接口 ID 列表:
!GetAtt VpcEndpoint.NetworkInterfaceIds
但是,这是一个字符串列表。如何获取 ID 列表中每个网络接口的 PrimaryPrivateIpAddress 属性?
为了完整起见,这里是网络负载均衡器和关联侦听器的定义:
NetworkLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: load_balancing.cross_zone.enabled
Value: true
Name: !Sub '${AWS::StackName}-NLB-Protected'
Scheme: internal
Subnets:
- !Ref ProtectedSubnetA
- !If [IsProd, !Ref ProtectedSubnetB, !Ref 'AWS::NoValue']
- !If [IsProd, !Ref ProtectedSubnetC, !Ref 'AWS::NoValue']
Type: network
NetworkLoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref NetworkLoadBalancerTargetGroup
LoadBalancerArn: !Ref NetworkLoadBalancer
Port: '443'
Protocol: TLS
SslPolicy: ELBSecurityPolicy-TLS-1-2-Ext-2018-06
Certificates:
- CertificateArn: !Ref ACMCertificate
However, that's a list of strings. How do I get the PrimaryPrivateIpAddress attribute for each network interface in the list of ids?
如您所述,AWS::EC2::VPCEndpoint
returns NetworkInterfaceIds
而不是 IP 地址。因此,要获得实际的 IP 地址,您必须开发 custom resource.
这将采用 lambda 函数 的形式,您将向其传递 ENI ID。该函数将使用 AWS SDK(例如 boto3)来获取相应的 IP 地址。该函数将 return CFN 的 IP 地址,您将在目标组中使用该地址。
我发现您 post 正在寻找完全相同问题的解决方案。让我分享我的结论
一旦GetAtt
我们无法访问IP列表,那么我们需要编写一个CustomResource来获取它们。
正如 AWS 在此建议的那样 link 我实施了一个 CustomResource 来获取这些 IP。 还有一个相关的 issue.
首先我们需要将 IAM 角色设置为 lambda。请注意,如果没有这些角色,我们将无法在 CloudWatch
上查看日志LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
LambdaPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: LambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:*
- logs:*
Resource: '*'
Roles:
- !Ref LambdaRole
并创建将接收 NetworkId 列表并搜索 NetworkInterface IP 的 lambda 函数:
LambdaFunction:
Type: AWS::Lambda::Function
Condition: IsNotProd
DeletionPolicy: 'Delete'
Properties:
Code:
ZipFile: !Sub |
import cfnresponse
import json
import boto3
def lambda_handler(event, context):
print('REQUEST RECEIVED:\n' + json.dumps(event))
responseData = {}
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
return
if event['RequestType'] == 'Create':
try:
ec2 = boto3.resource('ec2')
enis = event['ResourceProperties']['NetworkInterfaceIds']
for index, eni in enumerate(enis):
network_interface = ec2.NetworkInterface(eni)
responseData['IP' + str(index)] = network_interface.private_ip_address
print(responseData)
except Exception as e:
responseData = {'error': str(e)}
cfnresponse.send(event, context, cfnresponse.FAILED, responseData)
return
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
Handler: index.lambda_handler
Role: !GetAtt LambdaRole.Arn
Runtime: python3.7
Timeout: 10
然后创建自定义资源
GetPrivateIPs:
DependsOn:
- VPCEndpoint
Type: AWS::CloudFormation::CustomResource
Condition: IsNotProd
Properties:
ServiceToken: !GetAtt LambdaFunction.Arn
NetworkInterfaceIds: !GetAtt VPCEndpoint.NetworkInterfaceIds
您终于可以访问这些 IP
Targets:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 10
HealthCheckPort: 443
HealthCheckProtocol: TCP
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 3
UnhealthyThresholdCount: 3
Port: 443
Protocol: TLS
Targets:
- Id: !GetAtt GetPrivateIPs.IP0
Port: 443
- Id: !GetAtt GetPrivateIPs.IP1
Port: 443
TargetType: ip
VpcId: !Ref VPC