I have a VPC endpoint, and I want to retrieve its private IP addresses (to map to an Application Load Balancer through a Target Group).
I do that in two steps:
- First I create an AwsCustomResource to retrieve its Network Interface IDs, using
EC2::describeVpcEndpoints
- Then I create a second CustomResource to retrieve the private IP addresses, using
EC2::describeNetworkInterfaces
(with the previous Network Interface IDs passed in parameters)
Here my CDK code:
// 1st get the VPC endpoint network interface IDs
const vpcEndpointsCall: AwsSdkCall = {
service: "EC2",
action: "describeVpcEndpoints",
outputPaths: [`VpcEndpoints.0.NetworkInterfaceIds`],
parameters: {
VpcEndpointIds: [myVpcEndpointId],
},
physicalResourceId: PhysicalResourceId.of('describeVpcEndpoints'),
}
const vpces = new AwsCustomResource(
this.stack,
`DescribeVpcEndpoints-${this.stackProps.deploy.environmentName}`,
{
onCreate: vpcEndpointsCall,
onUpdate: vpcEndpointsCall,
policy: {
statements: [
new PolicyStatement({
actions: ["ec2:DescribeVpcEndpoints"],
resources: ["*"],
}),
],
},
}
);
const networkInterfaceIds = vpces.getResponseField(`VpcEndpoints.0.NetworkInterfaceIds`)
for (let index = 0; index < this.stackProps.core!.vpc.availabilityZones.length; index++) {
// 2nd get the private IP addresses of the VPC endpoint network interface IDs
const sdkCall: AwsSdkCall = {
service: "EC2",
action: "describeNetworkInterfaces",
outputPaths: [`NetworkInterfaces.${index}.PrivateIpAddress`],
parameters: {
NetworkInterfaceIds: Token.asString(networkInterfaceIds),
Filters: [
{ Name: "interface-type", Values: ["vpc_endpoint"] }
],
},
physicalResourceId: PhysicalResourceId.of('describeNetworkInterfaces'),
}
const eni = new AwsCustomResource(
this.stack,
`DescribeNetworkInterfaces-${this.stackProps.deploy.environmentName}${index}`,
{
onCreate: sdkCall,
onUpdate: sdkCall,
policy: {
statements: [
new PolicyStatement({
actions: ["ec2:DescribeNetworkInterfaces"],
resources: ["*"],
}),
],
},
}
);
targetGroup.addTarget(new IpTarget(Token.asString(eni.getResponseField(`NetworkInterfaces.${index}.PrivateIpAddress`)), targetPort))
}
But during the cdk deploy
, the following error is raised:
CustomResource attribute error: Vendor response doesn't contain VpcEndpoints.0.NetworkInterfaceIds key in object arn:aws:cloudformation:eu-west-3:XXXXXXXXXX:stack/CDK-Core-EC2-XXXXXXXXXX/XXXXXXXXXX|DescribeVpcEndpointsXXXXXXXXXX|XXXXXXXXXX in S3 bucket cloudformation-custom-resource-storage-euwest3
Here is the result of the DescribeVpcEndpoints
call:
{
"VpcEndpoints": [
{
"VpcEndpointId": "XXX",
"VpcEndpointType": "Interface",
"VpcId": "XXX",
"ServiceName": "com.amazonaws.eu-west-3.execute-api",
"State": "available",
"PolicyDocument": "{n "Statement": [n {n "Action": "*", n "Effect": "Allow", n "Principal": "*", n "Resource": "*"n }n ]n}",
"RouteTableIds": [],
"SubnetIds": [
"subnet-XXX",
"subnet-XXX",
"subnet-XXX"
],
"Groups": [
{
"GroupId": "XXX",
"GroupName": "XXX"
},
{
"GroupId": "XXX",
"GroupName": "XXX"
}
],
"IpAddressType": "ipv4",
"DnsOptions": {
"DnsRecordIpType": "ipv4"
},
"PrivateDnsEnabled": true,
"RequesterManaged": false,
"NetworkInterfaceIds": [
"eni-XXX",
"eni-XXX",
"eni-XXX"
],
"DnsEntries": [
{
"DnsName": "vpce-XXX.execute-api.eu-west-3.vpce.amazonaws.com",
"HostedZoneId": "XXX"
},
{
"DnsName": "vpce-XXX-eu-west-3c.execute-api.eu-west-3.vpce.amazonaws.com",
"HostedZoneId": "XXX"
},
{
"DnsName": "vpce-XXX-eu-west-3b.execute-api.eu-west-3.vpce.amazonaws.com",
"HostedZoneId": "XXX"
},
{
"DnsName": "vpce-XXX-eu-west-3a.execute-api.eu-west-3.vpce.amazonaws.com",
"HostedZoneId": "XXX"
},
{
"DnsName": "execute-api.eu-west-3.amazonaws.com",
"HostedZoneId": "XXX"
},
{
"DnsName": "*.execute-api.eu-west-3.amazonaws.com",
"HostedZoneId": "XXX"
}
],
"CreationTimestamp": "XXX",
"Tags": [],
"OwnerId": "XXX"
}
}
So I don’t understand why the getResponseField('VpcEndpoints.0.NetworkInterfaceIds')
is not working.
I expect to have the list of the Network Interface IDs in the field VpcEndpoints.0.NetworkInterfaceIds
.
Any idea?
2
Answers
I answer myself because I found something interesting in the Custom Resource (Lambda) logs:
So I need to manage each Network Interface individually.
The fixed code is now:
It works perfectly now.
Just fixed the same issue: your problem is
Token.asString(networkInterfaceIds)
,Token.asString
cannot handle Arrays.You have to access each array entry itself by index:
Token.asString(networkInterfaceIds)[0]
Token.asString(networkInterfaceIds)[1]
…
Also you should use Token.asString everywhere, so you have to restructure your code a little bit.