Skip to content

Commit

Permalink
feat(cli): write stack outputs to a file (#7020)
Browse files Browse the repository at this point in the history
feat(cli): write stack outputs to a file

Write stack outputs from deployments into a file. A flag `--outputs-file` can be provided where stack outputs will be written in `json` format.

Supports multi-stack and wild-card deployments where all the generated outputs from deployed stacks will be written to the outputs file.

Closes #1773
  • Loading branch information
shivlaks authored Apr 1, 2020
1 parent 1956ded commit 75d5ee9
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 3 deletions.
62 changes: 60 additions & 2 deletions packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,67 @@ Example of overwriting the topic name from a previous deployment.
$ cdk deploy --parameters "ParametersStack:TopicNameParam=blahagain" --force
```

⚠️ Parameters will be applied to all stacks if a stack name is not specified or `*` is provided. Parameters provided to Stacks that do not make use of the parameter will not successfully deploy.
⚠️ Parameters will be applied to all stacks if a stack name is not specified or `*` is provided.
Parameters provided to Stacks that do not make use of the parameter will not successfully deploy.

⚠️ Parameters do not propagate to NestedStacks. These must be sent with the constructor. See Nested Stack [documentation](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cloudformation.NestedStack.html)
⚠️ Parameters do not propagate to NestedStacks. These must be sent with the constructor.
See Nested Stack [documentation](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cloudformation.NestedStack.html)

##### Outputs

Write stack outputs from deployments into a file. When your stack finishes deploying, all stack outputs
will be written to the output file as JSON.

Usage of output in a CDK stack
```typescript
const fn = new lambda.Function(this, "fn", {
handler: "index.handler",
code: lambda.Code.fromInline(`exports.handler = \${handler.toString()}`),
runtime: lambda.Runtime.NODEJS_10_X
});

new cdk.CfnOutput(this, 'FunctionArn', {
value: fn.functionArn,
});
```

Specify an outputs file to write to by supplying the `--outputs-file` parameter

```console
$ cdk deploy --outputs-file outputs.json
```

When the stack finishes deployment, `outputs.json` would look like this:
```json
{
"MyStack": {
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:MyStack-fn5FF616E3-G632ITHSP5HK"
}
}
```

⚠️ The `key` of the outputs corresponds to the logical ID of the `CfnOutput`.
Read more about identifiers in the CDK [here](https://docs.aws.amazon.com/cdk/latest/guide/identifiers.html)

If multiple stacks are being deployed or the wild card `*` is used to deploy all stacks, all outputs
are written to the same output file where each stack artifact ID is a key in the JSON file


```console
$ cdk deploy '*' --outputs-file "/Users/code/myproject/outputs.json"
```

Example `outputs.json` after deployment of multiple stacks
```json
{
"MyStack": {
"FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:MyStack-fn5FF616E3-G632ITHSP5HK"
},
"AnotherStack": {
"VPCId": "vpc-z0mg270fee16693f"
}
}
```

#### `cdk destroy`
Deletes a stack from it's environment. This will cause the resources in the stack to be destroyed (unless they were
Expand Down
4 changes: 3 additions & 1 deletion packages/aws-cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ async function parseCommandLineArguments() {
.option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true })
.option('force', { alias: 'f', type: 'boolean', desc: 'Always deploy stack even if templates are identical', default: false })
.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 })
)
.command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', yargs => yargs
.option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only destroy requested stacks, don\'t include dependees' })
Expand Down Expand Up @@ -238,7 +239,8 @@ async function initCommandLine() {
sdk: aws,
execute: args.execute,
force: args.force,
parameters: parameterMap
parameters: parameterMap,
outputsFile: args.outputsFile
});

case 'destroy':
Expand Down
22 changes: 22 additions & 0 deletions packages/aws-cdk/lib/cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ export class CdkToolkit {
}
}

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

for (const stack of stacks) {
if (stacks.length !== 1) { highlight(stack.displayName); }
if (!stack.environment) {
Expand Down Expand Up @@ -170,6 +173,8 @@ export class CdkToolkit {

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

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

for (const name of Object.keys(result.outputs)) {
Expand All @@ -183,6 +188,17 @@ export class CdkToolkit {
} catch (e) {
error('\n ❌ %s failed: %s', colors.bold(stack.displayName), e);
throw e;
} finally {
// 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);
fs.writeJson(outputsFile, stackOutputs, {
spaces: 2,
encoding: 'utf8'
});
}
}
}
}
Expand Down Expand Up @@ -337,6 +353,12 @@ export interface DeployOptions {
* @default {}
*/
parameters?: { [name: string]: string | undefined };

/**
* Path to file where stack outputs will be written after a successful deploy as JSON
* @default - Outputs are not written to any file
*/
outputsFile?: string;
}

export interface DestroyOptions {
Expand Down
31 changes: 31 additions & 0 deletions packages/aws-cdk/test/integ/cli/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,34 @@ class OtherParameterStack extends cdk.Stack {
}
}

class OutputsStack extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);

const topic = new sns.Topic(this, 'MyOutput', {
topicName: 'MyTopic'
});

new cdk.CfnOutput(this, 'TopicName', {
value: topic.topicName
})
}
}

class AnotherOutputsStack extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);

const topic = new sns.Topic(this, 'MyOtherOutput', {
topicName: 'MyOtherTopic'
});

new cdk.CfnOutput(this, 'TopicName', {
value: topic.topicName
});
}
}

class IamStack extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);
Expand Down Expand Up @@ -208,6 +236,9 @@ new YourStack(app, `${stackPrefix}-test-2`);
// Deploy wildcard with parameters does ${stackPrefix}-param-test-*
new ParameterStack(app, `${stackPrefix}-param-test-1`);
new OtherParameterStack(app, `${stackPrefix}-param-test-2`);
// Deploy stack with outputs does ${stackPrefix}-outputs-test-*
new OutputsStack(app, `${stackPrefix}-outputs-test-1`);
new AnotherOutputsStack(app, `${stackPrefix}-outputs-test-2`);
// Not included in wildcard
new IamStack(app, `${stackPrefix}-iam-test`);
const providing = new ProvidingStack(app, `${stackPrefix}-order-providing`);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"cdk-toolkit-integration-outputs-test-1": {
"TopicName": "MyTopic"
},
"cdk-toolkit-integration-outputs-test-2": {
"TopicName": "MyOtherTopic"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cdk-toolkit-integration-outputs-test-1": {
"TopicName": "MyTopic"
}
}
2 changes: 2 additions & 0 deletions packages/aws-cdk/test/integ/cli/common.bash
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ function cleanup() {
cleanup_stack ${STACK_NAME_PREFIX}-test-2
cleanup_stack ${STACK_NAME_PREFIX}-iam-test
cleanup_stack ${STACK_NAME_PREFIX}-with-nested-stack
cleanup_stack ${STACK_NAME_PREFIX}-outputs-test-1
cleanup_stack ${STACK_NAME_PREFIX}-outputs-test-2
}

function setup() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash
set -euo pipefail
scriptdir=$(cd $(dirname $0) && pwd)
source ${scriptdir}/common.bash
# ----------------------------------------------------------

setup

outputs_file=${integ_test_dir}/outputs/outputs.json
expected_outputs=${scriptdir}/cdk-deploy-wildcard-with-outputs-expected.json

# deploy all outputs stacks
cdk deploy ${STACK_NAME_PREFIX}-outputs-test-\* --outputs-file ${outputs_file}
echo "Stacks deployed successfully"

# verify generated outputs file
generated_outputs_file="$(cat ${outputs_file})"
expected_outputs_file="$(cat ${expected_outputs})"
if [[ "${generated_outputs_file}" != "${expected_outputs_file}" ]]; then
fail "unexpected outputs. Expected: ${expected_outputs_file} Actual: ${generated_outputs_file}"
fi

# destroy
rm ${outputs_file}
cdk destroy -f ${STACK_NAME_PREFIX}-outputs-test-1
cdk destroy -f ${STACK_NAME_PREFIX}-outputs-test-2

echo "✅ success"
26 changes: 26 additions & 0 deletions packages/aws-cdk/test/integ/cli/test-cdk-deploy-with-outputs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash
set -euo pipefail
scriptdir=$(cd $(dirname $0) && pwd)
source ${scriptdir}/common.bash
# ----------------------------------------------------------

setup

outputs_file=${integ_test_dir}/outputs/outputs.json
expected_outputs=${scriptdir}/cdk-deploy-with-outputs-expected.json

cdk deploy -v ${STACK_NAME_PREFIX}-outputs-test-1 --outputs-file ${outputs_file}
echo "Stack deployed successfully"

# verify generated outputs file
generated_outputs_file="$(cat ${outputs_file})"
expected_outputs_file="$(cat ${expected_outputs})"
if [[ "${generated_outputs_file}" != "${expected_outputs_file}" ]]; then
fail "unexpected outputs. Expected: ${expected_outputs_file} Actual: ${generated_outputs_file}"
fi

# destroy
rm ${outputs_file}
cdk destroy -f ${STACK_NAME_PREFIX}-outputs-test-1

echo "✅ success"
2 changes: 2 additions & 0 deletions packages/aws-cdk/test/integ/cli/test-cdk-ls.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ ${STACK_NAME_PREFIX}-iam-test
${STACK_NAME_PREFIX}-lambda
${STACK_NAME_PREFIX}-missing-ssm-parameter
${STACK_NAME_PREFIX}-order-providing
${STACK_NAME_PREFIX}-outputs-test-1
${STACK_NAME_PREFIX}-outputs-test-2
${STACK_NAME_PREFIX}-param-test-1
${STACK_NAME_PREFIX}-param-test-2
${STACK_NAME_PREFIX}-test-1
Expand Down

0 comments on commit 75d5ee9

Please sign in to comment.