Replies: 3 comments
-
We have a similar setup where we share a central VPC across several accounts within the organization. Just like in your example, the VPC is shared via RAM. We then publish an internal CDK construct library with a function such as this: export function GetSharedVpc(): IVpc {
return Vpc.fromVpcAttributes(new Stack(), 'VPC', {
availabilityZones: [
'ap-southeast-2a',
'ap-southeast-2b',
'ap-southeast-2c',
],
isolatedSubnetIds: [
'subnet-xxx',
'subnet-xxy',
'subnet-xxz',
],
isolatedSubnetRouteTableIds: [
'rtb-xxx',
'rtb-xxy',
'rtb-xxz',
],
privateSubnetIds: [
'subnet-xyx',
'subnet-xyy',
'subnet-xyz',
],
privateSubnetRouteTableIds: [
'rtb-xyx',
'rtb-xyy',
'rtb-xyz',
],
publicSubnetIds: [
'subnet-xzx',
'subnet-xzy',
'subnet-xzz',
],
publicSubnetRouteTableIds: [
'rtb-xzx',
'rtb-xzx',
'rtb-xzx',
],
vpcCidrBlock: '10.123.0.0/16',
vpcId: 'vpc-yyy',
})
} the above code can be auto-generated from stack outputs if necessary. User applications import the CDK library and use it in their stacks, eg: new ApplicationStack(app, 'AppStack', {
env: {
account: '12345678',
region: 'ap-southeast-2',
},
vpc: sharedLibrary.GetSharedVpc(),
}); Hope that helps |
Beta Was this translation helpful? Give feedback.
-
I see, but in the end you are wiring things manually, through their ids. const vpc = ec2.Vpc.fromLookup(this, "vpc", {
isDefault: false,
}); This resolves to the shared VPC, assuming the accounts have a single VPC, one shared by the "network account", and "imported" in the other account. |
Beta Was this translation helpful? Give feedback.
-
import { App, Stack, StackProps, Tags } from "aws-cdk-lib";
import {
AclCidr,
AclTraffic,
Action,
NetworkAcl,
SubnetConfiguration,
SubnetType,
TrafficDirection,
Vpc
} from "aws-cdk-lib/aws-ec2";
import { CfnResourceShare } from "aws-cdk-lib/aws-ram";
import { merge } from "cidr-tools";
import { SubnetCategory, SubnetEnv } from "./helper";
export class VpcStack extends Stack {
constructor(
scope: App,
id: string,
props: StackProps,
configs: {
name: string;
ipPrefix: string;
sharedAccounts: any;
}
) {
super(scope, id, props);
const subnetGroupName = (subnetEnv: SubnetEnv, subnetCategory: SubnetCategory) =>
generateSubnetGroupName(configs.name, subnetEnv, subnetCategory);
const vpc = new Vpc(this, `Vpc`, {
vpcName: `${configs.name}Vpc`,
cidr: `${configs.ipPrefix}.0.0/16`,
natGateways: 1,
natGatewaySubnets: {
availabilityZones: ["ap-southeast-2c"],
subnetGroupName: subnetGroupName(SubnetEnv.Shared, SubnetCategory.Public)
},
maxAzs: 99,
subnetConfiguration: [
...generateSubnets(configs.name, SubnetEnv.Dev),
...generateSubnets(configs.name, SubnetEnv.Sit),
...generateSubnets(configs.name, SubnetEnv.Uat),
...generateSubnets(configs.name, SubnetEnv.Stg),
...generateSubnets(configs.name, SubnetEnv.Prd),
...generateSubnets(configs.name, SubnetEnv.Shared)
]
});
[...vpc.publicSubnets, ...vpc.privateSubnets, ...vpc.isolatedSubnets].forEach((subnet) => {
console.log(subnet.node.id);
Tags.of(subnet).add("Name", subnet.node.id);
});
Object.values(SubnetEnv).forEach((subnetEnv) => {
Object.values(SubnetCategory).forEach((subnetCategory) => {
const currentGroupName = subnetGroupName(subnetEnv, subnetCategory);
const networkAcl = new NetworkAcl(this, `${currentGroupName}Acl`, {
vpc: vpc,
networkAclName: `${configs.name}Vpc/${currentGroupName}Acl`,
subnetSelection: {
subnetGroupName: currentGroupName
}
});
Tags.of(networkAcl).add("Name", `${currentGroupName}Acl`);
});
Object.values(SubnetEnv).forEach((subnetEnv) => {
if (subnetEnv !== SubnetEnv.Shared) {
const subnetArns: string[] = [];
Object.values(SubnetCategory).forEach((subnetCategory) => {
const subnets = vpc.selectSubnets({
subnetGroupName: subnetGroupName(subnetEnv, subnetCategory)
});
subnetArns.push(
...subnets.subnetIds.map(
(subnetId) => `arn:aws:ec2:${this.region}:${this.account}:subnet/${subnetId}`
)
);
});
const cfnResourceShare = new CfnResourceShare(
this,
`Share${configs.name}${subnetEnv}Subnets`,
{
name: `Share${configs.name}${subnetEnv}Subnets`,
principals: [configs.sharedAccounts[subnetEnv.toUpperCase()]],
resourceArns: subnetArns,
allowExternalPrincipals: false,
tags: [
{
key: "Creator",
value: "CJ"
}
]
}
);
}
});
}
}
const generateSubnetGroupName = (
vpcName: string,
subnetEnv: SubnetEnv,
subnetCategory: SubnetCategory
) => `${vpcName}${subnetEnv}${subnetCategory}`;
const generateSubnets = (vpcName: string, subnetEnv: SubnetEnv): SubnetConfiguration[] => [
{
name: generateSubnetGroupName(vpcName, subnetEnv, SubnetCategory.Private),
subnetType: SubnetType.PRIVATE_WITH_NAT,
cidrMask: 21
},
{
name: generateSubnetGroupName(vpcName, subnetEnv, SubnetCategory.Public),
subnetType: SubnetType.PUBLIC,
cidrMask: 24
},
{
name: generateSubnetGroupName(vpcName, subnetEnv, SubnetCategory.Isolated),
subnetType: SubnetType.PRIVATE_ISOLATED,
cidrMask: 24
}
];
const vpc = new VpcStack(app, "IvoryTechVpc", ivoryTechShared, {
ipPrefix: "10.0",
name: "hehehe",
sharedAccounts: IvoryTechAccounts
}); Hope this helps |
Beta Was this translation helpful? Give feedback.
-
General Issue
Recommended way to handle cross-account VPCs
The Question
We are using a multi-account strategy for your AWS infrastructure, managed by AWS Control Tower, following the best practices as described in the documentation
Our configuration is similar to what is described in this article
We have a infrastructure account which is the creator of a VPC. This VPC is shared using Resource Access Manager.
Then a workload account use that VPC to launch services on it.
We don't know how to accomplish this using CDK.
Our CDK app has one stack called
NetworkStack
which creates the VPC and the VPC sharing, locked to an environment with an specific account.Then we have an application stack, using a different account, with access to the VPC through RAM.
This does not work. Because you can't share VPCs across different account like this.
Tricks like using SSM don't work either.
What is the recommended way to use a multi-account setup, using VPC shared through RAM, using CDK?
CDK CLI Version
2.2.0
Framework Version
2.2.0
Node.js Version
16.6.1
OS
MacOs
Language
Typescript
Language Version
4.5.4
Other information
No response
Beta Was this translation helpful? Give feedback.
All reactions