AWS CDK: Create a Subnet and Launch Instance in it With Existing VPC

0

I have a fairly simple task that I am unable to complete with AWS CDK: Create a subnet and launch an instance in it using an existing VPC.

The existing VPC is a CfnVpc that I obtained using the cdk migrate command. Here it is:

class Lab(Stack):

    @property
    def vpc(self) -> ec2.CfnVPC:
        return self._vpc

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(
            scope, construct_id, description="Nat instance with two subnets", **kwargs
        )
        self._vpc = ec2.CfnVPC(
            self,
            "VPC",
            enable_dns_support=True,
            enable_dns_hostnames=True,
            cidr_block=subnetConfig["VPC"]["CIDR"],
            tags=[
                {
                    "key": "Application",
                    "value": self.stack_name,
                },
                {
                    "key": "Network",
                    "value": "PublicA",
                },
            ],
        )

Here are my new resources:

class MyResources(Lab):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        vpc = ec2.Vpc.from_lookup(
            self, "L2VPC", tags={"aws:cloudformation:logical-id": "VPC"}
        )

        publicSubnetB = ec2.Subnet(
            self,
            "PublicSubnetB",
            availability_zone="us-east-1b",
            cidr_block="10.0.2.0/24",
            vpc_id=vpc.vpc_id,
        )
        cdk.Tags.of(publicSubnetB).add("Application", self.stack_name)
        cdk.Tags.of(publicSubnetB).add("Network", "PublicB")

        # These are also exposed from the cdk migrate command via properties. 
        ec2.CfnSubnetRouteTableAssociation(
            self,
            "PublicSubnetBRouteTableAssociation",
            subnet_id=publicSubnetB.subnet_id,
            route_table_id=self.publicRouteTable.ref,
        )
        ec2.CfnSubnetNetworkAclAssociation(
            self,
            "PublicSubnetBNetworkAclAssociation",
            subnet_id=publicSubnetB.subnet_id,
            network_acl_id=self.publicNetworkAcl.ref,
        )

And finally, here's the failing instance:

        instanceB = ec2.Instance(
            self,
            "InstanceB",
            instance_type=ec2.InstanceType.of(
                instance_class=ec2.InstanceClass.BURSTABLE3,
                instance_size=ec2.InstanceSize.MICRO,
            ),
            machine_image=ec2.AmazonLinuxImage(
                generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2
            ),
            vpc=vpc,
            vpc_subnets=ec2.SubnetSelection(
                subnet_type=ec2.SubnetType.PUBLIC, availability_zones=["us-east-1b"]
            ),
            user_data=ec2.UserData.custom(userDataB),
            private_ip_address="10.0.2.119",
            associate_public_ip_address=True,
            security_group=sg,
            user_data_causes_replacement=True,
        )

AWS CDK says that there is no public subnet in az us-east-1b - even though I created it just prior. The only way this works is if I comment out the instance code, run it, clear the context, run it again, and comment back in the instance code. There has got to be a better way!

2개 답변
0
수락된 답변

The solution was very tricky and not well documented in my opinion.

Since I was creating the subnet in the same stack as I used to look up the VPC, I had to use the ec2.Vpc.from_vpc_attributes() class method instead of the ec2.Vpc.from_lookup() class method.

Does not work:

# app.py

vpc = ec2.Vpc.from_lookup(self, "IVpc", region="us-east-1")

# ...[create public subnet ref CfnVpc]

# ...[create instance in public subnet] 

Error Logs

jsii.errors.JavaScriptError: 
  Error: To set 'associatePublicIpAddress: true' you must select Public subnets (vpcSubnets: { subnetType: SubnetType.PUBLIC })
      at new Instance (/tmp/jsii-kernel-wHeWcm/node_modules/aws-cdk-lib/aws-ec2/lib/instance.js:1:5169)
      at Kernel._Kernel_create (/tmp/tmpxiyze867/lib/program.js:10108:25)
      at Kernel.create (/tmp/tmpxiyze867/lib/program.js:9779:93)
      at KernelHost.processRequest (/tmp/tmpxiyze867/lib/program.js:11696:36)
      at KernelHost.run (/tmp/tmpxiyze867/lib/program.js:11656:22)
      at Immediate._onImmediate (/tmp/tmpxiyze867/lib/program.js:11657:46)
      at process.processImmediate (node:internal/timers:478:21)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/ubuntu/acg_nlb_lab/infra/app.py", line 80, in <module>
    MyInfra(app, STACK, env=env)
  File "/home/ubuntu/.pyenv/versions/cdk-3.10/lib/python3.10/site-packages/jsii/_runtime.py", line 118, in __call__
    inst = super(JSIIMeta, cast(JSIIMeta, cls)).__call__(*args, **kwargs)
  File "/home/ubuntu/acg_nlb_lab/infra/app.py", line 55, in __init__
    instanceB = ec2.Instance(
  File "/home/ubuntu/.pyenv/versions/cdk-3.10/lib/python3.10/site-packages/jsii/_runtime.py", line 118, in __call__
    inst = super(JSIIMeta, cast(JSIIMeta, cls)).__call__(*args, **kwargs)
  File "/home/ubuntu/.pyenv/versions/cdk-3.10/lib/python3.10/site-packages/aws_cdk/aws_ec2/__init__.py", line 71951, in __init__
    jsii.create(self.__class__, self, [scope, id, props])
  File "/home/ubuntu/.pyenv/versions/cdk-3.10/lib/python3.10/site-packages/jsii/_kernel/__init__.py", line 334, in create
    response = self.provider.create(
  File "/home/ubuntu/.pyenv/versions/cdk-3.10/lib/python3.10/site-packages/jsii/_kernel/providers/process.py", line 365, in create
    return self._process.send(request, CreateResponse)
  File "/home/ubuntu/.pyenv/versions/cdk-3.10/lib/python3.10/site-packages/jsii/_kernel/providers/process.py", line 342, in send
    raise RuntimeError(resp.error) from JavaScriptError(resp.stack)
RuntimeError: To set 'associatePublicIpAddress: true' you must select Public subnets (vpcSubnets: { subnetType: SubnetType.PUBLIC })

Works:

# app.py

# ...[create public subnet ref CfnVpc]

# ...[create instance in public subnet] 

vpc = ec2.Vpc.from_vpc_attributes(
    self,
    "IVpc",
    vpc_id=self.vpc.ref,
    availability_zones=["us-east-1a", "us-east-1b"],
    public_subnet_ids=[self.publicSubnet.ref, publicSubnetB.subnet_id],
    public_subnet_route_table_ids=[
        self.publicRouteTable.ref,
        self.publicRouteTable.ref,
    ],
)

# ...[able to use L2 IVpc and L2 instance]
jcaws
답변함 한 달 전
0

It's all about dependencies... You have an issue with the timing of resource creation when using AWS CDK. AWS CDK provisions resources in the order they are defined within the construct. However, when you use ec2.Vpc.from_lookup() to obtain a VPC, CDK might not wait for the lookup to complete before moving on to create other resources, resulting in the failure to find the public subnet in availability zone us-east-1b.

Try to use core.Construct to encapsulate the lookup logic and create a custom resource that waits for the lookup to finish.

profile picture
전문가
답변함 한 달 전
profile pictureAWS
전문가
검토됨 한 달 전
  • Hmm... I'm trying to understand this. So if I had something like this?

    app = cdk.App()
    env = cdk.Environment(region="us-east-1", account="289298103482")
    subnet = MySubnet(app, "cfst-1180-subnet-9a638f661a5137e356f02ee9c2db058c", env=env)
    resources = MyResources(app, "cfst-1180-9a638f661a5137e356f02ee9c2db058c", env=env)
    subnet.node.add_dependency(resources)

    In my mind this should work - create the subnet first, then launch the instance into it. But I'm still getting the same RuntimeError: To set 'associatePublicIpAddress: true' you must select Public subnets (vpcSubnets: { subnetType: SubnetType.PUBLIC }) error.

로그인하지 않았습니다. 로그인해야 답변을 게시할 수 있습니다.

좋은 답변은 질문에 명확하게 답하고 건설적인 피드백을 제공하며 질문자의 전문적인 성장을 장려합니다.

질문 답변하기에 대한 가이드라인

관련 콘텐츠