aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.46k stars 3.83k forks source link

(aws-ec2): `Vpc.from_lookup` Does Not Return Subnet IPv4 CIDR Information #11821

Closed joel-aws closed 3 years ago

joel-aws commented 3 years ago

When referencing a VPC from its ID, despite populating the Runtime Context with all the VPC's information (including the subnets' CIDRs), an error is thrown when trying to get the the IPv4 CIDR block of a given subnet.

Reproduction Steps

from aws_cdk import core, aws_ec2 as ec2

class VpcStack(core.Stack):
    def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        vpc = ec2.Vpc.from_lookup(self, "vpc", vpc_id="vpc-002244cc44997733") # Created using CDK
        private_subnet = vpc.private_subnets[0]
        print(private_subnet.subnet_id)  # This returns "subnet-00bb44dd2244dd33"
        print(private_subnet.ipv4_cidr_block)  # This errors out

app = core.App()
VpcStack(app, "vpctest", env={"account": "111122223333", "region": "us-east-1"})

app.synth()

What did you expect to happen?

I expected the subnet's IPv4 CIDR block to be returned, similar to how it would've been returned if the VPC were created (self.vpc = ec2.Vpc(self, "VPC", cidr="10.0.0.0/16", max_azs=3)) and referenced.

What actually happened?

The IPv4 CIDR was not returned; instead the following error was given:

You cannot reference an imported Subnet's IPv4 CIDR if it was not supplied. Add the ipv4CidrBlock when importing using Subnet.fromSubnetAttributes()

Environment


This is :bug: Bug Report

joel-aws commented 3 years ago

Temporary workaround in a stack when the runtime context for the imported VPC has already been generated:

    def _get_ipv4_cidr(self) -> str:
        """Returns the IPv4 CIDR for a given subnet.

        Temporary fix for: https://github.com/aws/aws-cdk/issues/11821
        """
        try:
            return self.emr_subnet.ipv4_cidr_block
        except:  # noqa
            print("Error getting the subnet via Lookup. Now searching Runtime Context.")
            ctx = json.loads(os.environ["CDK_CONTEXT_JSON"])
            for key in ctx.keys():
                if all(x in key for x in [self._vpc.vpc_id, self.region, self.account]):
                    for subnet_type in ctx[key]["subnetGroups"]:
                        for subnet in subnet_type["subnets"]:
                            if subnet["subnetId"] == self.emr_subnet.subnet_id:
                                return subnet["cidr"]
aktungmak commented 3 years ago

Hello, any idea when this might be fixed? I tried the workaround but it does not work in our build pipeline since the context is not there. I looked at the code for other resources that have a from_* factory method (eg SecurityGroup) to see if that could be reused, but I am not familiar with typescript so I didn't get much further unfortunately.

christophegrall commented 3 years ago

Hello @rix0rrr,

In the subnetGroupToSubnets function in https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-ec2/lib/vpc.ts#L1888

the ipv4CidrBlock attribute is missing :

private subnetGroupToSubnets(subnetGroup: cxapi.VpcSubnetGroup): ISubnet[] {
    const ret = new Array<ISubnet>();
    for (let i = 0; i < subnetGroup.subnets.length; i++) {
      const vpcSubnet = subnetGroup.subnets[i];
      ret.push(Subnet.fromSubnetAttributes(this, `${subnetGroup.name}Subnet${i + 1}`, {
        availabilityZone: vpcSubnet.availabilityZone,
        subnetId: vpcSubnet.subnetId,
        routeTableId: vpcSubnet.routeTableId,

       //  Add this to fix 
       ipv4CidrBlock: vpcSubnet.cidr
       //

      }));
    }
    return ret;
  }
github-actions[bot] commented 3 years ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

Exter-dg commented 5 months ago

If somebody is still using the old version, here is the workaround in typescript:

interface Subnet {
  subnetId: string;
  cidr: string;
}

interface SubnetGroup {
  subnets: Subnet[];
}

interface VpcData {
  vpcId: string;
  subnetGroups: SubnetGroup[];
}

interface ContextData {
  [key: string]: VpcData;
}

...
...

private getIpv4Cidr(vpc: ec2.IVpc, emrSubnet: ec2.ISubnet, region: string, account: string): string {
  try {
    return emrSubnet.ipv4CidrBlock;
  } catch (error) {
    console.error(`Error getting the subnet via Lookup. Error: ${error}`);

    const ctxJson = process.env.CDK_CONTEXT_JSON;
    if (!ctxJson) {
      throw new Error("CDK_CONTEXT_JSON environment variable not found.");
    }

    const ctxData: ContextData = JSON.parse(ctxJson);
    const vpcId = vpc.vpcId;

    for (const key in ctxData) {
      if (key.includes(vpcId) && key.includes(region) && key.includes(account)) {
        const vpcData = ctxData[key];
        for (const subnetGroup of vpcData["subnetGroups"]) {
          for (const subnet of subnetGroup["subnets"]) {
            if(subnet["subnetId"] === emrSubnet.subnetId) {
              console.log("Subnet CIDR: ", subnet["cidr"]);
              return subnet["cidr"];
            }
          }
        }
      }
    }
    console.log("Subnet CIDR not found!!!")
    return ""; // Return empty string if CIDR not found
  }
}