public async deploy()

in packages/aws-cdk/lib/cdk-toolkit.ts [117:264]


  public async deploy(options: DeployOptions) {
    if (options.watch) {
      return this.watch(options);
    }

    const startSynthTime = new Date().getTime();
    const stacks = await this.selectStacksForDeploy(options.selector, options.exclusively, options.cacheCloudAssembly);
    const elapsedSynthTime = new Date().getTime() - startSynthTime;
    print('\n✨  Synthesis time: %ss\n', formatTime(elapsedSynthTime));

    const requireApproval = options.requireApproval ?? RequireApproval.Broadening;

    const parameterMap: { [name: string]: { [name: string]: string | undefined } } = { '*': {} };
    for (const key in options.parameters) {
      if (options.parameters.hasOwnProperty(key)) {
        const [stack, parameter] = key.split(':', 2);
        if (!parameter) {
          parameterMap['*'][stack] = options.parameters[key];
        } else {
          if (!parameterMap[stack]) {
            parameterMap[stack] = {};
          }
          parameterMap[stack][parameter] = options.parameters[key];
        }
      }
    }

    if (options.hotswap) {
      warning('⚠️ The --hotswap flag deliberately introduces CloudFormation drift to speed up deployments');
      warning('⚠️ It should only be used for development - never use it for your production Stacks!');
    }

    const stackOutputs: { [key: string]: any } = { };
    const outputsFile = options.outputsFile;

    for (const stack of stacks.stackArtifacts) {
      if (stacks.stackCount !== 1) { highlight(stack.displayName); }
      if (!stack.environment) {
        // eslint-disable-next-line max-len
        throw new Error(`Stack ${stack.displayName} does not define an environment, and AWS credentials could not be obtained from standard locations or no region was configured.`);
      }

      if (Object.keys(stack.template.Resources || {}).length === 0) { // The generated stack has no resources
        if (!await this.props.cloudFormation.stackExists({ stack })) {
          warning('%s: stack has no resources, skipping deployment.', chalk.bold(stack.displayName));
        } else {
          warning('%s: stack has no resources, deleting existing stack.', chalk.bold(stack.displayName));
          await this.destroy({
            selector: { patterns: [stack.stackName] },
            exclusively: true,
            force: true,
            roleArn: options.roleArn,
            fromDeploy: true,
          });
        }
        continue;
      }

      if (requireApproval !== RequireApproval.Never) {
        const currentTemplate = await this.props.cloudFormation.readCurrentTemplate(stack);
        if (printSecurityDiff(currentTemplate, stack, requireApproval)) {

          // only talk to user if STDIN is a terminal (otherwise, fail)
          if (!process.stdin.isTTY) {
            throw new Error(
              '"--require-approval" is enabled and stack includes security-sensitive updates, ' +
              'but terminal (TTY) is not attached so we are unable to get a confirmation from the user');
          }

          const confirmed = await promptly.confirm('Do you wish to deploy these changes (y/n)?');
          if (!confirmed) { throw new Error('Aborted by user'); }
        }
      }

      print('%s: deploying...', chalk.bold(stack.displayName));
      const startDeployTime = new Date().getTime();

      let tags = options.tags;
      if (!tags || tags.length === 0) {
        tags = tagsForStack(stack);
      }

      let elapsedDeployTime = 0;
      try {
        const result = await this.props.cloudFormation.deployStack({
          stack,
          deployName: stack.stackName,
          roleArn: options.roleArn,
          toolkitStackName: options.toolkitStackName,
          reuseAssets: options.reuseAssets,
          notificationArns: options.notificationArns,
          tags,
          execute: options.execute,
          changeSetName: options.changeSetName,
          force: options.force,
          parameters: Object.assign({}, parameterMap['*'], parameterMap[stack.stackName]),
          usePreviousParameters: options.usePreviousParameters,
          progress: options.progress,
          ci: options.ci,
          rollback: options.rollback,
          hotswap: options.hotswap,
          extraUserAgent: options.extraUserAgent,
        });

        const message = result.noOp
          ? ' ✅  %s (no changes)'
          : ' ✅  %s';

        success('\n' + message, stack.displayName);
        elapsedDeployTime = new Date().getTime() - startDeployTime;
        print('\n✨  Deployment time: %ss\n', formatTime(elapsedDeployTime));

        if (Object.keys(result.outputs).length > 0) {
          print('Outputs:');

          stackOutputs[stack.stackName] = result.outputs;
        }

        for (const name of Object.keys(result.outputs).sort()) {
          const value = result.outputs[name];
          print('%s.%s = %s', chalk.cyan(stack.id), chalk.cyan(name), chalk.underline(chalk.cyan(value)));
        }

        print('Stack ARN:');

        data(result.stackArn);
      } catch (e) {
        error('\n ❌  %s failed: %s', chalk.bold(stack.displayName), e);
        throw e;
      } finally {
        if (options.cloudWatchLogMonitor) {
          const foundLogGroupsResult = await findCloudWatchLogGroups(this.props.sdkProvider, stack);
          options.cloudWatchLogMonitor.addLogGroups(foundLogGroupsResult.env, foundLogGroupsResult.sdk, foundLogGroupsResult.logGroupNames);
        }
        // If an outputs file has been specified, create the file path and write stack outputs to it once.
        // Outputs are written after all stacks have been deployed. If a stack deployment fails,
        // all of the outputs from successfully deployed stacks before the failure will still be written.
        if (outputsFile) {
          fs.ensureFileSync(outputsFile);
          await fs.writeJson(outputsFile, stackOutputs, {
            spaces: 2,
            encoding: 'utf8',
          });
        }
      }
      print('\n✨  Total time: %ss\n', formatTime(elapsedSynthTime + elapsedDeployTime));
    }
  }