export async function step4()

in src/deployments/cdk/src/deployments/central-endpoints/step-4.ts [45:252]


export async function step4(props: CentralEndpointsStep4Props) {
  const { accountStacks, config, outputs, accounts, assumeRole, executionRole } = props;
  const allVpcConfigs = config.getVpcConfigs();

  const globalPrivateHostedZoneIds: { [accountKey: string]: string[] } = {};

  const centralZones = allVpcConfigs.filter(
    vc => vc.vpcConfig['central-endpoint'] && vc.vpcConfig.zones && vc.vpcConfig.zones.private.length > 0,
  );
  const masterAccountKey = config['global-options']['aws-org-management'].account;
  for (const centralZone of centralZones) {
    const hostedZoneOutputs = HostedZoneOutputFinder.findAll({
      outputs,
      accountKey: centralZone.accountKey,
      region: centralZone.vpcConfig.region,
    });
    const centralVpcHostedZones = hostedZoneOutputs.filter(hzo => hzo.vpcName === centralZone.vpcConfig.name);
    if (centralVpcHostedZones) {
      if (!globalPrivateHostedZoneIds[centralZone.accountKey]) {
        globalPrivateHostedZoneIds[centralZone.accountKey] = centralVpcHostedZones
          .filter(cvh => centralZone.vpcConfig.zones?.private.includes(cvh.domain))
          .map(hz => hz.hostedZoneId);
      } else {
        globalPrivateHostedZoneIds[centralZone.accountKey].push(
          ...centralVpcHostedZones
            .filter(cvh => centralZone.vpcConfig.zones?.private.includes(cvh.domain))
            .map(hz => hz.hostedZoneId),
        );
      }
    }
  }

  const staticResources: StaticResourcesOutput[] = StaticResourcesOutputFinder.findAll({
    outputs,
    accountKey: masterAccountKey,
  }).filter(sr => sr.resourceType === RESOURCE_TYPE);

  // Initiate previous stacks to handle deletion of previously deployed stack if there are no resources
  for (const sr of staticResources) {
    const srLocalAccount = accounts.find(acc => acc.key === sr.accountKey);
    accountStacks.tryGetOrCreateAccountStack(
      sr.accountKey,
      sr.region,
      `${STACK_COMMON_SUFFIX}-${sr.suffix}`,
      srLocalAccount?.inScope,
    );
  }

  const existingRegionResources: { [region: string]: string[] } = {};
  const supportedregions = config['global-options']['supported-regions'];

  const regionalMaxSuffix: { [region: string]: number } = {};
  supportedregions.forEach(reg => {
    const localSuffix = staticResources.filter(sr => sr.region === reg).flatMap(r => r.suffix);
    regionalMaxSuffix[reg] = localSuffix.length === 0 ? 1 : Math.max(...localSuffix);
  });

  supportedregions.forEach(reg => {
    existingRegionResources[reg] = staticResources.filter(sr => sr.region === reg).flatMap(r => r.resources);
  });

  for (const { accountKey, vpcConfig } of allVpcConfigs) {
    if (!vpcConfig['use-central-endpoints']) {
      continue;
    }

    const vpcOutput = VpcOutputFinder.tryFindOneByAccountAndRegionAndName({
      outputs,
      accountKey,
      region: vpcConfig.region,
      vpcName: vpcConfig.name,
    });
    if (!vpcOutput) {
      console.warn(`Cannot find VPC "${vpcConfig.name}" in outputs`);
      continue;
    }

    let suffix = regionalMaxSuffix[vpcConfig.region];
    const existingResources = staticResources.find(sr => sr.region === vpcConfig.region && sr.suffix === suffix);

    if (existingResources && existingResources.resources.length >= MAX_RESOURCES_IN_STACK) {
      regionalMaxSuffix[vpcConfig.region] = ++suffix;
    }

    let stackSuffix = `${STACK_COMMON_SUFFIX}-${suffix}`;
    let updateOutputsRequired = true;
    const constructName = `AssociateHostedZones-${accountKey}-${vpcConfig.name}-${vpcConfig.region}`;
    if (existingRegionResources[vpcConfig.region].includes(constructName)) {
      updateOutputsRequired = false;
      const regionStacks = staticResources.filter(sr => sr.region === vpcConfig.region);
      for (const rs of regionStacks) {
        if (rs.resources.includes(constructName)) {
          stackSuffix = `${STACK_COMMON_SUFFIX}-${rs.suffix}`;
          break;
        }
      }
    }

    const accountStack = accountStacks.tryGetOrCreateAccountStack(
      masterAccountKey,
      vpcConfig.region,
      stackSuffix,
      true,
    );
    if (!accountStack) {
      console.error(`Cannot find account stack ${accountKey}: ${vpcConfig.region}, while Associating Resolver Rules`);
      continue;
    }

    const vpcAccountId = getAccountId(accounts, accountKey)!;

    const regionalCentralEndpoint = allVpcConfigs.find(
      vc => vc.vpcConfig.region === vpcConfig.region && vc.vpcConfig['central-endpoint'],
    );
    const hostedZoneIds: string[] = [];
    if (regionalCentralEndpoint) {
      // Retriving Hosted Zone ids for interface endpoints to be shared
      if (!vpcConfig['central-endpoint']) {
        hostedZoneIds.push(...getHostedZoneIds(regionalCentralEndpoint, vpcConfig, outputs));
      }
      if (globalPrivateHostedZoneIds[regionalCentralEndpoint.accountKey]) {
        hostedZoneIds.push(...globalPrivateHostedZoneIds[regionalCentralEndpoint.accountKey]);
      }
      const hostedZoneAccountId = getAccountId(accounts, regionalCentralEndpoint.accountKey)!;
      new AssociateHostedZones(accountStack, constructName, {
        assumeRoleName: assumeRole,
        vpcAccountId,
        vpcName: vpcConfig.name,
        vpcId: vpcOutput.vpcId,
        vpcRegion: vpcConfig.region,
        hostedZoneAccountId,
        hostedZoneIds,
        roleArn: `arn:aws:iam::${cdk.Aws.ACCOUNT_ID}:role/${executionRole}`,
      });
    }

    for (const [hAccountKey, hostedZoneIds] of Object.entries(globalPrivateHostedZoneIds)) {
      if (regionalCentralEndpoint && regionalCentralEndpoint.accountKey === hAccountKey) {
        continue;
      }
      const hostedZoneAccountId = getAccountId(accounts, hAccountKey)!;
      new AssociateHostedZones(
        accountStack,
        `AssociatePrivateZones-${hAccountKey}-${vpcConfig.name}-${vpcConfig.region}`,
        {
          assumeRoleName: assumeRole,
          vpcAccountId,
          vpcName: vpcConfig.name,
          vpcId: vpcOutput.vpcId,
          vpcRegion: vpcConfig.region,
          hostedZoneAccountId,
          hostedZoneIds,
          roleArn: `arn:aws:iam::${cdk.Aws.ACCOUNT_ID}:role/${executionRole}`,
        },
      );
    }

    // Update stackResources Object if new resource came
    if (updateOutputsRequired) {
      const currentSuffixIndex = staticResources.findIndex(
        sr => sr.region === vpcConfig.region && sr.suffix === suffix,
      );
      const vpcAssociationResources = [constructName];
      vpcAssociationResources.push(
        ...Object.keys(globalPrivateHostedZoneIds).map(
          hAccountKey => `AssociatePrivateZones-${hAccountKey}-${vpcConfig.name}-${vpcConfig.region}`,
        ),
      );
      if (currentSuffixIndex === -1) {
        const currentResourcesObject = {
          accountKey: masterAccountKey,
          id: `${STACK_COMMON_SUFFIX}-${vpcConfig.region}-${masterAccountKey}-${suffix}`,
          region: vpcConfig.region,
          resourceType: RESOURCE_TYPE,
          resources: [constructName],
          suffix,
        };
        currentResourcesObject.resources.push(
          ...Object.keys(globalPrivateHostedZoneIds).map(
            hAccountKey => `AssociatePrivateZones-${hAccountKey}-${vpcConfig.name}-${vpcConfig.region}`,
          ),
        );

        staticResources.push(currentResourcesObject);
      } else {
        const currentResourcesObject = staticResources[currentSuffixIndex];
        currentResourcesObject.resources.push(constructName);
        staticResources[currentSuffixIndex] = currentResourcesObject;
      }
    }
  }

  for (const sr of staticResources) {
    const srLocalAccount = accounts.find(acc => acc.key === sr.accountKey);
    const accountStack = accountStacks.tryGetOrCreateAccountStack(
      sr.accountKey,
      sr.region,
      `${STACK_COMMON_SUFFIX}-${sr.suffix}`,
      srLocalAccount?.inScope,
    );
    if (!accountStack) {
      throw new Error(
        `Not able to get or create stack for ${sr.accountKey}: ${sr.region}: ${STACK_COMMON_SUFFIX}-${sr.suffix}`,
      );
    }
    new CfnStaticResourcesOutput(accountStack, `StaticResourceOutput-${sr.suffix}`, sr);
  }
}