共有VPCをCDKで作成する

#AWS
#CDK
#Python
#共有VPC

この記事では、VPC, Public Subnet, Private SubnetをCIDRを指定した形でCDKでデプロイします。さらにPrivate Subnetを別のAWSアカウントで使えるように共有設定します。

今回作成したい構成図です

共有VPC構成図

今回CIDRを指定してリソースを作成したいため、cdk.context.jsonにCIDR情報をまとめました

cdk.context.json

{ "stack_type": "SampleStack", "service_name": "SampleService", "dev": { "vpc_cidr_block": "10.11.0.0/18", "az_list": [ { "public_subnet": { "cidr_block": "10.11.0.0/25" }, "private_subnets": [ { "label": "account-a", "cidr_block": "10.11.1.0/26" }, { "label": "account-b", "cidr_block": "10.11.1.64/26" } ] }, { "public_subnet": { "cidr_block": "10.11.0.128/25" }, "private_subnets": [ { "label": "account-a", "cidr_block": "10.11.1.128/26" }, { "label": "account-b", "cidr_block": "10.11.1.192/26" } ] } ], "share_accounts": [ { "label": "account-a", "account_id": "XXXXXXXXXXXX" }, { "label": "account-b", "account_id": "YYYYYYYYYYYY" } ] } }

VPC, Public Sunet, Private Subnet, Private Subnetの共有をCDKで定義します

AZごとにPublic Subnet, Private Subnetを定義し、共有先アカウントごとにPrivate SubnetにRAM(Resource Access Manager)を設定します

RAMを使用すると他のアカウントにリソースを共有できますが、使用する条件として同じAWS Organizationsで管理されている必要があります

import os from aws_cdk import aws_ec2 as ec2 from aws_cdk import aws_ram as ram from constructs import Construct stage = os.getenv("STAGE", "dev") class VpcResource(Construct): def __init__( self, scope: Construct, id: str, ): super().__init__(scope, id) ctx_stage = self.node.try_get_context(stage) # VPC vpc = ec2.Vpc( self, "VPC", ip_addresses=ec2.IpAddresses.cidr(ctx_stage["vpc_cidr_block"]), max_azs=2, subnet_configuration=[], nat_gateways=0, ) internet_gateway = ec2.CfnInternetGateway(self, "InternetGateway") ec2.CfnVPCGatewayAttachment( self, "VpcGatewayAttachment", vpc_id=vpc.vpc_id, internet_gateway_id=internet_gateway.ref, ) # PublicSubnet private_subnets = [] nat_gateway = None az_list = ctx_stage["az_list"] for i, az_info in enumerate(az_list): az_num = i + 1 availability_zone = vpc.availability_zones[i] # PublicSubnet if az_info.get("public_subnet") is not None: public_subnet = ec2.Subnet( self, f"PublicSubnet{az_num}", vpc_id=vpc.vpc_id, cidr_block=az_info["public_subnet"]["cidr_block"], availability_zone=availability_zone, ) public_subnet.add_default_internet_route( gateway_id=internet_gateway.ref, gateway_attachment=internet_gateway ) elastic_ip = ec2.CfnEIP(self, f"NatElasticIp{az_num}") nat_gateway = ec2.CfnNatGateway( self, f"NatGateway{az_num}", allocation_id=elastic_ip.attr_allocation_id, subnet_id=public_subnet.subnet_id, ) # PrivateSubnet for private_subnet_info in az_info["private_subnets"]: label = private_subnet_info["label"] private_subnet = ec2.Subnet( self, f"PrivateSubnet{label}{az_num}", vpc_id=vpc.vpc_id, cidr_block=private_subnet_info["cidr_block"], availability_zone=availability_zone, ) private_subnet.add_default_nat_route(nat_gateway_id=nat_gateway.ref) private_subnet_info["private_subnet"] = private_subnet private_subnets.append(private_subnet) # S3 Gateway Endopoint vpc.add_gateway_endpoint( "S3 Gateway Endpoint", service=ec2.GatewayVpcEndpointAwsService.S3, subnets=[ec2.SubnetSelection(subnets=private_subnets)], ) # DynamoDB Gateway Endpoint vpc.add_gateway_endpoint( "DynamoDB Gateway Endpoint", service=ec2.GatewayVpcEndpointAwsService.DYNAMODB, subnets=[ec2.SubnetSelection(subnets=private_subnets)], ) # 共有VPC for account in ctx_stage["share_accounts"]: label = account["label"] private_subnet_ids = [] for az_info in az_list: for private_subnet_info in az_info["private_subnets"]: if label == private_subnet_info["label"]: private_subnet_ids.append( private_subnet_info["private_subnet"].subnet_id ) share_resource_name = f"VpcPrivateSubnetShare{label}" ram.CfnResourceShare( self, share_resource_name, name=share_resource_name, resource_arns=[ f"arn:aws:ec2:{scope.region}:{scope.account}:subnet/{private_subnet_id}" for private_subnet_id in private_subnet_ids ], principals=[account["account_id"]], allow_external_principals=True, )

上記のようにVPCリソース群を一つのファイルに分割定義することで以下のように呼び出すだけでCDKをスッキリ書くことができます

from aws_cdk import ( Stack, ) from constructs import Construct from cdk_resources.custom_constructs.vpc_resource import VpcResource class SampleStack(Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) VpcResource(self, "VpcResource")

共有先アカウントでResource Access Managerのページを開きます

左タブにリソースの共有という項目があるのでクリックすると共有されているリソースを確認することができます

また、VPCのページに行くと共有されたVPCが表示されていることを確認できます