By using AWS re:Post, you agree to the Terms of Use

How to connect a Load balancer and an Interface VPC Endpoint together using CDK?


Acronym legend:

  • ALB - ApplicationLoadBalancer
  • ATG - ApplicationTargetGroup aka Target Group
  • VPC - Virtual Private Cloud

Our situation: Using the AWS Console manually, it was shown that using Route 53 to an ALB (Application Load Balancer) to a private Interface VPC Endpoint to a private REST API-Gateway to a private Lambda works well. (ALB and a gateway Custom-domain-name exist due to https and the needed Certificate)

The ALB needs a Target Group which targets the IP addresses of the Interface VPC Endpoint. (We tried using InstanceIdTarget with the endpoint's vpcEndpointId, but that failed with the error Instance ID 'vpce-WITHWHATEVERHERE' is not valid )

Using CDK, we created the following (among other things) using the aws_elasticloadbalancingv2 module:

  • ApplicationLoadBalancer (ALB)
  • ApplicationTargetGroup (ATG) aka Target Group

We added a listener to the ALB. We added the Target Group to the listener. It’s not clear how to get the IP addresses from the VPC endpoint. We want to add the IP addresses to the ATG aka Target Group using the targets property.

How to get the IP addresses of the Interface VPC Endpoint via CDK?

A sample of resources we've used:

We're using the latest available as of this writing (AWS CDK 2.5.0)

1 Answer
Accepted Answer

You'll need to use a Custom Resource to (call the API to) get the IP of the endpoint's network interface(s) as CloudFormation doesn't expose it as a return value, which means CDK can't offer it as a property.

(Note: this code was originally written for CDK ~1.40, but should still work or be easily updatable.)

Assuming you're creating the VPC Endpoint like this:

    // note: creates two endpoints because this VPC has two private subnets
    const VPCEndpoints = new ec2.InterfaceVpcEndpoint(this, "APIEndpoint", {
      vpc: my_vpc,
      service: ec2.InterfaceVpcEndpointAwsService.APIGATEWAY,
      subnets: { subnetType: ec2.SubnetType.PRIVATE },

Then you can create a custom resource to call the DescribeNetworkEndpoints API on them. This will be a Lambda function which CloudFormation will deploy and then invoke, and the return value from the function will have the API response in.

CDK has a helper which makes it easy to deploy a custom resource that just calls an AWS API, so we can use that. (CDK writes a little nodeJS function for us to do it, which is why I've linked to the JS SDK above.)

    const eni = new custom_resources.AwsCustomResource(
        onCreate: {
          service: "EC2",
          action: "describeNetworkInterfaces",
          parameters: {
            NetworkInterfaceIds: VPCEndpoints.vpcEndpointNetworkInterfaceIds,
          physicalResourceId: PhysicalResourceId.of(,
        onUpdate: {
          service: "EC2",
          action: "describeNetworkInterfaces",
          parameters: {
            NetworkInterfaceIds: VPCEndpoints.vpcEndpointNetworkInterfaceIds,
          physicalResourceId: PhysicalResourceId.of(,
        policy: {
          statements: [
            new iam.PolicyStatement({
              actions: ["ec2:DescribeNetworkInterfaces"],
              resources: ["*"],

We can now use the getResponseField() method on the eni object to read the response values from the API call, and then use them to create an ALB Target Group.

    // note: two ENIs in our endpoint as above, so we can get two IPs out of the response
    const ip1 = eni.getResponseField("NetworkInterfaces.0.PrivateIpAddress");
    const ip2 = eni.getResponseField("NetworkInterfaces.1.PrivateIpAddress");

    const ALBTargetGroup = new elbv2.ApplicationTargetGroup(this, "ALBTG", {
      port: 443,
      targetType: elbv2.TargetType.IP,
      vpc: my_vpc,

    ALBTargetGroup.addTarget(new elbv2_targets.IpTarget(ip1));
    ALBTargetGroup.addTarget(new elbv2_targets.IpTarget(ip2));

The target group can then just be used in an ALB Listener as usual.

answered 9 months ago
  • @James_S Your solution for getting the ENI IPs work great, but I can't seem to figure out why the health checks always return Unhealthy (Request timed out) and when I make a request through the LB, it returns {message:"Forbidden"}.

  • I have the same issue as you do yargnawh but I think the reason is that enis doesn't have an open service that listen or wait for request to response

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions