export async function deploy()

in src/deployments/cdk/src/apps/phase-2.ts [66:489]


export async function deploy({
  acceleratorConfig,
  accountStacks,
  accounts,
  context,
  outputs,
  limiter,
  organizations,
}: PhaseInput) {
  const { defaultRegion } = context;
  const rootOuId = organizations[0].rootOrgId!;
  // Find the account buckets in the outputs
  const accountBuckets = AccountBucketOutput.getAccountBuckets({
    accounts,
    accountStacks,
    config: acceleratorConfig,
    outputs,
  });

  const aesLogArchiveBucket = AesBucketOutput.getBucket({
    accountStacks,
    config: acceleratorConfig,
    outputs,
  });

  await createTrail.step1({
    accountBuckets,
    accountStacks,
    config: acceleratorConfig,
    outputs,
    context,
  });

  const vpcConfigs = acceleratorConfig.getVpcConfigs();
  /**
   * Add NFW Routes if they exist
   *
   */

  for (const { accountKey, vpcConfig } of vpcConfigs) {
    const vpcOutput = VpcOutputFinder.tryFindOneByAccountAndRegionAndName({
      outputs,
      vpcName: vpcConfig.name,
      region: vpcConfig.region,
      accountKey,
    });
    if (!vpcOutput) {
      console.warn(`Cannot find output with vpc name ${vpcConfig.name}`);
      continue;
    }

    const accountStack = accountStacks.tryGetOrCreateAccountStack(accountKey, vpcConfig.region);
    if (!accountStack) {
      console.warn(`Cannot find account stack ${accountKey}`);
      continue;
    }
    const nfwNameToIdMap = vpcOutput.nfw?.reduce((acc: any, nfwConfig) => {
      const vpcEndpointAz = nfwConfig.az.substr(-1);
      const vpcEndpointId = nfwConfig.vpcEndpoint;
      const mapSubnet = nfwConfig.subnets.filter(subnet => {
        return vpcEndpointAz === subnet.az;
      });
      acc[`NFW_${mapSubnet[0].subnetName}_az${vpcEndpointAz.toUpperCase()}`.toLowerCase()] = vpcEndpointId;
      return acc;
    }, {});
    console.log(nfwNameToIdMap);
    const routeTables = vpcConfig['route-tables'] || [];
    for (const routeTable of routeTables) {
      for (const route of routeTable.routes || []) {
        if (route.target.startsWith('NFW_')) {
          if (typeof route.destination !== 'string') {
            console.warn(`Route for NFW only supports cidr as destination`);
            continue;
          }
          let constructName = `${routeTable.name}_nfw_route`;
          if (route.destination !== '0.0.0.0/0') {
            constructName = `${routeTable.name}_nfw_${route.destination}_route`;
          }
          const routeParams: ec2.CfnRouteProps = {
            routeTableId: vpcOutput.routeTables[routeTable.name],
            destinationCidrBlock: route.destination,
            vpcEndpointId: nfwNameToIdMap[route.target.toLowerCase()],
          };
          new ec2.CfnRoute(accountStack, constructName, routeParams);
        }
      }
    }
  }

  /**
   * Code to create Peering Connection in all accounts
   */

  for (const { accountKey, vpcConfig } of vpcConfigs) {
    const pcxConfig = vpcConfig.pcx;
    if (!PeeringConnectionConfig.is(pcxConfig)) {
      continue;
    }

    const accountStack = accountStacks.tryGetOrCreateAccountStack(accountKey, vpcConfig.region);
    if (!accountStack) {
      console.warn(`Cannot find account stack ${accountKey}`);
      continue;
    }

    const pcxAcceptRole = IamRoleOutputFinder.tryFindOneByName({
      outputs,
      accountKey: pcxConfig.source,
      roleKey: 'PeeringConnectionAcceptRole',
    });
    let peerRoleArn = '';
    if (pcxAcceptRole) {
      peerRoleArn = pcxAcceptRole.roleArn;
    } else {
      const roleName = createRoleName(`VPC-PCX-${pascalCase(accountKey)}To${pascalCase(pcxConfig.source)}`, 0);
      peerRoleArn = `arn:aws:iam::${getAccountId(accounts, pcxConfig.source)}:role/${roleName}`;
    }

    // Get Peer VPC Configuration
    const pcxSourceVpc = pcxConfig['source-vpc'];
    const peerVpcConfig = getVpcConfig(vpcConfigs, pcxConfig.source, pcxSourceVpc);
    if (!VpcConfigType.is(peerVpcConfig)) {
      console.warn(`No configuration found for Peer VPC "${pcxSourceVpc}"`);
      continue;
    }
    const vpcOutput = VpcOutputFinder.tryFindOneByAccountAndRegionAndName({
      outputs,
      accountKey,
      vpcName: vpcConfig.name,
    });
    if (!vpcOutput) {
      console.warn(`No VPC Found in outputs for VPC name "${vpcConfig.name}"`);
      continue;
    }
    const peerVpcOutput = VpcOutputFinder.tryFindOneByAccountAndRegionAndName({
      outputs,
      accountKey: pcxConfig.source,
      vpcName: pcxSourceVpc,
    });
    if (!peerVpcOutput) {
      console.warn(`No VPC Found in outputs for VPC name "${pcxSourceVpc}"`);
      continue;
    }
    const peerOwnerId = getAccountId(accounts, pcxConfig.source);

    const pcx = new ec2.CfnVPCPeeringConnection(accountStack, `${vpcConfig.name}-${pcxSourceVpc}_pcx`, {
      vpcId: vpcOutput.vpcId,
      peerVpcId: peerVpcOutput.vpcId,
      peerRoleArn,
      peerOwnerId,
    });

    new StructuredOutput<PcxOutput>(accountStack, `PcxOutput${vpcConfig.name}`, {
      type: PcxOutputType,
      value: {
        pcxId: pcx.ref,
        vpcs: [
          {
            accountKey,
            vpcId: vpcOutput.vpcId,
            vpcName: vpcOutput.vpcName,
          },
          {
            accountKey: pcxConfig.source,
            vpcId: peerVpcOutput.vpcId,
            vpcName: peerVpcOutput.vpcName,
          },
        ],
      },
    });
  }

  // Creating Security Groups in shared accounts to respective accounts
  for (const { ouKey, accountKey, vpcConfig } of vpcConfigs) {
    const sharedToAccountKeys = getVpcSharedAccountKeys(accounts, vpcConfig, ouKey);
    const shareToAccountIds = Array.from(new Set(sharedToAccountKeys));
    if (sharedToAccountKeys.length > 0) {
      console.log(`Share VPC "${vpcConfig.name}" from Account "${accountKey}" to Accounts "${shareToAccountIds}"`);
    }

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

    for (const [index, sharedAccountKey] of shareToAccountIds.entries()) {
      // Initiating Security Group creation in shared account
      const accountStack = accountStacks.tryGetOrCreateAccountStack(sharedAccountKey, vpcConfig.region);
      if (!accountStack) {
        console.warn(`Cannot find account stack ${sharedAccountKey}`);
        continue;
      }

      /* **********************************************************
       * Saving index in outputs to handle nasty bug occur while
       * changing construct name when account is suspended
       * *********************************************************/
      const sgOutputs: SharedSecurityGroupIndexOutput[] = getStackJsonOutput(outputs, {
        accountKey: sharedAccountKey,
        outputType: 'SecurityGroupIndexOutput',
        region: vpcConfig.region,
      });

      const vpcSgIndex = sgOutputs.find(sgO => sgO.vpcName === vpcConfig.name);
      let sgIndex = index + 1;
      if (vpcSgIndex) {
        sgIndex = vpcSgIndex.index;
      }
      const securityGroupStack = new cdk.NestedStack(accountStack, `SecurityGroups${vpcConfig.name}-Shared-${sgIndex}`);
      const securityGroups = new SecurityGroup(securityGroupStack, `SecurityGroups-SharedAccount-${sgIndex}`, {
        securityGroups: vpcConfig['security-groups']!,
        vpcName: vpcConfig.name,
        vpcId: vpcOutput.vpcId,
        accountKey,
        vpcConfigs,
        sharedAccountKey,
        installerVersion: context.installerVersion,
      });

      // Add Tags Output
      const securityGroupsResources = Object.values(securityGroups.securityGroupNameMapping);

      new JsonOutputValue(securityGroupStack, `SecurityGroupOutput  `, {
        type: 'SecurityGroupsOutput',
        value: {
          vpcId: vpcOutput.vpcId,
          vpcName: vpcConfig.name,
          securityGroupIds: securityGroups.securityGroups.map(securityGroup => ({
            securityGroupId: securityGroup.id,
            securityGroupName: securityGroup.name,
          })),
        },
      });

      new JsonOutputValue(securityGroupStack, `SecurityGroupIndexOutput`, {
        type: 'SecurityGroupIndexOutput',
        value: {
          vpcName: vpcConfig.name,
          index: sgIndex,
        },
      });

      const accountId = getAccountId(accounts, accountKey);
      if (!accountId) {
        console.warn(`Cannot find account with key ${accountKey}`);
        continue;
      }

      new AddTagsToResourcesOutput(securityGroupStack, `OutputSharedResources${vpcConfig.name}-Shared-${index}`, {
        dependencies: securityGroupsResources,
        produceResources: () =>
          securityGroupsResources.map(securityGroup => ({
            resourceId: securityGroup.ref,
            resourceType: 'security-group',
            targetAccountIds: [accountId],
            tags: securityGroup.tags.renderTags(),
            region: securityGroupStack.region,
          })),
      });
    }
  }

  // Deploy Security Hub in Security Account
  // Enables Security Hub, Standards and send invites to member accounts
  await securityHub.step1({
    accountStacks,
    accounts,
    config: acceleratorConfig,
    outputs,
  });

  // Central Services step 2
  await centralServices.step2({
    accountStacks,
    config: acceleratorConfig,
    accounts,
    context,
    outputs,
    rootOuId,
  });

  // Import all VPCs from all outputs
  const allVpcOutputs = VpcOutputFinder.findAll({ outputs });
  const allVpcs = allVpcOutputs.map(vpcDeployment.ImportedVpc.fromOutput);

  // Find the central bucket in the outputs
  const centralBucket = CentralBucketOutput.getBucket({
    accountStacks,
    config: acceleratorConfig,
    outputs,
  });

  await vpcDeployment.step2({
    accountBuckets,
    accountStacks,
    config: acceleratorConfig,
    outputs,
  });

  await madDeployment.step2({
    acceleratorExecutionRoleName: context.acceleratorExecutionRoleName,
    acceleratorPrefix: context.acceleratorPrefix,
    accountStacks,
    config: acceleratorConfig,
    outputs,
  });

  await firewallCluster.step3({
    accountBuckets,
    accountStacks,
    centralBucket,
    config: acceleratorConfig,
    outputs,
    vpcs: allVpcs,
    accounts,
    defaultRegion,
  });

  await firewallManagement.step1({
    accountStacks,
    config: acceleratorConfig,
    vpcs: allVpcs,
    outputs,
    accounts,
    defaultRegion,
  });

  await tgwDeployment.step2({
    accountStacks,
    accounts,
    outputs,
  });

  // Macie step 2
  await macie.step2({
    accountStacks,
    accounts,
    config: acceleratorConfig,
    outputs,
  });

  /**
   * Step 2 of https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_organizations.html
   * Step 3 of https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_organizations.html
   *
   * @param props accountStacks and config passed from phases
   */
  await guardDutyDeployment.step2({
    accountStacks,
    config: acceleratorConfig,
    accounts,
    outputs,
  });

  /**
   * Creating required SNS Topics in Log accont
   */
  await snsDeployment.step1({
    accountStacks,
    config: acceleratorConfig,
    outputs,
    accounts,
  });

  const logBucket = accountBuckets[acceleratorConfig['global-options']['central-log-services'].account];
  await guardDutyDeployment.step3({
    accountStacks,
    config: acceleratorConfig,
    accounts,
    logBucket,
    outputs,
  });

  await logArchiveReadOnlyAccess({
    accountStacks,
    accounts,
    logBucket,
    config: acceleratorConfig,
    acceleratorPrefix: context.acceleratorPrefix,
  });

  await tgwDeployment.acceptPeeringAttachment({
    accountStacks,
    accounts,
    config: acceleratorConfig,
    outputs,
  });

  const masterAccountKey = acceleratorConfig['global-options']['aws-org-management'].account;
  const masterAccountId = getAccountId(accounts, masterAccountKey);
  await vpcDeployment.step3({
    accountStacks,
    config: acceleratorConfig,
    limiter,
    outputs,
    accounts,
  });

  await ssmDeployment.createDocument({
    acceleratorExecutionRoleName: context.acceleratorExecutionRoleName,
    centralAccountId: masterAccountId!,
    centralBucketName: centralBucket.bucketName,
    config: acceleratorConfig,
    accountStacks,
    accounts,
    outputs,
  });

  await alb.step1({
    accountStacks,
    config: acceleratorConfig,
    outputs,
    aesLogArchiveBucket,
    acceleratorExecutionRoleName: context.acceleratorExecutionRoleName,
    accounts,
    deployGlb: true,
  });
}