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));
}
}