Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): support Stage properties in App constructor #30050

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions packages/aws-cdk-lib/aws-iam/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,11 +490,7 @@ OR

```ts
new App({
context: {
[PERMISSIONS_BOUNDARY_CONTEXT_KEY]: {
name: 'cdk-${Qualifier}-PermissionsBoundary',
},
},
permissionsBoundary: PermissionsBoundary.fromName('cdk-${Qualifier}-PermissionsBoundary'),
});
```

Expand Down
45 changes: 45 additions & 0 deletions packages/aws-cdk-lib/core/lib/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Construct } from 'constructs';
import { Environment } from './environment';
import { PermissionsBoundary } from './permissions-boundary';
import * as fs from 'fs-extra';
import { PRIVATE_CONTEXT_DEFAULT_STACK_SYNTHESIZER } from './private/private-context';
import { addCustomSynthesis, ICustomSynthesis } from './private/synthesis';
Expand Down Expand Up @@ -126,6 +128,47 @@ export interface AppProps {
* @default - no validation plugins
*/
readonly policyValidationBeta1?: IPolicyValidationPluginBeta1[];

/**
* Default AWS environment (account/region) for `Stack`s in this `App`.
*
* Stacks defined inside this `App` with either `region` or `account` missing
* from its env will use the corresponding field given here.
*
* If either `region` or `account`is is not configured for `Stack` (either on
* the `Stack` itself or on the containing `App` or `Stage`), the Stack will be
* *environment-agnostic*.
*
* Environment-agnostic stacks can be deployed to any environment, may not be
* able to take advantage of all features of the CDK. For example, they will
* not be able to use environmental context lookups, will not automatically
* translate Service Principals to the right format based on the environment's
* AWS partition, and other such enhancements.
*
* @example
*
* // Use a concrete account and region to deploy this Stage to
* new App({
* env: { account: '123456789012', region: 'us-east-1' },
* });
*
* // Use the CLI's current credentials to determine the target environment
* new App({
* env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
* });
*
* @default - The environments should be configured on the `Stack`s.
*/
readonly env?: Environment;

/**
* Options for applying a permissions boundary to all IAM Roles
* and Users created within this App
*
* @default - no permissions boundary is applied
*/
readonly permissionsBoundary?: PermissionsBoundary;

}

/**
Expand Down Expand Up @@ -168,6 +211,8 @@ export class App extends Stage {
super(undefined as any, '', {
outdir: props.outdir ?? process.env[cxapi.OUTDIR_ENV],
policyValidationBeta1: props.policyValidationBeta1,
permissionsBoundary: props.permissionsBoundary,
env: props.env,
});

Object.defineProperty(this, APP_SYMBOL, { value: true });
Expand Down
37 changes: 36 additions & 1 deletion packages/aws-cdk-lib/core/test/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Construct } from 'constructs';
import * as fs from 'fs-extra';
import { ContextProvider } from '../../cloud-assembly-schema';
import * as cxapi from '../../cx-api';
import { CfnResource, DefaultStackSynthesizer, Stack, StackProps } from '../lib';
import { CfnResource, DefaultStackSynthesizer, Stack, StackProps, Stage } from '../lib';
import { Annotations } from '../lib/annotations';
import { App, AppProps } from '../lib/app';

Expand Down Expand Up @@ -361,6 +361,37 @@ describe('app', () => {
expect(app.node.tryGetContext('isNumber')).toEqual(10);
expect(app.node.tryGetContext('isObject')).toEqual({ isString: 'string', isNumber: 10 });
});
test('Stack inherits unspecified part of the env from App', () => {
// GIVEN
const app = new App({
env: { account: 'account', region: 'region' },
});

// WHEN
const stack1 = new Stack(app, 'Stack1', { env: { region: 'elsewhere' } });
const stack2 = new Stack(app, 'Stack2', { env: { account: 'tnuocca' } });

// THEN
expect(acctRegion(stack1)).toEqual(['account', 'elsewhere']);
expect(acctRegion(stack2)).toEqual(['tnuocca', 'region']);
});

test('envs are inherited deeply', () => {
// GIVEN
const app = new App({
env: { account: 'account', region: 'region' },
});

// WHEN
const innerAcct = new Stage(app, 'Acct', { env: { account: 'tnuocca' } });
const innerRegion = new Stage(app, 'Rgn', { env: { region: 'elsewhere' } });
const innerNeither = new Stage(app, 'Neither');

// THEN
expect(acctRegion(new Stack(innerAcct, 'Stack'))).toEqual(['tnuocca', 'region']);
expect(acctRegion(new Stack(innerRegion, 'Stack'))).toEqual(['account', 'elsewhere']);
expect(acctRegion(new Stack(innerNeither, 'Stack'))).toEqual(['account', 'region']);
});
});

class MyConstruct extends Construct {
Expand All @@ -371,3 +402,7 @@ class MyConstruct extends Construct {
new CfnResource(this, 'r2', { type: 'ResourceType2', properties: { FromContext: this.node.tryGetContext('ctx1') } });
}
}

function acctRegion(s: Stack) {
return [s.account, s.region];
}
Loading