public async diff()

in packages/aws-cdk/lib/cli/cdk-toolkit.ts [202:340]


  public async diff(options: DiffOptions): Promise<number> {
    const stacks = await this.selectStacksForDiff(options.stackNames, options.exclusively);

    const strict = !!options.strict;
    const contextLines = options.contextLines || 3;
    const quiet = options.quiet || false;

    let diffs = 0;
    const parameterMap = buildParameterMap(options.parameters);

    if (options.templatePath !== undefined) {
      // Compare single stack against fixed template
      if (stacks.stackCount !== 1) {
        throw new ToolkitError(
          'Can only select one stack when comparing to fixed template. Use --exclusively to avoid selecting multiple stacks.',
        );
      }

      if (!(await fs.pathExists(options.templatePath))) {
        throw new ToolkitError(`There is no file at ${options.templatePath}`);
      }

      const template = deserializeStructure(await fs.readFile(options.templatePath, { encoding: 'UTF-8' }));
      const formatter = new DiffFormatter({
        ioHelper: asIoHelper(this.ioHost, 'diff'),
        templateInfo: {
          oldTemplate: template,
          newTemplate: stacks.firstStack,
        },
      });

      if (options.securityOnly) {
        const securityDiff = formatter.formatSecurityDiff();
        // Warn, count, and display the diff only if the reported changes are broadening permissions
        if (securityDiff.permissionChangeType === PermissionChangeType.BROADENING) {
          warning('This deployment will make potentially sensitive changes according to your current security approval level.\nPlease confirm you intend to make the following modifications:\n');
          info(securityDiff.formattedDiff);
          diffs += 1;
        }
      } else {
        const diff = formatter.formatStackDiff({
          strict,
          context: contextLines,
          quiet,
        });
        diffs = diff.numStacksWithChanges;
        info(diff.formattedDiff);
      }
    } else {
      // Compare N stacks against deployed templates
      for (const stack of stacks.stackArtifacts) {
        const templateWithNestedStacks = await this.props.deployments.readCurrentTemplateWithNestedStacks(
          stack,
          options.compareAgainstProcessedTemplate,
        );
        const currentTemplate = templateWithNestedStacks.deployedRootTemplate;
        const nestedStacks = templateWithNestedStacks.nestedStacks;

        const migrator = new ResourceMigrator({
          deployments: this.props.deployments,
          ioHelper: asIoHelper(this.ioHost, 'diff'),
        });
        const resourcesToImport = await migrator.tryGetResources(await this.props.deployments.resolveEnvironment(stack));
        if (resourcesToImport) {
          removeNonImportResources(stack);
        }

        let changeSet = undefined;

        if (options.changeSet) {
          let stackExists = false;
          try {
            stackExists = await this.props.deployments.stackExists({
              stack,
              deployName: stack.stackName,
              tryLookupRole: true,
            });
          } catch (e: any) {
            debug(formatErrorMessage(e));
            if (!quiet) {
              info(
                `Checking if the stack ${stack.stackName} exists before creating the changeset has failed, will base the diff on template differences (run again with -v to see the reason)\n`,
              );
            }
            stackExists = false;
          }

          if (stackExists) {
            changeSet = await cfnApi.createDiffChangeSet(asIoHelper(this.ioHost, 'diff'), {
              stack,
              uuid: uuid.v4(),
              deployments: this.props.deployments,
              willExecute: false,
              sdkProvider: this.props.sdkProvider,
              parameters: Object.assign({}, parameterMap['*'], parameterMap[stack.stackName]),
              resourcesToImport,
            });
          } else {
            debug(
              `the stack '${stack.stackName}' has not been deployed to CloudFormation or describeStacks call failed, skipping changeset creation.`,
            );
          }
        }

        const formatter = new DiffFormatter({
          ioHelper: asIoHelper(this.ioHost, 'diff'),
          templateInfo: {
            oldTemplate: currentTemplate,
            newTemplate: stack,
            changeSet,
            isImport: !!resourcesToImport,
            nestedStacks,
          },
        });

        if (options.securityOnly) {
          const securityDiff = formatter.formatSecurityDiff();
          // Warn, count, and display the diff only if the reported changes are broadening permissions
          if (securityDiff.permissionChangeType === PermissionChangeType.BROADENING) {
            warning('This deployment will make potentially sensitive changes according to your current security approval level.\nPlease confirm you intend to make the following modifications:\n');
            info(securityDiff.formattedDiff);
            diffs += 1;
          }
        } else {
          const diff = formatter.formatStackDiff({
            strict,
            context: contextLines,
            quiet,
          });
          info(diff.formattedDiff);
          diffs += diff.numStacksWithChanges;
        }
      }
    }

    info(format('\n✨  Number of stacks with differences: %s\n', diffs));

    return diffs && options.fail ? 1 : 0;
  }