UNPKG

raindancers-network

Version:
585 lines 88.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Evpc = void 0; const path = require("path"); const cdk = require("aws-cdk-lib"); const aws_cdk_lib_1 = require("aws-cdk-lib"); /** * * @param ip A ip address in dotted decimal format * @returns an integer representing the ip address */ function ipToInt32(ip) { const ipSplit = ip.split('.'); var ipInt = 0; ipSplit.forEach((value, index) => { var octect = value; //ipInt += octect << (24-index*8); ipInt += octect * Math.pow(2, (24 - index * 8)); }); return ipInt; } /** * Extends the ec2.Vpc construct to provide additional functionality * - support for using AWS IPAM * - methods for integration * - Flow logs and Athena Querys * - Create and share 53 zones */ class Evpc extends aws_cdk_lib_1.aws_ec2.Vpc { constructor(scope, id, props = {}) { // make a copy of props as its properties are read Only. ( Jsii restriction ) // https://bobbyhadz.com/blog/typescript-change-readonly-to-mutable const vpcProps = props; // do some validation of props, for sanity if (props.cidr && props.ipamPoolId) { throw new Error('can only have one of cidr, or ipamPoolId'); } // if using an IPAM pool, we will set the cidr to 0/0, so, network builder can build the subnets which we then later manipulate if (props.ipamPoolId) { vpcProps.cidr = `0.0.0.0/${props.netmaskLength}`; } super(scope, id, vpcProps); //use escape hatches to remove the Cidr Block and set the Ipam Masks. const CfnVPC = this.node.defaultChild; CfnVPC.addPropertyDeletionOverride('CidrBlock'); CfnVPC.addPropertyOverride('Ipv4IpamPoolId', props.ipamPoolId); CfnVPC.addPropertyOverride('Ipv4NetmaskLength', props.netmaskLength); // Overide the subnets [this.privateSubnets, this.isolatedSubnets, this.publicSubnets].forEach((subnetType) => { subnetType.forEach((subnet) => { var subnetInt = ipToInt32(subnet.ipv4CidrBlock.split('/')[0]); var subnetMask = subnet.ipv4CidrBlock.split('/')[1]; var subnetPostion = subnetInt / 2 ** (32 - subnetMask); var cfnSubnet = subnet.node.defaultChild; cfnSubnet.addPropertyOverride('CidrBlock', cdk.Fn.select(subnetPostion, cdk.Fn.cidr(CfnVPC.attrCidrBlock, subnetPostion + 1, `${32 - subnetMask}`))); }); // end of subnetType }); // end of all subnets // flow logs if (!props.disableFlowlog) { // write to a shared VPC Flow log, that is specificed in the cdk.context const s3LogBucket = aws_cdk_lib_1.aws_s3.Bucket.fromBucketName(this, 'flowlogbucket', this.node.tryGetContext('flowlogbucket')); // create a flow log for the vpc const flowlog = new aws_cdk_lib_1.aws_ec2.FlowLog(this, 'VpcFlowLogs', { destination: aws_cdk_lib_1.aws_ec2.FlowLogDestination.toS3(s3LogBucket, 'VpcFlowLogs', { fileFormat: aws_cdk_lib_1.aws_ec2.FlowLogFileFormat.PARQUET, hiveCompatiblePartitions: false, perHourPartition: true, }), trafficType: aws_cdk_lib_1.aws_ec2.FlowLogTrafficType.ALL, flowLogName: 'sharedVpcFlowLogs', resourceType: aws_cdk_lib_1.aws_ec2.FlowLogResourceType.fromVpc(this), }); // allow for more grandular flowlog at the minimum increment. if (props.oneMinuteFlowLogs !== undefined) { const CfnFlowLog = flowlog.node.defaultChild; CfnFlowLog.addPropertyOverride('MaxAggregationInterval', 60); } // athena results bucket const athenaResultsBucket = new aws_cdk_lib_1.aws_s3.Bucket(this, 'AthenaFlowLogResults', { blockPublicAccess: aws_cdk_lib_1.aws_s3.BlockPublicAccess.BLOCK_ALL, bucketName: `athena-${props.vpcName?.toLowerCase()}`, removalPolicy: cdk.RemovalPolicy.DESTROY, }); // set up athena querys with a custom resource const athenaLogsHandler = new aws_cdk_lib_1.aws_lambda.Function(this, 'Function', { code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/evpc')), runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'flowlogintegration.on_event', }); athenaLogsHandler.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: [ '*', 'ec2:ListCoreNetworks', 'ec2:GetFlowLogsIntegrationTemplate', 'cloudformation:CreateStack', 'cloudformation:DeleteStack', ], })); new cdk.CustomResource(this, 'MyCustomResource', { serviceToken: new aws_cdk_lib_1.custom_resources.Provider(this, 'flowlogBuilderCR', { onEventHandler: athenaLogsHandler, }).serviceToken, properties: { AthenaBucket: athenaResultsBucket.bucketArn, FlowLogId: flowlog.flowLogId, VpcName: props.vpcName, }, }); } // if this is a VPC that we attach to the the core vpc, create a custom lookup to do lookups of the corewan if (props.attachToCoreNetworkSegment) { const lookupIdLambda = new aws_cdk_lib_1.aws_lambda.Function(this, 'lookupIdLambda-evpc', { runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'get_core_network_id.on_event', code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/evpc')), }); lookupIdLambda.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: ['networkmanager:ListCoreNetworks'], })); const networkManagerProvider = new aws_cdk_lib_1.custom_resources.Provider(this, 'NetworkManagerProvider', { onEventHandler: lookupIdLambda, }); this.lookUpProvider = networkManagerProvider; } // create an internal zone for the vpc if (props.r53InternalZoneName) { // create a new internal zone this.privateR53Zone = new aws_cdk_lib_1.aws_route53.PrivateHostedZone(this, 'privatednszone', { zoneName: props.r53InternalZoneName, vpc: this, }); this.privateR53ZoneId = this.privateR53Zone.hostedZoneId; } // if this is the centralResolvingV VPC we dont' need to associate this zone with itself. // This needs completing. this.centralResolvingVpc = false; if (props.centralResolvingVpc === true) { this.centralResolvingVpc = true; } } ; // end of constructor /** * Attach the VPC to a cloud wan segment * @param coreNetworkName * @param segment * @returns transport attachment id */ attachToCloudWan(coreNetworkName, segment) { // get the parameters from the core. const coreNetwork = new cdk.CustomResource(this, 'idfinderCR', { serviceToken: this.lookUpProvider.serviceToken, properties: { CoreNetworkName: coreNetworkName, }, }); const linknetsubnetarns = []; const linknetSelection = this.selectSubnets({ subnetGroupName: 'linknet' }); for (const subnet of linknetSelection.subnets) { linknetsubnetarns.push(`arn:aws:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:subnet/${subnet.subnetId}`); } const transportAttachmentId = new aws_cdk_lib_1.aws_networkmanager.CfnVpcAttachment(this, 'attachtowan', { coreNetworkId: coreNetwork.getAtt('CoreNetworkId'), subnetArns: linknetsubnetarns, tags: [ { key: 'NetworkSegment', value: segment, }, ], vpcArn: this.vpcArn, }); return transportAttachmentId.attrAttachmentId; } // end of attachToCloudWan /** * Add a route to routing tables attached to the private subnets. * @param destinationCidr cidr eg, 0.0.0.0/0 * @param coreNetworkId */ addRouteForPrivateSubnetstoCloudWan(destinationCidr, coreNetworkId) { var subnetSelection = []; try { const subnetSelectionPrivateIsolated = this.selectSubnets({ subnetType: aws_cdk_lib_1.aws_ec2.SubnetType.PRIVATE_ISOLATED }); subnetSelection = [...subnetSelection, ...subnetSelectionPrivateIsolated.subnets]; } catch (error) { console.log('there are no Private Subnets'); } try { const subnetSelectionPrivateIsolated = this.selectSubnets({ subnetType: aws_cdk_lib_1.aws_ec2.SubnetType.PRIVATE_WITH_EGRESS }); subnetSelection = [...subnetSelection, ...subnetSelectionPrivateIsolated.subnets]; } catch (error) { console.log('there are noSubnets with Egress'); } for (const subnet of subnetSelection) { // there is no cloudformation method for a cloudwan yet! const addRoutev4SdkCall = { service: 'EC2', action: 'createRoute', parameters: { CoreNetworkArn: `arn:aws:networkmanager::${this.node.tryGetContext('centralAccount')}:core-network/${coreNetworkId}`, RouteTableId: subnet.routeTable.routeTableId, DestinationCidrBlock: destinationCidr, }, region: cdk.Aws.REGION, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of(subnet.routeTable.routeTableId + '-cloudwan-route'), }; const deleteRoutev4SdkCall = { service: 'EC2', action: 'deleteRoute', parameters: { RouteTableId: subnet.routeTable.routeTableId, DestinationCidrBlock: destinationCidr, }, region: cdk.Aws.REGION, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of(subnet.routeTable.routeTableId + '-cloudwan-route'), }; new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, `${subnet.node.id}${destinationCidr}-v4route`, { onCreate: addRoutev4SdkCall, onUpdate: addRoutev4SdkCall, onDelete: deleteRoutev4SdkCall, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); } } ; // addRouteForPrivateSubnetstoCloudWan /** * Add routes to point at Network Firewalls, for specific subnetGroups. * this will place routes on a per AZ basis * * @param destinationCidr * @param subnetgroup * @param fwArn */ addRoutetoFirewall(destinationCidr, subnetgroup, fwArn) { /** * the reponse from the API call, exceeds 4096 bytes, so need to limit it with Output Paths */ const outputPaths = []; const azlist = cdk.Stack.of(this).availabilityZones; azlist.forEach((az) => { outputPaths.push(`FirewallStatus.SyncStates.${az}.Attachment.EndpointId`); }); const getfwendpoint = { service: 'NetworkFirewall', action: 'describeFirewall', parameters: { FirewallArn: fwArn, }, region: cdk.Aws.REGION, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of('DescribeFirewall'), outputPaths: outputPaths, }; const fwDescription = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, `DescribeFirewall${subnetgroup}`, { onCreate: getfwendpoint, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); this.selectSubnets({ subnetGroupName: subnetgroup }).subnets.forEach(subnet => { new aws_cdk_lib_1.aws_ec2.CfnRoute(this, 'FirewallRoute-' + subnet.node.path.split('/').pop(), { destinationCidrBlock: destinationCidr, routeTableId: subnet.routeTable.routeTableId, vpcEndpointId: fwDescription.getResponseField(`FirewallStatus.SyncStates.${subnet.availabilityZone}.Attachment.EndpointId`), }); }); } ; // end of addRoutetoFirewall /** * Add routes to routing tables associated with publicSubnets to Cloudwan * @param destinationCidr * @param coreNetworkId */ addRouteForPublicSubnetstoCloudWan(destinationCidr, coreNetworkId) { const subnetSelection = this.selectSubnets({ subnetType: aws_cdk_lib_1.aws_ec2.SubnetType.PUBLIC }); for (const subnet of subnetSelection.subnets) { // there is no cloudformation method for a cloudwan yet! const addRoutev4SdkCall = { service: 'EC2', action: 'createRoute', parameters: { CoreNetworkArn: `arn:aws:networkmanager::${this.node.tryGetContext('centralAccount')}:core-network/${coreNetworkId}`, RouteTableId: subnet.routeTable.routeTableId, DestinationCidrBlock: destinationCidr, }, region: cdk.Aws.REGION, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of(subnet.routeTable.routeTableId + '-cloudwan-route'), }; const deleteRoutev4SdkCall = { service: 'EC2', action: 'deleteRoute', parameters: { RouteTableId: subnet.routeTable.routeTableId, DestinationCidrBlock: destinationCidr, }, region: cdk.Aws.REGION, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of(subnet.routeTable.routeTableId + '-cloudwan-route'), }; new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, `${subnet.node.id}${destinationCidr}-v4route`, { onCreate: addRoutev4SdkCall, onUpdate: addRoutev4SdkCall, onDelete: deleteRoutev4SdkCall, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); } } ; // end of addRouteForPublicSubnetstoCloudWan /** * Create a connect Attachment to Cloudwan for Appliances * @param coreNetworkId * @param transportAttachmentId * @returns */ createConnectAttachment(coreNetworkId, transportAttachmentId) { const connectAttachment = new aws_cdk_lib_1.aws_networkmanager.CfnConnectAttachment(this, 'connectattachment', { coreNetworkId: coreNetworkId, edgeLocation: cdk.Aws.REGION, options: { protocol: 'GRE', }, transportAttachmentId: transportAttachmentId, }); return connectAttachment.attrAttachmentId; } ; // end of createConnectAttachment /** * Associate any rules shared to this vpc * @param owner * @param updatetopic */ associateSharedRoute53ResolverRules(owner, updatetopic) { const associateRouteResolvers = new aws_cdk_lib_1.aws_lambda.Function(this, 'associateLambda', { runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'getRouteResolvers.on_event', code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/evpc')), }); associateRouteResolvers.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: [ 'route53resolver:*', 'route53resolver:ListResolverRules', 'route53resolver:AssociateResolverRule', 'route53resolver:DisassociateResolverRule', '*', ], })); const rRProvider = new aws_cdk_lib_1.custom_resources.Provider(this, 'rRProvider', { onEventHandler: associateRouteResolvers, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS, }); new cdk.CustomResource(this, 'AssociateRouteResolverRules', { serviceToken: rRProvider.serviceToken, properties: { vpcId: this.vpcId, owner: owner, }, }); const deadLetterQueue = new aws_cdk_lib_1.aws_sqs.Queue(this, 'deadLetterQueue'); if (updatetopic !== undefined) { associateRouteResolvers.addEventSource(new aws_cdk_lib_1.aws_lambda_event_sources.SnsEventSource(updatetopic, { filterPolicy: {}, deadLetterQueue: deadLetterQueue, })); } } // end of associateSharedRouteResolverRules /** * Associate the internal R53 Zone with the Central VPC, for Org wide resolution */ associateVPCZonewithCentralVPC() { const fn = aws_cdk_lib_1.aws_lambda.Function.fromFunctionAttributes(this, 'Function', { functionArn: `arn:aws:lambda:${cdk.Stack.of(this).region}:${this.node.tryGetContext('centralAccount')}:function:${this.node.tryGetContext('CreateAuthforZoneLambdaName')}`, sameEnvironment: false, skipPermissions: true, }); const localVPCZoneProvider = new aws_cdk_lib_1.custom_resources.Provider(this, 'localvpcssnProvider', { onEventHandler: fn, }); // get an authorization to associate the zone with the tiritahi vpc const zoneAssn = new cdk.CustomResource(this, 'AuthZone', { serviceToken: localVPCZoneProvider.serviceToken, properties: { PrivateZoneId: this.privateR53ZoneId, }, }); // Associate this zone with the central VPC const associateVpcWithHostedZone = { service: 'Route53', action: 'associateVPCWithHostedZone', parameters: { HostedZoneId: this.privateR53ZoneId, VPC: { VPCId: zoneAssn.getAtt('VpcId'), VPCregion: zoneAssn.getAtt('VpcRegion'), }, }, region: cdk.Aws.REGION, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of('InboundRouteResolverIP'), }; const disassociateVpcWithHostedZone = { service: 'Route53', action: 'disassociateVPCFromHostedZone', parameters: { HostedZoneId: this.privateR53ZoneId, VPC: { VPCId: zoneAssn.getAtt('VpcId'), VPCregion: zoneAssn.getAtt('VpcRegion'), }, }, region: cdk.Aws.REGION, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of('InboundRouteResolverIP'), }; new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'AssociateVpcwithHostedZone', { onCreate: associateVpcWithHostedZone, onDelete: disassociateVpcWithHostedZone, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); } // end of associateVPCZonewithCentralVPC() /** * Add routes in private Subnets to a instance. Use this for routing to a network appliance. * @param destinationCidr * @param instanceId */ addRouteForPrivateSubnetstoinstance(destinationCidr, instanceId) { const subnetSelection = this.selectSubnets({ subnetType: aws_cdk_lib_1.aws_ec2.SubnetType.PRIVATE_ISOLATED }); for (const subnet of subnetSelection.subnets) { new aws_cdk_lib_1.aws_ec2.CfnRoute(this, 'instanceRoute-' + subnet.node.path.split('/').pop(), { destinationCidrBlock: destinationCidr, routeTableId: subnet.routeTable.routeTableId, instanceId: instanceId, }); } } ; // end of Private Routes. /** * Add routes for Private Subnets to a Transit Gateway * @param destinationCidr * @param TransitGatewayId */ addRouteForPrivateSubnetstoTransitGateway(destinationCidr, TransitGatewayId) { var subnetSelection = []; try { const subnetSelectionPrivateIsolated = this.selectSubnets({ subnetType: aws_cdk_lib_1.aws_ec2.SubnetType.PRIVATE_ISOLATED }); subnetSelection = [...subnetSelection, ...subnetSelectionPrivateIsolated.subnets]; } catch (error) { // statements to handle any exceptions //console.error(error); // pass exception object to error handler console.log('there are not Private Subnets'); } try { const subnetSelectionPrivateIsolated = this.selectSubnets({ subnetType: aws_cdk_lib_1.aws_ec2.SubnetType.PRIVATE_WITH_EGRESS }); subnetSelection = [...subnetSelection, ...subnetSelectionPrivateIsolated.subnets]; } catch (error) { // statements to handle any exceptions //console.error(error); // pass exception object to error handler console.log('there are not Private Subnets with Egress'); } for (const subnet of subnetSelection) { new aws_cdk_lib_1.aws_ec2.CfnRoute(this, `Route${subnet.subnetId}${destinationCidr}`, { routeTableId: subnet.routeTable.routeTableId, destinationCidrBlock: destinationCidr, transitGatewayId: TransitGatewayId, }); } } // end of privatesubnets to TransitGateway /** * Attach a VPC to a Transit Gateway in Appliance mode. Primarly used when the VPC is being used as a centralised egress with firewalls * A workaround to the problem of their not being support for Appliance mode connections to cloudwan * @param transitGateway * @param cidrs * @returns */ attachVpcToTGApplianceMode(transitGateway, cidrs) { // get the TG's default routing table, as the attribute is not workign const getDefaultRoutingTableId = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'defaultroutingtableId', { onCreate: { service: 'EC2', action: 'describeTransitGateways', parameters: { TransitGatewayIds: [transitGateway.attrId], }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of('TransitGateways.0.Options.AssociationDefaultRouteTableId'), }, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); const routingtableId = getDefaultRoutingTableId.getResponseField('TransitGateways.0.Options.AssociationDefaultRouteTableId'); const transitGatewaypeering = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'AttachtheVPCtoTG', { onCreate: { service: 'EC2', action: 'createTransitGatewayVpcAttachment', parameters: { SubnetIds: this.selectSubnets({ subnetGroupName: 'linknet' }).subnetIds, TransitGatewayId: transitGateway.attrId, VpcId: this.vpcId, Options: { ApplianceModeSupport: 'enable', }, }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('TransitGatewayVpcAttachment.TransitGatewayAttachmentId'), region: 'ap-southeast-2', }, onDelete: { service: 'EC2', action: 'deleteTransitGatewayVpcAttachment', parameters: { TransitGatewayAttachmentId: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(), }, region: 'ap-southeast-2', }, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_DAY, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromStatements([ new aws_cdk_lib_1.aws_iam.PolicyStatement({ actions: ['*'], resources: ['*'], }), ]), }); if (cidrs !== undefined) { // need to implment a check here to see if the attachment is in valid state to use. const waitForTransitGatewayPeering = new aws_cdk_lib_1.aws_lambda.Function(this, 'oneventttobeready', { runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'transitgatewayRoutes.on_event', code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/evpc')), }); // need to be able to delete the routes. waitForTransitGatewayPeering.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ actions: ['ec2:deleteTransitGatewayRoute'], resources: ['*'], effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, })); const isCompleteTransitGatewayPeering = new aws_cdk_lib_1.aws_lambda.Function(this, 'iscompleterattachmenttobeready', { runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'transitgatewayRoutes.is_complete', code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/evpc')), }); // need to be able to check if the attachment is done, and if it is add routes. isCompleteTransitGatewayPeering.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ actions: [ 'ec2:createTransitGatewayRoute', 'ec2:describeTransitGatewayVpcAttachments', ], resources: ['*'], effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, })); const tgReady = new aws_cdk_lib_1.custom_resources.Provider(this, 'tgReady', { onEventHandler: waitForTransitGatewayPeering, isCompleteHandler: isCompleteTransitGatewayPeering, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK, queryInterval: cdk.Duration.seconds(10), }); // create a resource for each route.. rather than pass the list of routes cidrs.forEach((cidr) => { new cdk.CustomResource(this, `WaitforTGCR${cidr}`, { serviceToken: tgReady.serviceToken, properties: { transitGatewayAttachmentId: transitGatewaypeering.getResponseField('TransitGatewayVpcAttachment.TransitGatewayAttachmentId'), cidr: cidr, transitGatewayRouteTableId: routingtableId, }, }); }); } // end of adding transitgateway routes. return transitGatewaypeering.getResponseField('TransitGatewayVpcAttachment.TransitGatewayAttachmentId'); } // end attachVpcToTGApplianceMode } exports.Evpc = Evpc; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZwYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ldnBjL2V2cGMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkJBQTZCO0FBQzdCLG1DQUFtQztBQUNuQyw2Q0FhcUI7QUFNckI7Ozs7R0FJRztBQUVILFNBQVMsU0FBUyxDQUFDLEVBQVU7SUFDM0IsTUFBTSxPQUFPLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM5QixJQUFJLEtBQUssR0FBVyxDQUFDLENBQUM7SUFDdEIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsRUFBRTtRQUMvQixJQUFJLE1BQU0sR0FBVyxLQUEwQixDQUFDO1FBQ2hELGtDQUFrQztRQUNsQyxLQUFLLElBQUksTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxHQUFDLEtBQUssR0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzlDLENBQUMsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBcUREOzs7Ozs7R0FNRztBQUNILE1BQWEsSUFBSyxTQUFRLHFCQUFHLENBQUMsR0FBRztJQThCL0IsWUFBWSxLQUEyQixFQUFFLEVBQVUsRUFBRSxRQUFtQixFQUFFO1FBR3hFLDZFQUE2RTtRQUM3RSxtRUFBbUU7UUFLdEUsTUFBTSxRQUFRLEdBQUcsS0FBMkIsQ0FBQztRQUc3QywwQ0FBMEM7UUFDMUMsSUFBSSxLQUFLLENBQUMsSUFBSSxJQUFJLEtBQUssQ0FBQyxVQUFVLEVBQUU7WUFDbEMsTUFBTSxJQUFJLEtBQUssQ0FBQywwQ0FBMEMsQ0FBQyxDQUFDO1NBRTdEO1FBQ0QsK0hBQStIO1FBQy9ILElBQUksS0FBSyxDQUFDLFVBQVUsRUFBRTtZQUVwQixRQUFRLENBQUMsSUFBSSxHQUFHLFdBQVcsS0FBSyxDQUFDLGFBQWEsRUFBRSxDQUFDO1NBQ2xEO1FB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