UNPKG

raindancers-network

Version:
592 lines 88.7 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.CloudWanTGW = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const path = require("path"); const cdk = require("aws-cdk-lib"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const constructs = require("constructs"); const CloudWanTGWProps = require("./cloudwanTGWProps"); const ipam_1 = require("../ipam/ipam"); /** * Create a TransitGateway That is attached to Cloudwan */ class CloudWanTGW extends constructs.Construct { /** * * @param scope scope in which the resource is c * @param id * @param props TGWOnCloudWanProps */ constructor(scope, id, props) { super(scope, id); this.tgcidr = props.tgCidr; //this.tgDXattachmentId = 'abcef'; // look up the corewan const lookupIdLambda = new aws_cdk_lib_1.aws_lambda.Function(this, 'lookupIdLambda-tgOnCloudwan', { runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'getcloudwanID.on_event', code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan')), }); lookupIdLambda.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: ['networkmanager:ListCoreNetworks'], })); // get the parameters from the core. const coreNetwork = new cdk.CustomResource(this, 'idfinderCR', { serviceToken: new aws_cdk_lib_1.custom_resources.Provider(this, 'NetworkManagerProvider', { onEventHandler: lookupIdLambda, }).serviceToken, properties: { CoreNetworkName: this.node.tryGetContext('coreNetworkName'), ForceUpdate: new Date().toISOString(), }, }); // create a transit gateway that will fit in as a shim between cloud WAN and the egress VPC. this is to // over come the issues with Cloudwan not supporting Appliance Mode, and asymetric routes. const egressTG = new aws_cdk_lib_1.aws_ec2.CfnTransitGateway(this, 'EgressTg', /* all optional props */ { amazonSideAsn: props.amazonSideAsn, transitGatewayCidrBlocks: props.tgCidr, autoAcceptSharedAttachments: 'enable', defaultRouteTableAssociation: 'enable', defaultRouteTablePropagation: 'enable', description: props.description, dnsSupport: 'enable', multicastSupport: 'enable', }); // a transit gateway must have a policy table to be attached to Cloudwan.. new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'TWpolicyTable', { onCreate: { service: 'EC2', action: 'createTransitGatewayPolicyTable', parameters: { TransitGatewayId: egressTG.attrId, }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('TransitGatewayPolicyTable.TransitGatewayPolicyTableId'), }, onDelete: { service: 'EC2', action: 'deleteTransitGatewayPolicyTable', parameters: { TransitGatewayPolicyTableId: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(), }, }, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); //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: [egressTG.attrId], }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('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'); this.transitGateway = egressTG; // const egressTGRoutingTable = new ec2.CfnTransitGatewayRouteTable(this, 'TGRouteTable', { // transitGatewayId: egressTG.attrId // }) //register the transitgateway with the global network. new aws_cdk_lib_1.aws_networkmanager.CfnTransitGatewayRegistration(this, 'TransitGatewayRegistration', { globalNetworkId: coreNetwork.getAttString('GlobalNetworkId'), transitGatewayArn: `arn:aws:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:transit-gateway/${egressTG.attrId}`, }); // peer the tg to cloudwan. // Checked that this builds and deletes const transitGatewaypeering = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'AttachTGtoCloudWan', { onCreate: { service: 'NetworkManager', action: 'createTransitGatewayPeering', parameters: { CoreNetworkId: coreNetwork.getAttString('CoreNetworkId'), TransitGatewayArn: `arn:aws:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:transit-gateway/${egressTG.attrId}`, }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('TransitGatewayPeering.Peering.PeeringId'), region: 'us-west-2', }, onDelete: { service: 'NetworkManager', action: 'deletePeering', parameters: { PeeringId: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(), }, region: 'us-west-2', }, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromStatements([ new aws_cdk_lib_1.aws_iam.PolicyStatement({ actions: ['*'], resources: ['*'], }), ]), }); // Attach TransitGatewayRouteTabletoCloudwan // lambdas for custom resource const onEvent = new aws_cdk_lib_1.aws_lambda.Function(this, 'oneventlambda', { code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan'), { bundling: { image: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9.bundlingImage, command: [ 'bash', '-c', 'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output', ], }, }), runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'peerroutingtable.on_event', }); onEvent.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: [ 'networkmanager:deleteAttachment', ], })); const isPeeringDone = new aws_cdk_lib_1.aws_lambda.Function(this, 'ispeeringDone', { code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan'), { bundling: { image: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9.bundlingImage, command: [ 'bash', '-c', 'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output', ], }, }), runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'peerroutingtable.is_complete', }); isPeeringDone.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: [ 'networkmanager:CreateTransitGatewayRouteTableAttachment', 'networkmanager:deleteAttachment', '*', ], })); const AttachTGRouteTableToCloudwanProvider = new aws_cdk_lib_1.custom_resources.Provider(this, 'isReadyProvider', { onEventHandler: onEvent, isCompleteHandler: isPeeringDone, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.SEVEN_YEARS, queryInterval: cdk.Duration.seconds(30), }); // we will place the attachmentId in a SSM parameter as its difficult to lookup with the SDK const attachmentId = new aws_cdk_lib_1.aws_ssm.StringParameter(this, 'attachmentID', { stringValue: 'Null', }); attachmentId.grantRead(isPeeringDone); attachmentId.grantWrite(isPeeringDone); attachmentId.grantRead(onEvent); attachmentId.grantWrite(onEvent); const cloudwanTgAttachment = new cdk.CustomResource(this, 'AttachTGRouteTable', { serviceToken: AttachTGRouteTableToCloudwanProvider.serviceToken, properties: { transitGatewayRouteTableArn: `arn:aws:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:transit-gateway-route-table/${routingtableId}`, PeeringId: transitGatewaypeering.getResponseField('TransitGatewayPeering.Peering.PeeringId'), AttachmentSegment: props.attachmentSegment, CoreNetworkId: coreNetwork.getAttString('CoreNetworkId'), EdgeLocation: cdk.Aws.REGION, attachmentIdSSMName: attachmentId.parameterName, }, }); this.cloudwanTgAttachmentId = cloudwanTgAttachment.getAttString('AttachmentId'); } /** * @param dxgatewayId Id of a DX gateway that */ createDirectConnectGatewayAssociation(dxgatewayId) { // associate the TransitGateway with the DXGateway // https://docs.aws.amazon.com/cli/latest/reference/directconnect/create-direct-connect-gateway-association.html var cidrList = []; // create-direct-connect-gateway-association requires the prefix lists to be presented as a array of objects // in the format {'cidr':cidr} // // this is a bit unfriendly, so we we create them here. if (this.tgcidr) { this.tgcidr?.forEach((cidr) => { cidrList.push({ cidr: cidr }); }); } const dxGWAssn = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'CreateDXGatewayAssn', { onCreate: { service: 'DirectConnect', action: 'createDirectConnectGatewayAssociation', parameters: { directConnectGatewayId: dxgatewayId, addAllowedPrefixesToDirectConnectGateway: cidrList, gatewayId: this.transitGateway.attrId, }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('directConnectGatewayAssociation.associationId'), }, onDelete: { service: 'DirectConnect', action: 'DeleteDirectConnectGatewayAssociation', parameters: { directConnectGatewayID: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(), }, }, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); const getAttachmentId = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'Getattachment', { onCreate: { service: 'EC2', action: 'describeTransitGatewayAttachments', parameters: { Filters: [ { Name: 'resource-type', Values: [ 'direct-connect-gateway', ], }, { Name: 'transit-gateway-id', Values: [ this.transitGateway.attrId, ], }, ], }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.of('getAttachmentId'), }, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); getAttachmentId.node.addDependency(dxGWAssn); this.tgDXattachmentId = getAttachmentId.getResponseField('TransitGatewayAttachments.0.TransitGatewayAttachmentId'); //this.tgDXattachmentId = dxGWAssn.getResponseField('directConnectGatewayAssociation.associationId'); return getAttachmentId.getResponseField('TransitGatewayAttachments.0.TransitGatewayAttachmentId'); } /** * provision a DX Gateway and attach it to the transit gateway * @param dxgatewayname The name of the dxgateway * @param dxgatewayASN An ASN for the Dxgateway * @returns Direct Connect gatewayId */ addDXGateway(dxgatewayname, dxgatewayASN) { const dxgateway = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'CreateDXGateway', { onCreate: { service: 'DirectConnect', action: 'createDirectConnectGateway', parameters: { directConnectGatewayName: dxgatewayname, amazonSideAsn: dxgatewayASN, }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('directConnectGateway.directConnectGatewayId'), }, onDelete: { service: 'DirectConnect', action: 'deleteDirectConnectGateway', parameters: { directConnectGatewayID: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(), }, }, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); const gatewayId = dxgateway.getResponseField('directConnectGateway.directConnectGatewayId'); // associate the TransitGateway with the DXGateway // https://docs.aws.amazon.com/cli/latest/reference/directconnect/create-direct-connect-gateway-association.html var cidrList = []; // create-direct-connect-gateway-association requires the prefix lists to be presented as a array of objects // in the format {'cidr':cidr} // // this is a bit unfriendly, so we we create them here. if (this.tgcidr) { this.tgcidr?.forEach((cidr) => { cidrList.push({ cidr: cidr }); }); } new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, 'CreateDXGateway', { onCreate: { service: 'DirectConnect', action: 'createDirectConnectGatewayAssociation', parameters: { directConnectGatewayId: dxgateway.getResponseField('directConnectGateway.directConnectGatewayId'), addAllowedPrefixesToDirectConnectGateway: this.tgcidr, gatewayId: this.transitGateway.attrId, }, physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('directConnectGatewayAssociation.associationId'), }, onDelete: { service: 'associationId', action: 'DeleteDirectConnectGatewayAssociation', parameters: { directConnectGatewayID: new aws_cdk_lib_1.custom_resources.PhysicalResourceIdReference(), }, }, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK, policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), }); return gatewayId; } /** * Creates a Site To Site IPSec VPN between the Transit Gateway and Customer Gateway, * using a defined set of VPn Properties. * @param name A name to identify the vpn * @param vpnprops the vpn properties * @returns */ adds2sVPN(name, vpnprops) { const vpnPresharedKey = new aws_cdk_lib_1.aws_secretsmanager.Secret(this, `${name}PresharedKey`, { generateSecretString: { excludePunctuation: true, passwordLength: 61, }, secretName: `${name}-PSK`, description: `PresharedKey for ${name} s2S VPN`, }); const createVpnCrLambda = new aws_cdk_lib_1.aws_lambda.SingletonFunction(this, `createVpnCrLambda${name}`, { uuid: 'AABBCCDDEEFF000001', code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan'), { bundling: { image: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9.bundlingImage, command: [ 'bash', '-c', 'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output', ], }, }), runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, handler: 'addS2Svpn.on_event', }); createVpnCrLambda.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: [ 'ec2:CreateVpnConnection', 'ec2:DeleteVpnConnection', 'ec2:DeleteTags', 'ec2:CreateTags', ], })); vpnPresharedKey.grantRead(createVpnCrLambda); const createVPNProvider = new aws_cdk_lib_1.custom_resources.Provider(this, `createVPNProvider${name}`, { onEventHandler: createVpnCrLambda, logRetention: aws_cdk_lib_1.aws_logs.RetentionDays.ONE_WEEK, }); // create a flow log for the vpn var cloudWatchLogOptions; if (vpnprops.vpnspec.enableLogging !== undefined) { if (vpnprops.vpnspec.enableLogging) { var vpnlog = new aws_cdk_lib_1.aws_logs.LogGroup(this, `${name}vpnLogGroup`, { retention: aws_cdk_lib_1.aws_logs.RetentionDays.THREE_MONTHS, }); vpnlog.grantWrite(new aws_cdk_lib_1.aws_iam.ServicePrincipal('delivery.logs.amazonaws.com')); // const cloudWatchLogOptions = { // LogEnabled: true, // LogGroupArn: vpnlog.logGroupArn, // LogOutputFormat: 'json' // } } } // validate that if the Outside addrss's are Private that Acceleration is not enabled. if (vpnprops.vpnspec.outsideIpAddressType === CloudWanTGWProps.OutsideIpAddressType.PRIVATE && vpnprops.vpnspec.enableAcceleration === true) { throw new Error('if using Private addresss for S2S Vpn, Vpn Acceleration can not be enabled.'); } // validate that if the Outside address's are Private that a DX gateway Assn is provided. if (vpnprops.vpnspec.outsideIpAddressType === CloudWanTGWProps.OutsideIpAddressType.PRIVATE && !this.tgDXattachmentId) { throw new Error('If the Outside address for a S2S VPN is Private, a dxAssociationId must be provided'); } // validate Timeout if (vpnprops.vpnspec.phase1LifetimeSeconds !== undefined) { if (!(vpnprops.vpnspec.phase1LifetimeSeconds >= 900 && vpnprops.vpnspec.phase1LifetimeSeconds <= 28000)) { throw new Error('Phase1 Life time must be between 900 and 28000'); } } // validate p2 Timeout if (vpnprops.vpnspec.phase2LifeTimeSeconds !== undefined) { if (!(vpnprops.vpnspec.phase2LifeTimeSeconds >= 900 && vpnprops.vpnspec.phase2LifeTimeSeconds <= 3600)) { throw new Error('Phase2 Life time must be between 900 and 3600'); } if (vpnprops.vpnspec.phase1LifetimeSeconds !== undefined) { if (!(vpnprops.vpnspec.phase1LifetimeSeconds > vpnprops.vpnspec.phase2LifeTimeSeconds)) { throw new Error('Phase1 Life time must be greater than phase2 lifetime '); } } } // validate rekeyFuzzPercentage if (vpnprops.vpnspec.rekeyFuzzPercentage !== undefined) { if (vpnprops.vpnspec.rekeyFuzzPercentage > 100 && vpnprops.vpnspec.rekeyFuzzPercentage < 0) { throw new Error('rekey Fuzz Percentage must be between 0 and 100'); } } // validate rekeyMarginTimeSeconds // Constraints: A value between 60 and half of Phase2LifetimeSeconds var p2lifetime; if (vpnprops.vpnspec.phase2LifeTimeSeconds === undefined) { p2lifetime = 540; } else { p2lifetime = vpnprops.vpnspec.phase2LifeTimeSeconds / 2; } if (vpnprops.vpnspec.rekeyMarginTimeSeconds !== undefined) { if (!(vpnprops.vpnspec.rekeyMarginTimeSeconds >= 60 && vpnprops.vpnspec.rekeyMarginTimeSeconds <= p2lifetime)) { throw new Error('rekeyMarginTimeSeconds must be a value between 60 and half of Phase2LifetimeSeconds '); } } //valdiate replayWindowsSize: // Constraints: A value between 64 and 2048. if (vpnprops.vpnspec.replayWindowSize !== undefined) { if (vpnprops.vpnspec.replayWindowSize <= 64 && vpnprops.vpnspec.replayWindowSize >= 2048) { throw new Error('replayWindowSize must be a value between 64 and 2048'); } } // validate or create Ipam address's if (vpnprops.tunnelInsideCidr === undefined && vpnprops.tunnelIpamPool === undefined) { throw new Error('At least one of tunnelInsideCidr or tunnelIpamPool must be defined'); } else if (vpnprops.tunnelInsideCidr !== undefined && vpnprops.tunnelIpamPool !== undefined) { throw new Error('Only one of tunnelInsideCidr or tunnelIpamPool can be defined'); } var assignedCidrs = []; // validate if supplied tunnels are valid if (vpnprops.tunnelInsideCidr !== undefined) { vpnprops.tunnelInsideCidr.forEach((cidr) => { // check to see that cidr starts with 169.254 and ends with /30 if (!(cidr.endsWith('/30') && cidr.startsWith('169.254'))) { throw new Error('Invalid tunnel Cidr'); } //check to see if cidr is in reserved range const reservedTunnelIP = [ '169.254.0.0/30', '169.254.1.0/30', '169.254.2.0/30', '169.254.3.0/30', '169.254.4.0/30', '169.254.5.0/30', '169.254.169.252/30', ]; if (cidr in reservedTunnelIP) { throw new Error(`the Cidr is a reserved range and can not be used, refer: \n https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-vpnconnection-vpntunneloptionsspecification.html" `); } assignedCidrs.push(cidr); }); } else if (vpnprops.tunnelIpamPool !== undefined) { const tunnelAllocation = new ipam_1.GetTunnelAddressPair(this, `${name}tunneladdress`, { ipamPoolId: vpnprops.tunnelIpamPool.attrIpamPoolId, name: name, }); assignedCidrs = tunnelAllocation.assignedCidrPair; } const tunnels = []; assignedCidrs.forEach((cidr) => { tunnels.push({ // key and tunnel addressing PreSharedKeyArn: vpnPresharedKey.secretFullArn, TunnelInsideCidr: cidr, // Dead peer detection and action DPDTimeoutAction: vpnprops.vpnspec.dpdTimeoutAction, DPDTimeoutSeconds: vpnprops.vpnspec.dpdTimeoutSeconds, // Allowable IKE versions IKEVersions: makeObject(vpnprops.vpnspec.ikeVersions), LogOptions: { CloudWatchLogOptions: cloudWatchLogOptions, }, // Phase one Configuration Phase1DHGroupNumbers: makeObject(vpnprops.vpnspec.phase1DHGroupNumbers), Phase1EncryptionAlgorithms: makeObject(vpnprops.vpnspec.phase1EncryptionAlgorithms), Phase1IntegrityAlgorithms: makeObject(vpnprops.vpnspec.phase1IntegrityAlgorithms), Phase1LifetimeSeconds: vpnprops.vpnspec.phase1LifetimeSeconds, // Phase Two configuration Phase2DHGroupNumbers: makeObject(vpnprops.vpnspec.phase2DHGroupNumbers), Phase2EncryptionAlgorithms: makeObject(vpnprops.vpnspec.phase2EncryptionAlgorithms), Phase2IntegrityAlgorithms: makeObject(vpnprops.vpnspec.phase2IntegrityAlgorithms), Phase2LifetimeSeconds: vpnprops.vpnspec.phase2LifeTimeSeconds, // rekeying and windowsizes RekeyFuzzPercentage: vpnprops.vpnspec.rekeyFuzzPercentage, RekeyMarginTimeSeconds: vpnprops.vpnspec.rekeyMarginTimeSeconds, ReplayWindowSize: vpnprops.vpnspec.replayWindowSize, StartupAction: vpnprops.vpnspec.startupAction, }); }); // the converison to a base64 string is so that the types of the string can be passed to the lambda without them being converted to a string. // json supports ints and boleans, but natively the propertys in a lambda dont. // where the the properties are not plain strings, they need to be packed in base64, and then decoded lambda side. const props = { CustomerGatewayId: vpnprops.customerGateway.attrCustomerGatewayId, Type: 'ipsec.1', TransitGatewayId: this.transitGateway.attrId, Name: name, Options: { EnableAcceleration: vpnprops.vpnspec.enableAcceleration, LocalIpv4NetworkCidr: vpnprops.vpnspec.localIpv4NetworkCidr, RemoteIpv4NetworkCidr: vpnprops.vpnspec.remoteIpv4NetworkCidr, OutsideIpAddressType: vpnprops.vpnspec.outsideIpAddressType, StaticRoutesOnly: vpnprops.vpnspec.staticRoutesOnly, TunnelInsideIpVersion: vpnprops.vpnspec.tunnelInsideIpVersion, TunnelOptions: tunnels, TransportTransitGatewayAttachmentId: this.tgDXattachmentId, }, }; const vpn = new cdk.CustomResource(this, `CreateP2PVPN${name}`, { serviceToken: createVPNProvider.serviceToken, properties: { props64: cdk.Fn.base64(cdk.Stack.of(this).toJsonString(props)), }, }); if (vpnprops.sampleconfig !== undefined) { const sampleConfigLambda = new aws_cdk_lib_1.aws_lambda.SingletonFunction(this, `${name}sampleconfig`, { code: aws_cdk_lib_1.aws_lambda.Code.fromAsset(path.join(__dirname, '../../lambda/cloudwan')), uuid: 'FFFFAAFFEEDDDDE000', handler: 'samplevpn.on_event', runtime: aws_cdk_lib_1.aws_lambda.Runtime.PYTHON_3_9, }); sampleConfigLambda.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({ effect: aws_cdk_lib_1.aws_iam.Effect.ALLOW, resources: ['*'], actions: ['ec2:getVpnConnectionDeviceSampleConfiguration'], })); vpnprops.sampleconfig.bucket.grantReadWrite(sampleConfigLambda); // get the parameters from the core. new cdk.CustomResource(this, `createconfigplaceinbucket${name}`, { serviceToken: new aws_cdk_lib_1.custom_resources.Provider(this, `NetworkManagerProvider${name}`, { onEventHandler: sampleConfigLambda, }).serviceToken, properties: { Name: `${name}`, BucketName: vpnprops.sampleconfig.bucket.bucketName, VpnConnectionId: vpn.getAtt('VpnConnectionId'), VpnConnectionDeviceTypeId: vpnprops.sampleconfig.deviceType, InternetKeyExchangeVersion: vpnprops.sampleconfig.ikeVersion, }, }); } } } exports.CloudWanTGW = CloudWanTGW; _a = JSII_RTTI_SYMBOL_1; CloudWanTGW[_a] = { fqn: "raindancers-network.cloudwan.CloudWanTGW", version: "1.29.3" }; function makeObject(x) { let object = []; if (x) { x.forEach((item) => { object.push({ Value: item }); }); return object; } return null; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWR3YW5UR1cuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xvdWR3YW4vY2xvdWR3YW5UR1cudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw2QkFBNkI7QUFDN0IsbUNBQW1DO0FBQ25DLDZDQVVxQjtBQUNyQix5Q0FBeUM7QUFHekMsdURBQXVEO0FBQ3ZELHVDQUFvRDtBQUdwRDs7R0FFRztBQUNILE1BQWEsV0FBWSxTQUFRLFVBQVUsQ0FBQyxTQUFTO0lBbUJuRDs7Ozs7U0FLRTtJQUNGLFlBQVksS0FBMkIsRUFBRSxFQUFVLEVBQUUsS0FBMEM7UUFDN0YsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVqQixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFDM0Isa0NBQWtDO1FBRWxDLHNCQUFzQjtRQUN0QixNQUFNLGNBQWMsR0FBRyxJQUFJLHdCQUFVLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSw2QkFBNkIsRUFBRTtZQUNsRixPQUFPLEVBQUUsd0JBQVUsQ0FBQyxPQUFPLENBQUMsVUFBVTtZQUN0QyxPQUFPLEVBQUUsd0JBQXdCO1lBQ2pDLElBQUksRUFBRSx3QkFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsdUJBQXVCLENBQUMsQ0FBQztTQUMvRSxDQUFDLENBQUM7UUFFSCxjQUFjLENBQUMsZUFBZSxDQUM1QixJQUFJLHFCQUFHLENBQUMsZUFBZSxDQUFDO1lBQ3RCLE1BQU0sRUFBRSxxQkFBRyxDQUFDLE1BQU0sQ0FBQyxLQUFLO1lBQ3hCLFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQztZQUNoQixPQUFPLEVBQUUsQ0FBQyxpQ0FBaUMsQ0FBQztTQUM3QyxDQUFDLENBQ0gsQ0FBQztRQUVGLG9DQUFvQztRQUNwQyxNQUFNLFdBQVcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxjQUFjLENBQUMs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