Skip to content

Commit

Permalink
feat(cli): --all flag to select all stacks (#10745)
Browse files Browse the repository at this point in the history
Adding the --all option to deploy all stacks.

`cdk deploy --all` 

This is an alias of `*`. The previous command does the same as the following one:

`cdk deploy '*'` 

Closes #3222

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
IsmaelMartinez authored Oct 26, 2020
1 parent 35ad608 commit bcd9d0a
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 11 deletions.
8 changes: 8 additions & 0 deletions packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ currently deployed stack to the template and tags that are about to be deployed
will skip deployment if they are identical. Use `--force` to override this behavior
and always deploy the stack.

##### Deploying multiple stacks

You can have multiple stacks in a cdk app. An example can be found in [how to create multiple stacks](https://docs.aws.amazon.com/cdk/latest/guide/stack_how_to_create_multiple_stacks.html).

In order to deploy them, you can list the stacks you want to deploy.

If you want to deploy all of them, you can use the flag `--all` or the wildcard `*` to deploy all stacks in an app.

##### Parameters

Pass parameters to your template during deployment by using `--parameters
Expand Down
25 changes: 16 additions & 9 deletions packages/aws-cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ async function parseCommandLineArguments() {
.option('verbose', { type: 'boolean', alias: 'v', desc: 'Show debug logs (specify multiple times to increase verbosity)', default: false })
.count('verbose')
.option('profile', { type: 'string', desc: 'Use the indicated AWS profile as the default environment', requiresArg: true })
.option('proxy', { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified.', requiresArg: true })
.option('ca-bundle-path', { type: 'string', desc: 'Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified.', requiresArg: true })
.option('ec2creds', { type: 'boolean', alias: 'i', default: undefined, desc: 'Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status.' })
.option('proxy', { type: 'string', desc: 'Use the indicated proxy. Will read from HTTPS_PROXY environment variable if not specified', requiresArg: true })
.option('ca-bundle-path', { type: 'string', desc: 'Path to CA certificate to use when validating HTTPS requests. Will read from AWS_CA_BUNDLE environment variable if not specified', requiresArg: true })
.option('ec2creds', { type: 'boolean', alias: 'i', default: undefined, desc: 'Force trying to fetch EC2 instance credentials. Default: guess EC2 instance status' })
.option('version-reporting', { type: 'boolean', desc: 'Include the "AWS::CDK::Metadata" resource in synthesized templates (enabled by default)', default: undefined })
.option('path-metadata', { type: 'boolean', desc: 'Include "aws:cdk:path" CloudFormation metadata for each resource (enabled by default)', default: true })
.option('asset-metadata', { type: 'boolean', desc: 'Include "aws:asset:*" CloudFormation metadata for resources that user assets (enabled by default)', default: true })
Expand All @@ -79,11 +79,12 @@ async function parseCommandLineArguments() {
.option('cloudformation-execution-policies', { type: 'array', desc: 'The Managed Policy ARNs that should be attached to the role performing deployments into this environment (may be repeated, modern bootstrapping only)', default: [], nargs: 1, requiresArg: true })
.option('force', { alias: 'f', type: 'boolean', desc: 'Always bootstrap even if it would downgrade template version', default: false })
.option('termination-protection', { type: 'boolean', default: undefined, desc: 'Toggle CloudFormation termination protection on the bootstrap stacks' })
.option('show-template', { type: 'boolean', desc: 'Instead of actual bootstrapping, print the current CLI\'s bootstrapping template to stdout for customization.', default: false })
.option('template', { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example).' }),
.option('show-template', { type: 'boolean', desc: 'Instead of actual bootstrapping, print the current CLI\'s bootstrapping template to stdout for customization', default: false })
.option('template', { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)' }),
)
.command('deploy [STACKS..]', 'Deploys the stack(s) named STACKS into your AWS account', yargs => yargs
.option('build-exclude', { type: 'array', alias: 'E', nargs: 1, desc: 'Do not rebuild asset with the given ID. Can be specified multiple times.', default: [] })
.option('all', { type: 'boolean', default: false, desc: 'Deploy all available stacks' })
.option('build-exclude', { type: 'array', alias: 'E', nargs: 1, desc: 'Do not rebuild asset with the given ID. Can be specified multiple times', default: [] })
.option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' })
.option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' })
.option('ci', { type: 'boolean', desc: 'Force CI detection', default: process.env.CI !== undefined })
Expand All @@ -95,9 +96,10 @@ async function parseCommandLineArguments() {
.option('parameters', { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} })
.option('outputs-file', { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true })
.option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' })
.option('progress', { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events.' }),
.option('progress', { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events' }),
)
.command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', yargs => yargs
.option('all', { type: 'boolean', default: false, desc: 'Destroy all available stacks' })
.option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only destroy requested stacks, don\'t include dependees' })
.option('force', { type: 'boolean', alias: 'f', desc: 'Do not ask for confirmation before destroying the stacks' }))
.command('diff [STACKS..]', 'Compares the specified stack with the deployed stack or a local template file, and returns with status 1 if any difference is found', yargs => yargs
Expand Down Expand Up @@ -210,9 +212,14 @@ async function initCommandLine() {
const toolkitStackName: string = ToolkitInfo.determineName(configuration.settings.get(['toolkitStackName']));
debug(`Toolkit stack: ${colors.bold(toolkitStackName)}`);

if (args.all && args.STACKS) {
throw new Error('You must either specify a list of Stacks or the `--all` argument');
}

args.STACKS = args.STACKS || [];
args.ENVIRONMENTS = args.ENVIRONMENTS || [];

const stacks = (args.all) ? ['*'] : args.STACKS;
const cli = new CdkToolkit({
cloudExecutable,
cloudFormation,
Expand Down Expand Up @@ -292,7 +299,7 @@ async function initCommandLine() {
}
}
return cli.deploy({
stackNames: args.STACKS,
stackNames: stacks,
exclusively: args.exclusively,
toolkitStackName,
roleArn: args.roleArn,
Expand All @@ -311,7 +318,7 @@ async function initCommandLine() {

case 'destroy':
return cli.destroy({
stackNames: args.STACKS,
stackNames: stacks,
exclusively: args.exclusively,
force: args.force,
roleArn: args.roleArn,
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-cdk/lib/api/cxapp/cloud-assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class CloudAssembly {
if (stacks.length === 1) {
return new StackCollection(this, stacks);
} else {
throw new Error('Since this app includes more than a single stack, specify which stacks to use (wildcards are supported)\n' +
throw new Error('Since this app includes more than a single stack, specify which stacks to use (wildcards are supported) or specify `--all`\n' +
`Stacks: ${stacks.map(x => x.id).join(' ')}`);
}
default:
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-cdk/test/api/cloud-assembly.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test('select behavior: single', async () => {

// WHEN
await expect(cxasm.selectStacks([], { defaultBehavior: DefaultSelection.OnlySingle }))
.rejects.toThrow('Since this app includes more than a single stack, specify which stacks to use (wildcards are supported)');
.rejects.toThrow('Since this app includes more than a single stack, specify which stacks to use (wildcards are supported) or specify `--all`');
});

test('select behavior: repeat', async () => {
Expand Down
16 changes: 16 additions & 0 deletions packages/aws-cdk/test/cdk-toolkit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ describe('deploy', () => {
await toolkit.deploy({ stackNames: ['Test-Stack-A', 'Test-Stack-B'] });
});

test('with one stack specified', async () => {
// GIVEN
const toolkit = defaultToolkitSetup();

// WHEN
await toolkit.deploy({ stackNames: ['Test-Stack-A'] });
});

test('with stacks all stacks specified as wildcard', async () => {
// GIVEN
const toolkit = defaultToolkitSetup();

// WHEN
await toolkit.deploy({ stackNames: ['*'] });
});

test('with sns notification arns', async () => {
// GIVEN
const notificationArns = ['arn:aws:sns:::cfn-notifications', 'arn:aws:sns:::my-cool-topic'];
Expand Down

0 comments on commit bcd9d0a

Please sign in to comment.