Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* and limitations under the License.
*/
import * as t from './types';
import * as crypto from 'crypto';

export const MandatoryAccountType = t.enums('MandatoryAccountType', [
'master',
Expand Down Expand Up @@ -1317,16 +1318,18 @@ export class AcceleratorConfig {
* Find all VPC configurations in mandatory accounts, workload accounts and organizational units. VPC configuration in
* organizational units will have the correct `accountKey` based on the `deploy` value of the VPC configuration.
*/
getVpcConfigs(): ResolvedVpcConfig[] {
getVpcConfigs(appendSuffix: boolean): ResolvedVpcConfig[] {
const vpcConfigs: ResolvedVpcConfig[] = [];

// Add mandatory account VPC configuration first
for (const [accountKey, accountConfig] of this.getMandatoryAccountConfigs()) {
for (const vpcConfig of accountConfig.vpc || []) {
const lzaVpcName = createLzaVpcName(vpcConfig.name, accountKey, vpcConfig.region, appendSuffix);
vpcConfigs.push({
accountKey,
vpcConfig,
ouKey: accountConfig.ou,
lzaVpcName
});
}
}
Expand All @@ -1346,13 +1349,14 @@ export class AcceleratorConfig {
continue;
}
}
vpcConfig.lzaVpcName = `${vpcConfig.name}_${accountKey}`;
vpcConfig.lzaVpcName = createLzaVpcName(vpcConfig.name, accountKey, vpcConfig.region, appendSuffix);
if (vpcConfig['cidr-src'] === 'dynamic') {
const lzaVpcName = createLzaVpcName(vpcConfig.name, accountKey, vpcConfig.region, appendSuffix);
vpcConfigs.push({
ouKey,
accountKey,
vpcConfig,
lzaVpcName: `${vpcConfig.name}_${accountKey}`,
lzaVpcName,
});
}
}
Expand All @@ -1361,6 +1365,7 @@ export class AcceleratorConfig {
ouKey,
vpcConfig,
excludeAccounts,
lzaVpcName: createLzaVpcName(vpcConfig.name, ouKey, vpcConfig.region, appendSuffix),
});
}
} else {
Expand All @@ -1369,6 +1374,7 @@ export class AcceleratorConfig {
ouKey,
accountKey: destinationAccountKey,
vpcConfig,
lzaVpcName: createLzaVpcName(vpcConfig.name, destinationAccountKey, vpcConfig.region, appendSuffix)
});
}
}
Expand All @@ -1381,15 +1387,16 @@ export class AcceleratorConfig {
accountKey,
vpcConfig,
ouKey: accountConfig.ou,
lzaVpcName: createLzaVpcName(vpcConfig.name, accountKey, vpcConfig.region, appendSuffix),
});
}
}

return vpcConfigs;
}

getAzSubnets(accountKey: string, vpcName: string, subnetName: string) {
const vpcConfigs = this.getVpcConfigs();
getAzSubnets(accountKey: string, vpcName: string, subnetName: string, appendSuffix: boolean) {
const vpcConfigs = this.getVpcConfigs(appendSuffix);
const vpcConfig = vpcConfigs.find((v) => v.accountKey === accountKey && v.vpcConfig.name === vpcName)?.vpcConfig;
if (!vpcConfig) {
throw new Error(`VPC named "${vpcName}" not found in account "${accountKey}"`);
Expand All @@ -1406,3 +1413,10 @@ export class AcceleratorConfig {
}));
}
}

export function createLzaVpcName(vpcName: string, accountKey: string, region: string, appendSuffix: boolean): string {
const md5Hash = crypto.createHash('md5').update(`${vpcName}_${accountKey}_${region}`).digest('hex');
const vpcNameWithType = vpcName.endsWith('_vpc') ? vpcName : `${vpcName}_vpc`;
const lzaVpcName = appendSuffix ? `${vpcNameWithType}..${md5Hash.substring(0,5)}` : vpcNameWithType;
return lzaVpcName;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ export interface Config {
skipDriftDetection?: boolean;
localConfigFilePath?: string;
enableTerminationProtection?: boolean;
appendUniqueSuffixToVPCNames?: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
TransitGatewayRouteConfig,
VpcConfig,
VpcFlowLogsDestinationConfig,
createLzaVpcName,
} from './asea-config';
import { loadAseaConfig } from './asea-config/load';
import * as WriteToSourcesTypes from './common//utils/types/writeToSourcesTypes';
Expand Down Expand Up @@ -189,6 +190,7 @@ export class ConvertAseaConfig {
private readonly assumeRoleName: string;
private readonly writeFilesConfig: WriteToSourcesTypes.WriteToSourcesConfig;
private readonly ouToNestedOuMap: Map<string, Set<string>> = new Map();
private readonly appendVpcSuffixes: boolean;
private accounts: Account[] = [];
private outputs: StackOutput[] = [];
private vpcAssignedCidrs: VpcAssignedCidr[] = [];
Expand All @@ -212,6 +214,7 @@ export class ConvertAseaConfig {
this.region = config.homeRegion;
this.centralBucketName = config.centralBucket!;
this.aseaPrefix = config.aseaPrefix!.endsWith('-') ? config.aseaPrefix! : `${config.aseaPrefix}-`;
this.appendVpcSuffixes = config.appendUniqueSuffixToVPCNames ?? false;
this.parametersTable = `${this.aseaPrefix}Parameters`;
this.acceleratorName = config.acceleratorName!;
this.sts = new STS();
Expand Down Expand Up @@ -251,7 +254,7 @@ export class ConvertAseaConfig {
this.subnetAssignedCidrs = await loadSubnetAssignedCidrs(subnetsCidrsTableName(this.aseaPrefix), this.dynamoDb);
this.outputs = await loadOutputs(`${this.aseaPrefix}Outputs`, this.dynamoDb);
this.globalOptions = aseaConfig['global-options'];
this.vpcConfigs = aseaConfig.getVpcConfigs();
this.vpcConfigs = aseaConfig.getVpcConfigs(this.appendVpcSuffixes);
const regionsWithVpc = this.vpcConfigs.map((resolvedConfig) => resolvedConfig.vpcConfig.region);
this.regionsWithoutVpc = this.globalOptions['supported-regions'].filter(
(region) => !regionsWithVpc.includes(region),
Expand Down Expand Up @@ -539,9 +542,9 @@ export class ConvertAseaConfig {
name: createNetworkFirewallName(firewallConfigName, this.aseaPrefix),
subnetChangeProtection: false,
tags: [],
vpc: createVpcName(lzaVpcName ?? vpcConfig.name),
vpc: lzaVpcName!,
subnets: this.getAzSubnets(vpcConfig, networkFirewallConfig.subnet.name).map((subnet) =>
createSubnetName(lzaVpcName ?? vpcConfig.name, subnet.subnetName, subnet.az),
createSubnetName(vpcConfig.name, subnet.subnetName, subnet.az),
),
});
}
Expand Down Expand Up @@ -1776,7 +1779,7 @@ export class ConvertAseaConfig {
name: instanceNameWithAz,
account,
launchTemplate,
vpc: `${vpcName}_vpc`,
vpc: firewallScopedVpcConfig?.lzaVpcName!,
terminationProtection,
detailedMonitoring,
tags,
Expand Down Expand Up @@ -2432,7 +2435,7 @@ export class ConvertAseaConfig {
const setConfigRulesConfig = async () => {
if (!globalOptions['aws-config']) return;
// TODO: Consider account regions for deploymentTargets
const currentNodeRuntime = 'nodejs18.x';
const currentNodeRuntime = 'nodejs20.x';
const rulesWithTarget: (AwsConfigRule & {
deployTo?: string[];
excludedAccounts?: string[];
Expand Down Expand Up @@ -2800,7 +2803,7 @@ export class ConvertAseaConfig {
if (route['target-vpc']) {
return {
account: this.getAccountKeyforLza(globalOptions, route['target-account'] || accountKey),
vpcName: createVpcName(route['target-vpc']),
vpcName: this.getLzaVpcName(route['target-vpc']),
};
} else if (route['target-vpn']) {
return {
Expand Down Expand Up @@ -2967,9 +2970,9 @@ export class ConvertAseaConfig {
sources: [],
};
for (const source of rule.source) {
let sourceVpcAccountKey: string | undefined = undefined;
let sourceVpcConfig: ResolvedVpcConfig | undefined;
if (SubnetSourceConfig.is(source)) {
sourceVpcAccountKey = this.vpcConfigs.find(({ vpcConfig }) => vpcConfig.name === source.vpc)?.accountKey;
sourceVpcConfig = this.vpcConfigs.find(({ vpcConfig }) => vpcConfig.name === source.vpc);
}
if (SecurityGroupSourceConfig.is(source)) {
lzaRule.sources.push({
Expand All @@ -2980,14 +2983,14 @@ export class ConvertAseaConfig {
//account: this.getAccountKeyforLza(globalOptions, source.account || accountKey || ''),
account: this.getAccountKeyforLza(
globalOptions,
sourceVpcAccountKey || source.account || accountKey || '',
sourceVpcConfig?.accountKey || source.account || accountKey || '',
),
subnets: source.subnet.flatMap((sourceSubnet) =>
aseaConfig
.getAzSubnets(sourceVpcAccountKey || source.account || accountKey || '', source.vpc, sourceSubnet)
.getAzSubnets(sourceVpcConfig?.accountKey || source.account || accountKey || '', source.vpc, sourceSubnet, this.appendVpcSuffixes)
.map((s) => createSubnetName(source.vpc, s.subnetName, s.az)),
),
vpc: createVpcName(source.vpc),
vpc: sourceVpcConfig?.lzaVpcName ?? source.vpc,
});
} else {
lzaRule.sources.push(source);
Expand All @@ -3011,7 +3014,6 @@ export class ConvertAseaConfig {
rules: NaclConfig[],
vpcConfig: VpcConfig,
accountKey?: string,
lzaVpcName?: string,
) => {
const lzaRules: (ConvertConfigTypes.LzaNaclInboundRuleType | ConvertConfigTypes.LzaNaclOutboundRuleType)[] = [];
for (const rule of rules) {
Expand Down Expand Up @@ -3055,18 +3057,17 @@ export class ConvertAseaConfig {
});
} else {
// determine which vpc the nacl rule references
// use the lzaVpcName when the config is from ou
let destination: string;
if (dest.vpc === vpcConfig.name) {
destination = createVpcName(lzaVpcName ?? vpcConfig.name);
destination = vpcConfig.name;
} else {
destination = createVpcName(dest.vpc);
destination = dest.vpc;
}
const destinationAccountKey = destinationVpcKey ? this.getAccountKeyforLza(globalOptions, destinationVpcKey): undefined;
target = {
account: destinationVpcKey ? this.getAccountKeyforLza(globalOptions, destinationVpcKey) : undefined,
account: destinationAccountKey,
subnet: createSubnetName(dest.vpc, ruleSubnet.subnetName, ruleSubnet.az),
//vpc: createVpcName(dest.vpc),
vpc: destination,
vpc: createLzaVpcName(destination, destinationAccountKey!, vpcConfig.region, this.appendVpcSuffixes),
region: targetRegion,
};
}
Expand All @@ -3086,7 +3087,7 @@ export class ConvertAseaConfig {
}
return lzaRules;
};
const prepareNaclConfig = (vpcConfig: VpcConfig, accountKey?: string, lzaVpcName?: string) => {
const prepareNaclConfig = (vpcConfig: VpcConfig, accountKey?: string) => {
const naclSubnetConfigs = vpcConfig.subnets?.filter((s) => !!s.nacls);
if (!naclSubnetConfigs) return;
const nacls = [];
Expand All @@ -3100,8 +3101,8 @@ export class ConvertAseaConfig {
subnetAssociations: this.getAzSubnets(vpcConfig, subnetConfig.name).map((s) =>
createSubnetName(vpcConfig.name, s.subnetName, s.az),
),
inboundRules: prepareNaclRules(inboundRules, vpcConfig, accountKey, lzaVpcName),
outboundRules: prepareNaclRules(outboundRules, vpcConfig, accountKey, lzaVpcName),
inboundRules: prepareNaclRules(inboundRules, vpcConfig, accountKey),
outboundRules: prepareNaclRules(outboundRules, vpcConfig, accountKey),
});
}
return nacls;
Expand Down Expand Up @@ -3205,14 +3206,15 @@ export class ConvertAseaConfig {
vpcConfig: VpcConfig,
lzaEndpointsConfig: ConvertConfigTypes.ResolverEndpointsType[],
lzaEndpointsRulesConfig: ConvertConfigTypes.ResolverEndpointRulesType[],
accountKey: string | undefined,
): ConvertConfigTypes.ResolverEndpointsType[] => {
let inboundResolver = vpcConfig.resolvers!.inbound;
let outboundResolver = vpcConfig.resolvers!.outbound;
if (vpcConfig.resolvers) {
if (inboundResolver) {
lzaEndpointsConfig.push({
name: `${vpcConfig.name}InboundEndpoint`,
vpc: createVpcName(vpcConfig.lzaVpcName ?? vpcConfig.name),
vpc: createLzaVpcName(vpcConfig.name, accountKey!, vpcConfig.region, this.appendVpcSuffixes),
subnets:
vpcConfig.subnets
?.find((subnetItem) => subnetItem.name === vpcConfig.resolvers?.subnet)
Expand All @@ -3226,7 +3228,7 @@ export class ConvertAseaConfig {
if (outboundResolver) {
lzaEndpointsConfig.push({
name: `${vpcConfig.name}OutboundEndpoint`,
vpc: createVpcName(vpcConfig.lzaVpcName ?? vpcConfig.name),
vpc: createLzaVpcName(vpcConfig.name, accountKey!, vpcConfig.region, this.appendVpcSuffixes),
subnets:
vpcConfig.subnets
?.find((subnetItem) => subnetItem.name === vpcConfig.resolvers?.subnet)
Expand Down Expand Up @@ -3262,7 +3264,7 @@ export class ConvertAseaConfig {
return lzaEndpointsRulesConfig;
};

const prepareResolverConfig = (vpcConfig: VpcConfig) => {
const prepareResolverConfig = (vpcConfig: VpcConfig, accountKey: string | undefined) => {
let lzaResolverConfig: {
endpoints: ConvertConfigTypes.ResolverEndpointsType[] | undefined;
queryLogs: { name: string; destinations: string[] } | undefined;
Expand All @@ -3274,7 +3276,7 @@ export class ConvertAseaConfig {
let endpoints: any[] = [];
if (vpcConfig.resolvers) {
rules = prepareRulesConfig(vpcConfig, lzaEndpointsRulesConfig);
endpoints = prepareEndpointsConfig(vpcConfig, lzaEndpointsConfig, rules!);
endpoints = prepareEndpointsConfig(vpcConfig, lzaEndpointsConfig, rules!, accountKey);
}

lzaResolverConfig = {
Expand Down Expand Up @@ -3419,7 +3421,7 @@ export class ConvertAseaConfig {

const prepareVpcConfig = ({ accountKey, ouKey, vpcConfig, excludeAccounts, lzaVpcName }: ResolvedVpcConfig) => {
return {
name: createVpcName(lzaVpcName ?? vpcConfig.name),
name: lzaVpcName ?? createVpcName(vpcConfig.name),
account: accountKey ? this.getAccountKeyforLza(globalOptions, accountKey) : undefined,
deploymentTargets: !accountKey
? {
Expand Down Expand Up @@ -3458,13 +3460,13 @@ export class ConvertAseaConfig {
useCentralEndpoints: vpcConfig['use-central-endpoints'],
natGateways: prepareNatGatewayConfig(vpcConfig),
securityGroups: prepareSecurityGroupsConfig(vpcConfig, accountKey),
networkAcls: prepareNaclConfig(vpcConfig, accountKey, lzaVpcName),
networkAcls: prepareNaclConfig(vpcConfig, accountKey),
vpcFlowLogs: prepareVpcFlowLogs(vpcConfig['flow-logs']),
subnets: prepareSubnetConfig(vpcConfig, ouKey, accountKey),
transitGatewayAttachments: prepareTgwAttachConfig(vpcConfig),
virtualPrivateGateway: vpcConfig.vgw,
routeTables: prepareRouteTableConfig(vpcConfig, accountKey),
vpcRoute53Resolver: prepareResolverConfig(vpcConfig),
vpcRoute53Resolver: prepareResolverConfig(vpcConfig, accountKey),
};
};

Expand Down Expand Up @@ -3493,7 +3495,7 @@ export class ConvertAseaConfig {
.filter(({ vpcConfig }) => !!vpcConfig.pcx)
.map(({ vpcConfig }) => ({
name: peeringConnectionName(vpcConfig.name, vpcConfig.pcx!['source-vpc']),
vpcs: [createVpcName(vpcConfig.lzaVpcName ?? vpcConfig.name), createVpcName(vpcConfig.pcx!['source-vpc'])],
vpcs: [this.getLzaVpcName(vpcConfig.name), this.getLzaVpcName(vpcConfig.pcx!['source-vpc'])],
}));
};
await setCertificatesConfig();
Expand Down Expand Up @@ -3660,6 +3662,10 @@ export class ConvertAseaConfig {
);
}

private getLzaVpcName(vpcName: string): string {
return this.vpcConfigs.find((vc) => vc.vpcConfig.name === vpcName )?.lzaVpcName!
}

private getVpcCidr({ accountKey, vpcConfig, ouKey }: { accountKey?: string; vpcConfig: VpcConfig; ouKey?: string }) {
const cidrs: string[] = [];
if (vpcConfig['cidr-src'] === 'provided') {
Expand Down Expand Up @@ -3721,7 +3727,7 @@ export class ConvertAseaConfig {
return ipv4CidrBlock;
}
private async createCloudFormationStacksForALBIpForwarding(aseaConfig: AcceleratorConfig) {
const vpcs = aseaConfig.getVpcConfigs();
const vpcs = aseaConfig.getVpcConfigs(this.appendVpcSuffixes);
const vpcMaps = [];
for (const vpc of vpcs) {
if (vpc.vpcConfig['alb-forwarding']) {
Expand Down
18 changes: 18 additions & 0 deletions src/mkdocs/docs/lza-upgrade/known-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ cat network-config.yaml | yq '[.vpcs[] | select(.account == "shared-network" and

**Resolution or workaround:** If resource synchronization issues are encountered, executing the LZA pipeline can be re-run from the beginning to synchronize resource mappings.

### Duplicate VPC Names

**Description:** After converting the AESA to LZA configuration files you get an error about duplicate VPC names in LZA configuration validation.
> config-validator | network-config.yaml has 1 issues: Duplicate VPC/VPC template names exist. VPC names must be unique

**Root cause:** The name of VPC names in LZA `network-config.yaml` need to be unique globally.

**Resolution or workaround:** A workaround was implemented into LZA to support existing ASEA VPCs that have non-unique names across different accounts or regions. Suffixes in the form of `..uniquehash` can be added to the names of VPC (i.e. `Central_vpc..f7678`) in the LZA configurations files and all their references. LZA will remove the suffix at runtime to match with the existing VPC (`Central_vpc`).

You can activate a configuration option in the upgrade tool to generate suffixes on all VPC references in the configuration.

1. After running the `yarn migration-config` command from the [Preparation phase](./preparation/prereq-config/#configuration)
2. Edit the `src/input-config/input-config.json` file
3. Add the following property to enable the behavior: `"appendUniqueSuffixToVPCNames": true`
4. Continue with the remaining of the LZA upgrade steps. When running `yarn convert-config` with this option, suffixes will be appended to all VPCs from the configuration.

**Note:** The existing VPC resources won't be renamed, the suffix is only used in the configuration. Some internal references will include the suffixes, such as SSM Parameters describing networking resources and path of VPC Flow Logs on S3.

## Landing Zone Accelerator known issues
The following issues will not prevent a successful upgrade from ASEA to LZA, but can impact functionalities and operations in the upgraded Landing Zone.

Expand Down