in src/deployments/cdk/src/deployments/vpc/step-3.ts [42:252]
export async function step3(props: VpcStep3Props) {
const { config, outputs, accountStacks, limiter, accounts } = props;
const allStaticResources = StaticResourcesOutputFinder.findAll({
outputs,
}).filter(sr => sr.resourceType === RESOURCE_TYPE);
const portOverrides = config['global-options']['endpoint-port-overrides'];
const accountStaticResourcesConfig: { [accountKey: string]: StaticResourcesOutput[] } = {};
const accountRegionExistingResources: {
[accountKey: string]: {
[region: string]: string[];
};
} = {};
const accountRegionMaxSuffix: {
[accountKey: string]: {
[region: string]: number;
};
} = {};
// Initiate previous stacks to handle deletion of previously deployed stack if there are no resources
for (const sr of allStaticResources) {
const srLocalAccount = accounts.find(acc => acc.key === sr.accountKey);
accountStacks.tryGetOrCreateAccountStack(
sr.accountKey,
sr.region,
`${STACK_SUFFIX}-${sr.suffix}`,
srLocalAccount?.inScope,
);
}
for (const { accountKey, vpcConfig } of config.getVpcConfigs()) {
if (!InterfaceEndpointConfig.is(vpcConfig['interface-endpoints'])) {
continue;
}
const endpointsConfig = vpcConfig['interface-endpoints'];
// Retrieving current VPCId
const vpcOutput = VpcOutputFinder.tryFindOneByAccountAndRegionAndName({
outputs,
accountKey,
region: vpcConfig.region,
vpcName: vpcConfig.name,
});
if (!vpcOutput) {
console.error(`Cannot find resolved VPC with name "${vpcConfig.name}"`);
continue;
}
let suffix: number;
let stackSuffix: string;
// Load all account stacks to object
if (!accountStaticResourcesConfig[accountKey]) {
accountStaticResourcesConfig[accountKey] = allStaticResources.filter(sr => sr.accountKey === accountKey);
}
if (!accountRegionMaxSuffix[accountKey]) {
accountRegionMaxSuffix[accountKey] = {};
}
// Load Max suffix for each region of account to object
if (!accountRegionMaxSuffix[accountKey][vpcConfig.region]) {
const localSuffix = accountStaticResourcesConfig[accountKey]
.filter(sr => sr.region === vpcConfig.region)
.flatMap(r => r.suffix);
accountRegionMaxSuffix[accountKey][vpcConfig.region] = localSuffix.length === 0 ? 1 : Math.max(...localSuffix);
}
if (!accountRegionExistingResources[accountKey]) {
const localRegionalResources = accountStaticResourcesConfig[accountKey]
.filter(sr => sr.region === vpcConfig.region)
.flatMap(sr => sr.resources);
accountRegionExistingResources[accountKey] = {};
accountRegionExistingResources[accountKey][vpcConfig.region] = localRegionalResources;
} else if (!accountRegionExistingResources[accountKey][vpcConfig.region]) {
const localRegionalResources = accountStaticResourcesConfig[accountKey]
.filter(sr => sr.region === vpcConfig.region)
.flatMap(sr => sr.resources);
accountRegionExistingResources[accountKey][vpcConfig.region] = localRegionalResources;
}
const regionStacks = accountStaticResourcesConfig[accountKey].filter(sr => sr.region === vpcConfig.region);
// Get Account & Region Current Max Suffix and update it when it is changed
suffix = accountRegionMaxSuffix[accountKey][vpcConfig.region];
stackSuffix = `${STACK_SUFFIX}-${suffix}`;
const hostedZoneOutputs = HostedZoneOutputFinder.findAll({
outputs,
accountKey,
region: vpcConfig.region,
});
const prevVpcEndpoints = hostedZoneOutputs
.filter(phz => phz.zoneType === 'PRIVATE' && phz.vpcName === vpcConfig.name && !!phz.serviceName)
.map(hz => hz.serviceName);
const removedVpcEndpoints = prevVpcEndpoints.filter(ed => endpointsConfig.endpoints.indexOf(ed!) < 0);
removedVpcEndpoints.map(() => {
// Increasing Limiter to handle removed Interface endpoints from config w.r.t InterfaceEndpoints limits
limiter.create(accountKey, Limit.VpcInterfaceEndpointsPerVpc, vpcConfig.region, vpcConfig.name);
});
for (const endpoint of endpointsConfig.endpoints) {
let newResource = true;
if (!limiter.create(accountKey, Limit.VpcInterfaceEndpointsPerVpc, vpcConfig.region, vpcConfig.name)) {
console.log(
`Skipping endpoint "${endpoint}" creation in VPC "${vpcConfig.name}". Reached maximum interface endpoints per VPC`,
accountKey,
vpcConfig.region,
);
continue;
}
const constructName = `${STACK_SUFFIX}-${vpcConfig.name}-${endpoint}`;
if (accountRegionExistingResources[accountKey][vpcConfig.region].includes(constructName)) {
newResource = false;
const currentStaticResource = regionStacks.find(rs => rs.resources.includes(constructName));
if (currentStaticResource) {
stackSuffix = `${STACK_SUFFIX}-${currentStaticResource.suffix}`;
}
} else {
const existingResources = accountStaticResourcesConfig[accountKey].find(
sr => sr.region === vpcConfig.region && sr.suffix === suffix,
);
if (existingResources && existingResources.resources.length >= MAX_RESOURCES_IN_STACK) {
// Updating Account & Region Max Suffix
accountRegionMaxSuffix[accountKey][vpcConfig.region] = ++suffix;
}
stackSuffix = `${STACK_SUFFIX}-${suffix}`;
}
const loocalAccount = accounts.find(acc => acc.key === accountKey);
const accountStack = accountStacks.tryGetOrCreateAccountStack(
accountKey,
vpcConfig.region,
stackSuffix,
loocalAccount?.inScope,
);
if (!accountStack) {
console.error(`Cannot find account stack ${accountKey}: ${vpcConfig.region}, while Associating Resolver Rules`);
continue;
}
const interfaceEndpoint = new InterfaceEndpoint(
accountStack,
`Endpoint-${vpcConfig.name}-${pascalCase(endpoint)}`,
{
serviceName: endpoint,
vpcId: vpcOutput.vpcId,
vpcRegion: vpcConfig.region,
subnetIds: vpcOutput.subnets.filter(sn => sn.subnetName === endpointsConfig.subnet).map(s => s.subnetId),
allowedCidrs: endpointsConfig['allowed-cidrs']?.map(c => c.toCidrString()),
ports: portOverrides?.[endpoint],
},
);
new CfnHostedZoneOutput(accountStack, `HostedZoneOutput-${vpcConfig.name}-${pascalCase(endpoint)}`, {
accountKey,
domain: interfaceEndpoint.hostedZone.name,
hostedZoneId: interfaceEndpoint.hostedZone.ref,
region: vpcConfig.region,
zoneType: 'PRIVATE',
serviceName: endpoint,
vpcName: vpcConfig.name,
aliasTargetDns: interfaceEndpoint.aliasTargetDns,
});
if (newResource) {
const currentSuffixIndex = allStaticResources.findIndex(
sr => sr.region === vpcConfig.region && sr.suffix === suffix && sr.accountKey === accountKey,
);
const currentAccountSuffixIndex = accountStaticResourcesConfig[accountKey].findIndex(
sr => sr.region === vpcConfig.region && sr.suffix === suffix,
);
if (currentSuffixIndex === -1) {
const currentResourcesObject: StaticResourcesOutput = {
accountKey,
id: `${STACK_SUFFIX}-${vpcConfig.region}-${accountKey}-${suffix}`,
region: vpcConfig.region,
resourceType: RESOURCE_TYPE,
resources: [constructName],
suffix,
};
allStaticResources.push(currentResourcesObject);
accountStaticResourcesConfig[accountKey].push(currentResourcesObject);
} else {
const currentResourcesObject = allStaticResources[currentSuffixIndex];
const currentAccountResourcesObject = accountStaticResourcesConfig[accountKey][currentAccountSuffixIndex];
if (!currentResourcesObject.resources.includes(constructName)) {
currentResourcesObject.resources.push(constructName);
}
if (!currentAccountResourcesObject.resources.includes(constructName)) {
currentAccountResourcesObject.resources.push(constructName);
}
allStaticResources[currentSuffixIndex] = currentResourcesObject;
accountStaticResourcesConfig[accountKey][currentAccountSuffixIndex] = currentAccountResourcesObject;
}
}
}
}
for (const sr of allStaticResources) {
const srLocalAccount = accounts.find(acc => acc.key === sr.accountKey);
const accountStack = accountStacks.tryGetOrCreateAccountStack(
sr.accountKey,
sr.region,
`${STACK_SUFFIX}-${sr.suffix}`,
srLocalAccount?.inScope,
);
if (!accountStack) {
throw new Error(
`Not able to get or create stack for ${sr.accountKey}: ${sr.region}: ${STACK_SUFFIX}-${sr.suffix}`,
);
}
new CfnStaticResourcesOutput(accountStack, `StaticResourceOutput-${sr.suffix}`, sr);
}
}