diff --git a/API.md b/API.md index abe3f9d8..c3dbf900 100644 --- a/API.md +++ b/API.md @@ -5462,41 +5462,162 @@ const baseStackProps: BaseStackProps = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | +| analyticsReporting | boolean | Include runtime versioning information in this Stack. | +| crossRegionReferences | boolean | Enable this flag to allow native cross region stack references. | +| description | string | A description of the stack. | +| env | aws-cdk-lib.Environment | The AWS environment (account/region) where this stack will be deployed. | +| permissionsBoundary | aws-cdk-lib.PermissionsBoundary | Options for applying a permissions boundary to all IAM Roles and Users created within this Stage. | +| stackName | string | Name to deploy the stack with. | +| synthesizer | aws-cdk-lib.IStackSynthesizer | Synthesis method to use while deploying this stack. | +| tags | {[ key: string ]: string} | Stack tags that will be applied to all the taggable resources and the stack itself. | +| terminationProtection | boolean | Whether to enable termination protection for this stack. | | config | string \| object | *No description.* | | environmentId | string | *No description.* | | permissionsBoundaryArn | string | *No description.* | -| synthesizer | aws-cdk-lib.IStackSynthesizer | *No description.* | -| terminationProtection | boolean | *No description.* | --- -##### `config`Optional +##### `analyticsReporting`Optional ```typescript -public readonly config: string | object; +public readonly analyticsReporting: boolean; ``` -- *Type:* string | object +- *Type:* boolean +- *Default:* `analyticsReporting` setting of containing `App`, or value of 'aws:cdk:version-reporting' context key + +Include runtime versioning information in this Stack. --- -##### `environmentId`Optional +##### `crossRegionReferences`Optional ```typescript -public readonly environmentId: string; +public readonly crossRegionReferences: boolean; +``` + +- *Type:* boolean +- *Default:* false + +Enable this flag to allow native cross region stack references. + +Enabling this will create a CloudFormation custom resource +in both the producing stack and consuming stack in order to perform the export/import + +This feature is currently experimental + +--- + +##### `description`Optional + +```typescript +public readonly description: string; ``` - *Type:* string +- *Default:* No description. + +A description of the stack. --- -##### `permissionsBoundaryArn`Optional +##### `env`Optional ```typescript -public readonly permissionsBoundaryArn: string; +public readonly env: Environment; +``` + +- *Type:* aws-cdk-lib.Environment +- *Default:* The environment of the containing `Stage` if available, otherwise create the stack will be environment-agnostic. + +The AWS environment (account/region) where this stack will be deployed. + +Set the `region`/`account` fields of `env` to either a concrete value to +select the indicated environment (recommended for production stacks), or to +the values of environment variables +`CDK_DEFAULT_REGION`/`CDK_DEFAULT_ACCOUNT` to let the target environment +depend on the AWS credentials/configuration that the CDK CLI is executed +under (recommended for development stacks). + +If the `Stack` is instantiated inside a `Stage`, any undefined +`region`/`account` fields from `env` will default to the same field on the +encompassing `Stage`, if configured there. + +If either `region` or `account` are not set nor inherited from `Stage`, the +Stack will be considered "*environment-agnostic*"". Environment-agnostic +stacks can be deployed to any environment but 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 such as `ec2.Vpc.fromLookup` and will not +automatically translate Service Principals to the right format based on the +environment's AWS partition, and other such enhancements. + +--- + +*Example* + +```typescript +// Use a concrete account and region to deploy this stack to: +// `.account` and `.region` will simply return these values. +new Stack(app, 'Stack1', { + env: { + account: '123456789012', + region: 'us-east-1' + }, +}); + +// Use the CLI's current credentials to determine the target environment: +// `.account` and `.region` will reflect the account+region the CLI +// is configured to use (based on the user CLI credentials) +new Stack(app, 'Stack2', { + env: { + account: process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_DEFAULT_REGION + }, +}); + +// Define multiple stacks stage associated with an environment +const myStage = new Stage(app, 'MyStage', { + env: { + account: '123456789012', + region: 'us-east-1' + } +}); + +// both of these stacks will use the stage's account/region: +// `.account` and `.region` will resolve to the concrete values as above +new MyStack(myStage, 'Stack1'); +new YourStack(myStage, 'Stack2'); + +// Define an environment-agnostic stack: +// `.account` and `.region` will resolve to `{ "Ref": "AWS::AccountId" }` and `{ "Ref": "AWS::Region" }` respectively. +// which will only resolve to actual values by CloudFormation during deployment. +new MyStack(app, 'Stack1'); +``` + + +##### `permissionsBoundary`Optional + +```typescript +public readonly permissionsBoundary: PermissionsBoundary; +``` + +- *Type:* aws-cdk-lib.PermissionsBoundary +- *Default:* no permissions boundary is applied + +Options for applying a permissions boundary to all IAM Roles and Users created within this Stage. + +--- + +##### `stackName`Optional + +```typescript +public readonly stackName: string; ``` - *Type:* string +- *Default:* Derived from construct path. + +Name to deploy the stack with. --- @@ -5507,6 +5628,32 @@ public readonly synthesizer: IStackSynthesizer; ``` - *Type:* aws-cdk-lib.IStackSynthesizer +- *Default:* The synthesizer specified on `App`, or `DefaultStackSynthesizer` otherwise. + +Synthesis method to use while deploying this stack. + +The Stack Synthesizer controls aspects of synthesis and deployment, +like how assets are referenced and what IAM roles to use. For more +information, see the README of the main CDK package. + +If not specified, the `defaultStackSynthesizer` from `App` will be used. +If that is not specified, `DefaultStackSynthesizer` is used if +`@aws-cdk/core:newStyleStackSynthesis` is set to `true` or the CDK major +version is v2. In CDK v1 `LegacyStackSynthesizer` is the default if no +other synthesizer is specified. + +--- + +##### `tags`Optional + +```typescript +public readonly tags: {[ key: string ]: string}; +``` + +- *Type:* {[ key: string ]: string} +- *Default:* {} + +Stack tags that will be applied to all the taggable resources and the stack itself. --- @@ -5517,6 +5664,39 @@ public readonly terminationProtection: boolean; ``` - *Type:* boolean +- *Default:* false + +Whether to enable termination protection for this stack. + +--- + +##### `config`Optional + +```typescript +public readonly config: string | object; +``` + +- *Type:* string | object + +--- + +##### `environmentId`Optional + +```typescript +public readonly environmentId: string; +``` + +- *Type:* string + +--- + +##### `permissionsBoundaryArn`Optional + +```typescript +public readonly permissionsBoundaryArn: string; +``` + +- *Type:* string --- @@ -5543,9 +5723,9 @@ const cICDPipelineStackProps: CICDPipelineStackProps = { ... } | synthesizer | aws-cdk-lib.IStackSynthesizer | Synthesis method to use while deploying this stack. | | tags | {[ key: string ]: string} | Stack tags that will be applied to all the taggable resources and the stack itself. | | terminationProtection | boolean | Whether to enable termination protection for this stack. | -| config | object | *No description.* | -| configPath | string | *No description.* | +| config | string \| object | *No description.* | | environmentId | string | *No description.* | +| permissionsBoundaryArn | string | *No description.* | | pipelineName | string | *No description.* | --- @@ -5746,27 +5926,27 @@ Whether to enable termination protection for this stack. ##### `config`Optional ```typescript -public readonly config: object; +public readonly config: string | object; ``` -- *Type:* object +- *Type:* string | object --- -##### `configPath`Optional +##### `environmentId`Optional ```typescript -public readonly configPath: string; +public readonly environmentId: string; ``` - *Type:* string --- -##### `environmentId`Optional +##### `permissionsBoundaryArn`Optional ```typescript -public readonly environmentId: string; +public readonly permissionsBoundaryArn: string; ``` - *Type:* string diff --git a/integ/integ.cicd-pipeline.test.ts b/integ/integ.cicd-pipeline.test.ts index 46056452..699ed6b5 100644 --- a/integ/integ.cicd-pipeline.test.ts +++ b/integ/integ.cicd-pipeline.test.ts @@ -1,9 +1,7 @@ -import path from "path"; import * as integration from "@aws-cdk/integ-tests-alpha"; import * as cdk from "aws-cdk-lib"; import * as lambda from "aws-cdk-lib/aws-lambda"; import * as s3 from "aws-cdk-lib/aws-s3"; -import * as glue_alpha from "@aws-cdk/aws-glue-alpha"; import { Construct } from "constructs"; import { RequireApproval } from "aws-cdk-lib/cloud-assembly-schema"; @@ -14,7 +12,7 @@ interface CICDPipelineTestStackProps extends cdk.StackProps {} class CICDPipelineTestStack extends cdk.Stack { constructor(scope: Construct, id: string, props: CICDPipelineTestStackProps) { super(scope, id, props); - const devStage = new cdk.Stage(this, "dev", { env: { account: "228197580683" } }); + const devStage = new cdk.Stage(this, "dev"); const devStack = new cdk.Stack(devStage, "application-stack"); const bucket = new s3.Bucket(devStack, "Bucket", {removalPolicy: cdk.RemovalPolicy.DESTROY}); @@ -33,7 +31,7 @@ class CICDPipelineTestStack extends cdk.Stack { pipeline.addStage({ stage: firehoseToS3Stage }).addStage({ stage: sqsToLambdaStage }); - new CICDPipelineStack(this, "dummy-pipeline", { environmentId: "dev", pipelineName: "dummy-pipeline" }) + new CICDPipelineStack(this, "dummy-pipeline", { environmentId: "cicd", pipelineName: "dummy-pipeline" }) .addSourceAction({ repositoryName: "dummy-repository" }) .addSynthAction() .buildPipeline() diff --git a/src/base/stack.ts b/src/base/stack.ts index 7eebbd1e..5d9461b4 100644 --- a/src/base/stack.ts +++ b/src/base/stack.ts @@ -3,10 +3,8 @@ import * as iam from "aws-cdk-lib/aws-iam"; import { Construct } from "constructs"; import { getStackSynthesizer } from "../config"; -export interface BaseStackProps { - readonly terminationProtection?: boolean | undefined; +export interface BaseStackProps extends cdk.StackProps { readonly permissionsBoundaryArn?: string; - readonly synthesizer?: cdk.IStackSynthesizer; readonly environmentId?: string; readonly config?: string | object; } @@ -20,7 +18,7 @@ export class BaseStack extends cdk.Stack { ? props.synthesizer : getStackSynthesizer({ environmentId: environmentId, config: props.config }); - super(scope, id, { synthesizer: synthesizer, terminationProtection: props.terminationProtection }); + super(scope, id, { synthesizer: synthesizer, ...props }); if (props.permissionsBoundaryArn) { iam.PermissionsBoundary.of(scope).apply( diff --git a/src/cicd/pipelines.ts b/src/cicd/pipelines.ts index 8f21218a..f85f7f71 100644 --- a/src/cicd/pipelines.ts +++ b/src/cicd/pipelines.ts @@ -7,7 +7,7 @@ import * as pipelines from "aws-cdk-lib/pipelines"; import { Construct, IConstruct } from "constructs"; import { CICDActions } from "./actions"; import { toTitleCase } from "./utils"; -import { BaseStack } from "../base"; +import { BaseStack, BaseStackProps } from "../base"; import { Configurator } from "../config"; export interface SourceActionProps { @@ -58,11 +58,8 @@ export interface AddCustomStageProps { readonly steps: pipelines.Step[]; } -export interface CICDPipelineStackProps extends cdk.StackProps { - readonly environmentId?: string; +export interface CICDPipelineStackProps extends BaseStackProps { readonly pipelineName?: string; - readonly configPath?: string; - readonly config?: object; } export interface AdditionalPipelineProps { @@ -97,7 +94,7 @@ export class CICDPipelineStack extends BaseStack { this.environmentId = props.environmentId; this.pipelineName = props.pipelineName; this.pipelineId = id; - const config = props.configPath ?? props.config ?? "./ddk.json"; + const config = props.config ?? "./ddk.json"; this.config = new Configurator(this, config, this.environmentId); } diff --git a/test/base-stack.test.ts b/test/base-stack.test.ts index 97d67de8..334d3b37 100644 --- a/test/base-stack.test.ts +++ b/test/base-stack.test.ts @@ -389,3 +389,13 @@ test("Bring Your Own Stack Synthesizer", () => { ); } }); + +test("Additional Stack Props", () => { + const app = new cdk.App(); + + new BaseStack(app, "my-stack", { + environmentId: "dev", + description: "My Description", + stackName: "MyStack", + }); +}); diff --git a/test/cicd-pipeline-stack.test.ts b/test/cicd-pipeline-stack.test.ts index beeff993..c72bd5e4 100644 --- a/test/cicd-pipeline-stack.test.ts +++ b/test/cicd-pipeline-stack.test.ts @@ -614,3 +614,30 @@ test("Test Pipeline with Artifact Upload", () => { Template.fromStack(stack); }); + +test("CICDPipeline with different environments", () => { + const app = new cdk.App(); + + const stageA = new cdk.Stage(app, "foo", { env: { account: "000000000000", region: "us-east-1" } }); + const stackA = new cdk.Stack(stageA, "application-stack"); + new s3.Bucket(stackA, "BucketA"); + + const stageB = new cdk.Stage(app, "bar", { env: { account: "111111111111", region: "us-east-1" } }); + const stackB = new cdk.Stack(stageB, "application-stack"); + new s3.Bucket(stackB, "BucketB"); + + const stack = new CICDPipelineStack(app, "dummy-pipeline", { + env: { region: "us-east-1" }, + environmentId: "dev", + pipelineName: "dummy-pipeline", + }) + .addSourceAction({ repositoryName: "dummy-repository" }) + .addSynthAction() + .buildPipeline() + .addStage({ stageId: "a", stage: stageA }) + .addStage({ stageId: "b", stage: stageB }) + .synth(); + + const template = Template.fromStack(stack); + template.resourceCountIs("AWS::CodePipeline::Pipeline", 1); +});