I am trying to update my subnet routing table to point to the firewall endpoint in the same AZ using CDK.
I have the firewall created using CfnFirewall
and I can fetch all the firewall endpoints with attrEndpointIds
property. attrEndpointIds
returns a list-encoded token and contains the endpoints for all the AZs.
The unique IDs of the firewall endpoints for all of the subnets that you attached to the firewall.
The subnets are not listed in any particular order. For example: ["us-west-2c:vpce-111122223333", "us-west-2a:vpce-987654321098", "us-west-2b:vpce-012345678901"] .
I currently have the following code
// for each firewall subnet, update its route table to firewall endpoint in the same AZ
for (let i = 0; i < this.vpc.availabilityZones.length; i++) {
const azWithFirewallVpcEndpointId = cdk.Fn.split(':', cdk.Fn.select(i, firewall.attrEndpointIds), 2);
const az = azWithFirewallVpcEndpointId[0];
const firewallVpcEndpointId = azWithFirewallVpcEndpointId[1];
const firewallSubnetInAz = this.vpc.selectSubnets({
subnetGroupName: FIREWALL_SUBNET_GROUP_NAME,
availabilityZones: [az],
onePerAz: true, // there should be 1 and only 1 subnet with the group name in each AZ
}).subnets[0];
(firewallSubnetInAz as ec2.Subnet).addRoute('firewall-route-' + i, {
routerId: firewallVpcEndpointId,
routerType: ec2.RouterType.VPC_ENDPOINT,
});
// const firewallSubnets = this.vpc.selectSubnets({
// subnetGroupName: FIREWALL_SUBNET_GROUP_NAME,
// }).subnets;
// for (let k = 0; k < firewallSubnets.length; k++) {
// if (cdk.Token.compareStrings(az, firewallSubnets[i].availabilityZone)) {
// (firewallSubnets[i] as ec2.Subnet).addRoute('firewall-route-' + i, {
// routerId: firewallVpcEndpointId,
// routerType: ec2.RouterType.VPC_ENDPOINT,
// });
// }
// }
}
I am struggling to update each subnet's routing table with its corresponding firewall endpoint in the same AZ. firewallSubnetInAz
from vpc.selectSubnets()
is undefined
. I am getting the following error. Cannot read property 'addRoute' of undefined
.
How can I map the subnet to the firewall endpoint in the same AZ using CDK?
very late to answer my question but i figured it out with some voodoo magic. Thanks to this blog post that helped me alot to sort AZ in order.
/** this is actually some voodoo magic here...
* we select firewall subnet from vpc, assuming (hoping...) vpc.availabilityZones are in order of AZs
* this means we require `firewall.attrEndpointIds` to be in order.
* however, `firewall.attrEndpointIds` is not in order for some inexplicable reasons.
* https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-networkfirewall/issues/15
* `firewall.attrEndpointIds` is a List-encoded tokens - https://docs.aws.amazon.com/cdk/v2/guide/tokens.html#tokens_list
* in order to sort `firewall.attrEndpointIds` in order of AZs, we need to use a combination of splits and joins.
* AWS AZs are ordered lexicographically starting at `a`, and "assuming" vpc.availabilityZones are returned in order of AZs
* to sort `firewall.attrEndpointIds` as a List-encoded tokens, the following steps are taken:
* 1. join `firewall.attrEndpointIds` as a string token with `,` as delimiter.
* -> "us-west-2c:vpce-111122223333,us-west-2a:vpce-987654321098,us-west-2b:vpce-012345678901"
* 2. split the resulting string token `azWithFirewallVpcEndpointIds` with `a:` for the first AZ (`b:` and `c:` subsequently).
* -> [ "us-west-2c:vpce-111122223333,us-west-2", "vpce-987654321098,us-west-2b:vpce-012345678901" ]
* 3. select 2nd element as it contains the endpoint ID, along with some extra garbage and store it into var `temp`
* -> "vpce-987654321098,us-west-2b:vpce-012345678901"
* 4. split `temp` by `,` as that was the joined list delimiter
* -> [ "vpce-987654321098", "us-west-2b:vpce-012345678901" ]
* 5. select 1st element as it contains the endpoint ID without any extra garbage now
* -> "vpce-987654321098"
* https://carriagereturn.nl/aws/cloudformation/firewall/endpoint/routing/2022/05/22/firewall-endpoints.html
*/
getFirewallEndpointsInOrder(firewall: fw.CfnFirewall): string[] {
const firewallVpcEndpointIds: string[] = [];
const azWithFirewallVpcEndpointIds = cdk.Fn.join(',', firewall.attrEndpointIds);
const aZs = ['a', 'b', 'c', 'd', 'e', 'f']; // max amount of AZs in AWS (us-east-1)
for (let i = 0; i < this.vpc.availabilityZones.length; i++) {
const temp = cdk.Fn.split(`${aZs[i]}:`, azWithFirewallVpcEndpointIds, 2)[1];
const firewallVpcEndpointId = cdk.Fn.split(',', temp, 2)[0];
firewallVpcEndpointIds.push(firewallVpcEndpointId);
}
return firewallVpcEndpointIds;
}
once firewallVpcEndpointIds are sorted by AZ, we can use the following code to map the subnet with its corresponding VPC endpoint in the same AZ.
updateFirewallSubnetRouteTableWithFirewallVpcEndpoint(sortedVpcEndpointIds) {
for (let i = 0; i < this.vpc.availabilityZones.length; i++) {
const firewallSubnetInAz = this.vpc.selectSubnets({
subnetGroupName: FIREWALL_SUBNET_GROUP_NAME,
availabilityZones: [this.vpc.availabilityZones[i]], // assuming availabilityZones are in order?
onePerAz: true, // there should be 1 and only 1 subnet with the group name in each AZ
}).subnets[0];
(firewallSubnetInAz as ec2.Subnet).addRoute('firewall-route-' + i, {
routerId: sortedVpcEndpointIds[i],
routerType: ec2.RouterType.VPC_ENDPOINT,
});
}
}