Looking at the CDK NLB example in the CDK documentation it shows a simple code block of creating a network load balancer using a new VPC in the same stack. There isn’t an example of adding a load balancer to an existing VPC that targets subnets you want from that VPC that I could find.

Here’s the TypeScript example from the documentation. You can see that it’s only passing in the value of a created vpc with the only other option being that it will be an internet facing NLB. Some investigation is needed.


import * as ec2 from '@aws-cdk/aws-ec2';
import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';
import * as autoscaling from '@aws-cdk/aws-autoscaling';

const vpc = new ec2.Vpc(...);

// Create the load balancer in a VPC. 'internetFacing' is 'false'
// by default, which creates an internal load balancer.
const lb = new elbv2.NetworkLoadBalancer(this, 'LB', {
  vpc,
  internetFacing: true
});

First, a lookup needs to happen to create an object for the existing VPC. To do this we need to import aws_ec2 and then use the built in method from the VPC class of from_lookup()

Now for some Python…


from aws_cdk import aws_ec2 as ec2

# Looking up my existing VPC from the ID of vpc-12345678
vpc = ec2.Vpc.from_lookup(scope=self, id='MyExistingVPC', vpc_id='vpc-12345678')

Now that we have a VPC object to work with let’s try to figure out how to add subnets from the documentation of the NetworkLoadBalancer class.

Parameters

        scope (Construct) –

        id (str) –

        cross_zone_enabled (Optional[bool]) – Indicates whether cross-zone load balancing is enabled. Default: false

        vpc (IVpc) – The VPC network to place the load balancer in.

        deletion_protection (Optional[bool]) – Indicates whether deletion protection is enabled. Default: false

        internet_facing (Optional[bool]) – Whether the load balancer has an internet-routable address. Default: false

        load_balancer_name (Optional[str]) – Name of the load balancer. Default: - Automatically generated name.

        vpc_subnets (Optional[SubnetSelection]) – Which subnets place the load balancer in. Default: - the Vpc default strategy.

Here we can see that there’s a ‘vpc_subnets’ parameter. Unlike when working with CloudFormation where you can simply pass a string of the subnet IDs this parameter requires a SubnetSelection object.

Parameters

        availability_zones (Optional[List[str]]) – Select subnets only in the given AZs. Default: no filtering on AZs is done

        one_per_az (Optional[bool]) – If true, return at most one subnet per AZ. Default: false

        subnet_group_name (Optional[str]) – Select the subnet group with the given name. Select the subnet group with the given name. This only needs to be used if you have multiple subnet groups of the same type and you need to distinguish between them. Otherwise, prefer subnetType. This field does not select individual subnets, it selects all subnets that share the given subnet group name. This is the name supplied in subnetConfiguration. At most one of subnetType and subnetGroupName can be supplied. Default: - Selection by type instead of by name

        subnet_name (Optional[str]) – Alias for subnetGroupName. Select the subnet group with the given name. This only needs to be used if you have multiple subnet groups of the same type and you need to distinguish between them.

        subnets (Optional[List[ISubnet]]) – Explicitly select individual subnets. Use this if you don’t want to automatically use all subnets in a group, but have a need to control selection down to individual subnets. Cannot be specified together with subnetType or subnetGroupName. Default: - Use all subnets in a selected group (all private subnets by default)

        subnet_type (Optional[SubnetType]) – Select all subnets of the given type. At most one of subnetType and subnetGroupName can be supplied. Default: SubnetType.PRIVATE (or ISOLATED or PUBLIC if there are no PRIVATE subnets)

Okay, great, looking at the parameters for this object there’s a subnets option, sweet. But wait, this also isn’t a list of id strings. It’s another object type of Subnet. Now we’re getting somewhere, there’s a static method of from_subnet_id() that we can use. Bringing that all together what does that look like?

There are many ways to pass in the values of your subnet ids or do dynamic lookups. I’ll leave that up to you and just show a hard coded list for the purposes of this example.


subnet_id_list = ['subnet-12345', 'subnet-67890', 'subnet-135790']
subnets = [ec2.Subnet.from_subnet_id(self, id + subnet_id, subnet_id) for subnet_id in subnet_id_list]

Now that a list of Subnet objects is created we can pass this list into the SubnetSelection class.


from aws_elasticloadbalancingv2 as elb

elb.NetworkLoadBalancer(
        self,
        id='NLB',
        vpc=vpc, # The object from above vpc from lookup
        vpc_subnets=ec2.SubnetSelection(subnets=subnets)
    )

Hopefully that helps save someone some time and gives a better idea of working with classes in cdk if that’s unfamiliar territory.