Skip to content

Commit b705956

Browse files
authored
feat(core): add option to suppress indentation in templates (#25892)
Fixes #18694, #8712 This change adds an option to suppress indentation in CloudFormation template files. Suppressing indentation will reduce the file size of templates. Indentation can be set by enabling for specific Stacks using the new `suppressTemplateIndentation` property in `StackProps`, or globally using the new `@aws-cdk/core:suppressTemplateIndentation` context key. This PR provides additional template size reduction beyond the indentation change in #19656. @rix0rrr @mackalex @PatMyron ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 97d2fab commit b705956

File tree

4 files changed

+107
-2
lines changed

4 files changed

+107
-2
lines changed

packages/aws-cdk-lib/core/README.md

+14
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,20 @@ It's possible to synthesize the project with more Resources than the allowed (or
12071207

12081208
Set the context key `@aws-cdk/core:stackResourceLimit` with the proper value, being 0 for disable the limit of resources.
12091209

1210+
### Template Indentation
1211+
1212+
The AWS CloudFormation templates generated by CDK include indentation by default.
1213+
Indentation makes the templates more readable, but also increases their size,
1214+
and CloudFormation templates cannot exceed 1MB.
1215+
1216+
It's possible to reduce the size of your templates by suppressing indentation.
1217+
1218+
To do this for all templates, set the context key `@aws-cdk/core:suppressTemplateIndentation` to `true`.
1219+
1220+
To do this for a specific stack, add a `suppressTemplateIndentation: true` property to the
1221+
stack's `StackProps` parameter. You can also set this property to `false` to override
1222+
the context key setting.
1223+
12101224
## App Context
12111225

12121226
[Context values](https://docs.aws.amazon.com/cdk/v2/guide/context.html) are key-value pairs that can be associated with an app, stack, or construct.

packages/aws-cdk-lib/core/lib/stack.ts

+44-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const MY_STACK_CACHE = Symbol.for('@aws-cdk/core.Stack.myStack');
2828

2929
export const STACK_RESOURCE_LIMIT_CONTEXT = '@aws-cdk/core:stackResourceLimit';
3030

31+
const SUPPRESS_TEMPLATE_INDENTATION_CONTEXT = '@aws-cdk/core:suppressTemplateIndentation';
32+
const TEMPLATE_BODY_MAXIMUM_SIZE = 1_000_000;
33+
3134
const VALID_STACK_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-]*$/;
3235

3336
const MAX_RESOURCES = 500;
@@ -172,6 +175,18 @@ export interface StackProps {
172175
* @default - no permissions boundary is applied
173176
*/
174177
readonly permissionsBoundary?: PermissionsBoundary;
178+
179+
/**
180+
* Enable this flag to suppress indentation in generated
181+
* CloudFormation templates.
182+
*
183+
* If not specified, the value of the `@aws-cdk/core:suppressTemplateIndentation`
184+
* context key will be used. If that is not specified, then the
185+
* default value `false` will be used.
186+
*
187+
* @default - the value of `@aws-cdk/core:suppressTemplateIndentation`, or `false` if that is not set.
188+
*/
189+
readonly suppressTemplateIndentation?: boolean;
175190
}
176191

177192
/**
@@ -359,6 +374,18 @@ export class Stack extends Construct implements ITaggable {
359374

360375
private readonly _stackName: string;
361376

377+
/**
378+
* Enable this flag to suppress indentation in generated
379+
* CloudFormation templates.
380+
*
381+
* If not specified, the value of the `@aws-cdk/core:suppressTemplateIndentation`
382+
* context key will be used. If that is not specified, then the
383+
* default value `false` will be used.
384+
*
385+
* @default - the value of `@aws-cdk/core:suppressTemplateIndentation`, or `false` if that is not set.
386+
*/
387+
private readonly _suppressTemplateIndentation: boolean;
388+
362389
/**
363390
* Creates a new stack.
364391
*
@@ -385,6 +412,7 @@ export class Stack extends Construct implements ITaggable {
385412
this._stackDependencies = { };
386413
this.templateOptions = { };
387414
this._crossRegionReferences = !!props.crossRegionReferences;
415+
this._suppressTemplateIndentation = props.suppressTemplateIndentation ?? this.node.tryGetContext(SUPPRESS_TEMPLATE_INDENTATION_CONTEXT) ?? false;
388416

389417
Object.defineProperty(this, STACK_SYMBOL, { value: true });
390418

@@ -1047,7 +1075,22 @@ export class Stack extends Construct implements ITaggable {
10471075
Annotations.of(this).addInfo(`Number of resources: ${numberOfResources} is approaching allowed maximum of ${this.maxResources}`);
10481076
}
10491077
}
1050-
fs.writeFileSync(outPath, JSON.stringify(template, undefined, 1));
1078+
1079+
const indent = this._suppressTemplateIndentation ? undefined : 1;
1080+
const templateData = JSON.stringify(template, undefined, indent);
1081+
1082+
if (templateData.length > (TEMPLATE_BODY_MAXIMUM_SIZE * 0.8)) {
1083+
const verb = templateData.length > TEMPLATE_BODY_MAXIMUM_SIZE ? 'exceeds' : 'is approaching';
1084+
const advice = this._suppressTemplateIndentation ?
1085+
'Split resources into multiple stacks to reduce template size' :
1086+
'Split resources into multiple stacks or set suppressTemplateIndentation to reduce template size';
1087+
1088+
const message = `Template size ${verb} limit: ${templateData.length}/${TEMPLATE_BODY_MAXIMUM_SIZE}. ${advice}.`;
1089+
1090+
Annotations.of(this).addWarning(message);
1091+
}
1092+
1093+
fs.writeFileSync(outPath, templateData);
10511094

10521095
for (const ctx of this._missingContext) {
10531096
if (lookupRoleArn != null) {

packages/aws-cdk-lib/core/test/stack.test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as fs from 'fs';
12
import { testDeprecated } from '@aws-cdk/cdk-build-tools';
23
import { Construct, Node } from 'constructs';
34
import { toCloudFormation } from './util';
@@ -2104,6 +2105,49 @@ describe('stack', () => {
21042105
});
21052106
}).toThrowError('Region of stack environment must be a \'string\' but received \'number\'');
21062107
});
2108+
2109+
test('indent templates when suppressTemplateIndentation is not set', () => {
2110+
const app = new App();
2111+
2112+
const stack = new Stack(app, 'Stack');
2113+
new CfnResource(stack, 'MyResource', { type: 'MyResourceType' });
2114+
2115+
const assembly = app.synth();
2116+
const artifact = assembly.getStackArtifact(stack.artifactId);
2117+
const templateData = fs.readFileSync(artifact.templateFullPath, 'utf-8');
2118+
2119+
expect(templateData).toMatch(/^{\n \"Resources\": {\n \"MyResource\": {\n \"Type\": \"MyResourceType\"\n }\n }/);
2120+
});
2121+
2122+
test('do not indent templates when suppressTemplateIndentation is true', () => {
2123+
const app = new App();
2124+
2125+
const stack = new Stack(app, 'Stack', { suppressTemplateIndentation: true });
2126+
new CfnResource(stack, 'MyResource', { type: 'MyResourceType' });
2127+
2128+
const assembly = app.synth();
2129+
const artifact = assembly.getStackArtifact(stack.artifactId);
2130+
const templateData = fs.readFileSync(artifact.templateFullPath, 'utf-8');
2131+
2132+
expect(templateData).toMatch(/^{\"Resources\":{\"MyResource\":{\"Type\":\"MyResourceType\"}}/);
2133+
});
2134+
2135+
test('do not indent templates when @aws-cdk/core:suppressTemplateIndentation is true', () => {
2136+
const app = new App({
2137+
context: {
2138+
'@aws-cdk/core:suppressTemplateIndentation': true,
2139+
},
2140+
});
2141+
2142+
const stack = new Stack(app, 'Stack');
2143+
new CfnResource(stack, 'MyResource', { type: 'MyResourceType' });
2144+
2145+
const assembly = app.synth();
2146+
const artifact = assembly.getStackArtifact(stack.artifactId);
2147+
const templateData = fs.readFileSync(artifact.templateFullPath, 'utf-8');
2148+
2149+
expect(templateData).toMatch(/^{\"Resources\":{\"MyResource\":{\"Type\":\"MyResourceType\"}}/);
2150+
});
21072151
});
21082152

21092153
describe('permissions boundary', () => {

packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,11 @@ test('Policy sizes do not exceed the maximum size', () => {
171171
}
172172
}
173173

174-
Annotations.fromStack(pipelineStack).hasNoWarning('*', Match.anyValue());
174+
// expect template size warning, but no other warnings
175+
const annotations = Annotations.fromStack(pipelineStack);
176+
annotations.hasWarning('*', Match.stringLikeRegexp('^Template size is approaching limit'));
177+
const warnings = annotations.findWarning('*', Match.anyValue());
178+
expect(warnings.length).toEqual(1);
175179
});
176180

177181
test('CodeBuild action role has the right AssumeRolePolicyDocument', () => {

0 commit comments

Comments
 (0)