diff --git a/packages/@aws-cdk/pipelines/README.md b/packages/@aws-cdk/pipelines/README.md index bf9def55c5ced..57e0b0b3ed15f 100644 --- a/packages/@aws-cdk/pipelines/README.md +++ b/packages/@aws-cdk/pipelines/README.md @@ -544,6 +544,32 @@ const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { }); ``` +#### Deploying without change sets + +Deployment is done by default with `CodePipeline` engine using change sets, +i.e. to first create a change set and then execute it. This allows you to inject +steps that inspect the change set and approve or reject it, but failed deployments +are not retryable and creation of the change set costs time. + +The creation of change sets can be switched off by setting `useChangeSets: false`: + +```ts +declare const synth: pipelines.ShellStep; + +class PipelineStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { + synth, + + // Disable change set creation and make deployments in pipeline as single step + useChangeSets: false, + }); + } +} +``` + ### Validation Every `addStage()` and `addWave()` command takes additional options. As part of these options, @@ -867,11 +893,11 @@ because those values are passed in directly to the underlying `codepipeline.Pipe Docker can be used in 3 different places in the pipeline: -* If you are using Docker image assets in your application stages: Docker will +- If you are using Docker image assets in your application stages: Docker will run in the asset publishing projects. -* If you are using Docker image assets in your stack (for example as +- If you are using Docker image assets in your stack (for example as images for your CodeBuild projects): Docker will run in the self-mutate project. -* If you are using Docker to bundle file assets anywhere in your project (for +- If you are using Docker to bundle file assets anywhere in your project (for example, if you are using such construct libraries as `@aws-cdk/aws-lambda-nodejs`): Docker will run in the *synth* project. @@ -1069,26 +1095,26 @@ $ npx cdk bootstrap \ These command lines explained: -* `npx`: means to use the CDK CLI from the current NPM install. If you are using +- `npx`: means to use the CDK CLI from the current NPM install. If you are using a global install of the CDK CLI, leave this out. -* `--profile`: should indicate a profile with administrator privileges that has +- `--profile`: should indicate a profile with administrator privileges that has permissions to provision a pipeline in the indicated account. You can leave this flag out if either the AWS default credentials or the `AWS_*` environment variables confer these permissions. -* `--cloudformation-execution-policies`: ARN of the managed policy that future CDK +- `--cloudformation-execution-policies`: ARN of the managed policy that future CDK deployments should execute with. By default this is `AdministratorAccess`, but if you also specify the `--trust` flag to give another Account permissions to deploy into the current account, you must specify a value here. -* `--trust`: indicates which other account(s) should have permissions to deploy +- `--trust`: indicates which other account(s) should have permissions to deploy CDK applications into this account. In this case we indicate the Pipeline's account, but you could also use this for developer accounts (don't do that for production application accounts though!). -* `--trust-for-lookup`: gives a more limited set of permissions to the +- `--trust-for-lookup`: gives a more limited set of permissions to the trusted account, only allowing it to look up values such as availability zones, EC2 images and VPCs. `--trust-for-lookup` does not give permissions to modify anything in the account. Note that `--trust` implies `--trust-for-lookup`, so you don't need to specify the same acocunt twice. -* `aws://222222222222/us-east-2`: the account and region we're bootstrapping. +- `aws://222222222222/us-east-2`: the account and region we're bootstrapping. > Be aware that anyone who has access to the trusted Accounts **effectively has all > permissions conferred by the configured CloudFormation execution policies**, @@ -1126,10 +1152,10 @@ The "new" bootstrap stack (obtained by running `cdk bootstrap` with `CDK_NEW_BOOTSTRAP=1`) is slightly more elaborate than the "old" stack. It contains: -* An S3 bucket and ECR repository with predictable names, so that we can reference +- An S3 bucket and ECR repository with predictable names, so that we can reference assets in these storage locations *without* the use of CloudFormation template parameters. -* A set of roles with permissions to access these asset locations and to execute +- A set of roles with permissions to access these asset locations and to execute CloudFormation, assumable from whatever accounts you specify under `--trust`. It is possible and safe to migrate from the old bootstrap stack to the new @@ -1209,15 +1235,15 @@ very nature the library cannot take care of everything. We therefore expect you to mind the following: -* Maintain dependency hygiene and vet 3rd-party software you use. Any software you +- Maintain dependency hygiene and vet 3rd-party software you use. Any software you run on your build machine has the ability to change the infrastructure that gets deployed. Be careful with the software you depend on. -* Use dependency locking to prevent accidental upgrades! The default `CdkSynths` that +- Use dependency locking to prevent accidental upgrades! The default `CdkSynths` that come with CDK Pipelines will expect `package-lock.json` and `yarn.lock` to ensure your dependencies are the ones you expect. -* Credentials to production environments should be short-lived. After +- Credentials to production environments should be short-lived. After bootstrapping and the initial pipeline provisioning, there is no more need for developers to have access to any of the account credentials; all further changes can be deployed through git. Avoid the chances of credentials leaking @@ -1292,7 +1318,7 @@ use CDK Pipelines to build pipelines backed by other deployment engines. Here is a list of CDK Libraries that integrate CDK Pipelines with alternative deployment engines: -* GitHub Workflows: [`cdk-pipelines-github`](https://github.com/cdklabs/cdk-pipelines-github) +- GitHub Workflows: [`cdk-pipelines-github`](https://github.com/cdklabs/cdk-pipelines-github) ## Troubleshooting @@ -1329,11 +1355,11 @@ If you see this error during the **Synth** step, it means that CodeBuild is expecting to find a `cdk.out` directory in the root of your CodeBuild project, but the directory wasn't there. There are two common causes for this: -* `cdk synth` is not being executed: `cdk synth` used to be run +- `cdk synth` is not being executed: `cdk synth` used to be run implicitly for you, but you now have to explicitly include the command. For NPM-based projects, add `npx cdk synth` to the end of the `commands` property, for other languages add `npm install -g aws-cdk` and `cdk synth`. -* Your CDK project lives in a subdirectory: you added a `cd ` command +- Your CDK project lives in a subdirectory: you added a `cd ` command to the list of commands; don't forget to tell the `ScriptStep` about the different location of `cdk.out`, by passing `primaryOutputDirectory: '/cdk.out'`. @@ -1426,9 +1452,9 @@ all, and commit a file called `cdk.context.json` with the right lookup values in If you do want to do lookups in the pipeline, the cause is one of the following: -* The target environment has not been bootstrapped; OR -* The target environment has been bootstrapped without the right `--trust` relationship; OR -* The CodeBuild execution role does not have permissions to call `sts:AssumeRole`. +- The target environment has not been bootstrapped; OR +- The target environment has been bootstrapped without the right `--trust` relationship; OR +- The CodeBuild execution role does not have permissions to call `sts:AssumeRole`. See the section called **Context Lookups** for more information on using this feature. @@ -1452,8 +1478,8 @@ following: An "S3 Access Denied" error can have two causes: -* Asset hashes have changed, but self-mutation has been disabled in the pipeline. -* You have deleted and recreated the bootstrap stack, or changed its qualifier. +- Asset hashes have changed, but self-mutation has been disabled in the pipeline. +- You have deleted and recreated the bootstrap stack, or changed its qualifier. #### Self-mutation step has been removed @@ -1495,7 +1521,7 @@ The most automated way to solve the issue is to introduce a secondary bootstrap that the pipeline stack looks for, a change will be detected and the impacted policies and resources will be updated. A hypothetical recovery workflow would look something like this: -* First, for all impacted environments, create a secondary bootstrap stack: +- First, for all impacted environments, create a secondary bootstrap stack: ```sh $ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \ @@ -1504,7 +1530,7 @@ $ env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \ aws://111111111111/us-east-1 ``` -* Update all impacted stacks in the pipeline to use this new qualifier. +- Update all impacted stacks in the pipeline to use this new qualifier. See https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html for more info. ```ts @@ -1516,11 +1542,11 @@ new Stack(this, 'MyStack', { }); ``` -* Deploy the updated stacks. This will update the stacks to use the roles created in the new bootstrap stack. -* (Optional) Restore back to the original state: - * Revert the change made in step #2 above - * Re-deploy the pipeline to use the original qualifier. - * Delete the temporary bootstrap stack(s) +- Deploy the updated stacks. This will update the stacks to use the roles created in the new bootstrap stack. +- (Optional) Restore back to the original state: + - Revert the change made in step #2 above + - Re-deploy the pipeline to use the original qualifier. + - Delete the temporary bootstrap stack(s) ##### Manual Alternative @@ -1655,14 +1681,14 @@ const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { There are some usability issues that are caused by underlying technology, and cannot be remedied by CDK at this point. They are reproduced here for completeness. -* **Console links to other accounts will not work**: the AWS CodePipeline +- **Console links to other accounts will not work**: the AWS CodePipeline console will assume all links are relative to the current account. You will not be able to use the pipeline console to click through to a CloudFormation stack in a different account. -* **If a change set failed to apply the pipeline must restarted**: if a change +- **If a change set failed to apply the pipeline must restarted**: if a change set failed to apply, it cannot be retried. The pipeline must be restarted from the top by clicking **Release Change**. -* **A stack that failed to create must be deleted manually**: if a stack +- **A stack that failed to create must be deleted manually**: if a stack failed to create on the first attempt, you must delete it using the CloudFormation console before starting the pipeline again by clicking **Release Change**. diff --git a/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts b/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts index 651ccb3b46d53..0761f03b6a01b 100644 --- a/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts +++ b/packages/@aws-cdk/pipelines/lib/blueprint/stage-deployment.ts @@ -117,6 +117,12 @@ export class StageDeployment { */ public readonly stackSteps: StackSteps[]; + /** + * Determine if all stacks in stage should be deployed with prepare + * step or not. + */ + public readonly prepareStep?: boolean; + private constructor( /** The stacks deployed in this stage */ public readonly stacks: StackDeployment[], props: StageDeploymentProps = {}) { diff --git a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts index 8d06e9bb1fa80..8bc6165ea1077 100644 --- a/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts +++ b/packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts @@ -214,6 +214,16 @@ export interface CodePipelineProps { * @default - A new role is created */ readonly role?: iam.IRole; + + /** + * Deploy every stack by creating a change set and executing it + * + * When enabled, creates a "Prepare" and "Execute" action for each stack. Disable + * to deploy the stack in one pipeline action. + * + * @default true + */ + readonly useChangeSets?: boolean; } /** @@ -299,6 +309,7 @@ export class CodePipeline extends PipelineBase { private artifacts = new ArtifactMap(); private _synthProject?: cb.IProject; private readonly selfMutation: boolean; + private readonly useChangeSets: boolean; private _myCxAsmRoot?: string; private readonly dockerCredentials: DockerCredential[]; private readonly cachedFnSub = new CachedFnSub(); @@ -325,6 +336,7 @@ export class CodePipeline extends PipelineBase { this.dockerCredentials = props.dockerCredentials ?? []; this.singlePublisherPerAssetType = !(props.publishAssetsInParallel ?? true); this.cliVersion = props.cliVersion ?? preferredCliVersion(); + this.useChangeSets = props.useChangeSets ?? true; } /** @@ -389,6 +401,7 @@ export class CodePipeline extends PipelineBase { const graphFromBp = new PipelineGraph(this, { selfMutation: this.selfMutation, singlePublisherPerAssetType: this.singlePublisherPerAssetType, + prepareStep: this.useChangeSets, }); this._cloudAssemblyFileSet = graphFromBp.cloudAssemblyFileSet; @@ -519,10 +532,15 @@ export class CodePipeline extends PipelineBase { return this.createChangeSetAction(node.data.stack); case 'execute': - return this.executeChangeSetAction(node.data.stack, node.data.captureOutputs); + return node.data.withoutChangeSet + ? this.executeDeploymentAction(node.data.stack, node.data.captureOutputs) + : this.executeChangeSetAction(node.data.stack, node.data.captureOutputs); case 'step': return this.actionFromStep(node, node.data.step); + + default: + throw new Error(`CodePipeline does not support graph nodes of type '${node.data?.type}'. You are probably using a feature this CDK Pipelines implementation does not support.`); } } @@ -630,6 +648,38 @@ export class CodePipeline extends PipelineBase { }; } + private executeDeploymentAction(stack: StackDeployment, captureOutputs: boolean): ICodePipelineActionFactory { + const templateArtifact = this.artifacts.toCodePipeline(this._cloudAssemblyFileSet!); + const templateConfigurationPath = this.writeTemplateConfiguration(stack); + + const region = stack.region !== Stack.of(this).region ? stack.region : undefined; + const account = stack.account !== Stack.of(this).account ? stack.account : undefined; + + const relativeTemplatePath = path.relative(this.myCxAsmRoot, stack.absoluteTemplatePath); + + return { + produceAction: (stage, options) => { + stage.addAction(new cpa.CloudFormationCreateUpdateStackAction({ + actionName: options.actionName, + runOrder: options.runOrder, + stackName: stack.stackName, + templatePath: templateArtifact.atPath(toPosixPath(relativeTemplatePath)), + adminPermissions: true, + role: this.roleFromPlaceholderArn(this.pipeline, region, account, stack.assumeRoleArn), + deploymentRole: this.roleFromPlaceholderArn(this.pipeline, region, account, stack.executionRoleArn), + region: region, + templateConfiguration: templateConfigurationPath + ? templateArtifact.atPath(toPosixPath(templateConfigurationPath)) + : undefined, + cfnCapabilities: [CfnCapabilities.NAMED_IAM, CfnCapabilities.AUTO_EXPAND], + variablesNamespace: captureOutputs ? stackVariableNamespace(stack) : undefined, + })); + + return { runOrdersConsumed: 1 }; + }, + }; + } + private selfMutateAction(): ICodePipelineActionFactory { const installSuffix = this.cliVersion ? `@${this.cliVersion}` : ''; diff --git a/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts b/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts index e26058b724b62..22692f509581a 100644 --- a/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts +++ b/packages/@aws-cdk/pipelines/lib/helpers-internal/pipeline-graph.ts @@ -88,7 +88,7 @@ export class PipelineGraph { if (props.selfMutation) { const stage: AGraph = Graph.of('UpdatePipeline', { type: 'group' }); this.graph.add(stage); - this.selfMutateNode = GraphNode.of('SelfMutate', { type: 'self-update' }); + this.selfMutateNode = aGraphNode('SelfMutate', { type: 'self-update' }); stage.add(this.selfMutateNode); this.selfMutateNode.dependOn(this.synthNode); @@ -134,11 +134,12 @@ export class PipelineGraph { for (const stack of stage.stacks) { const stackGraph: AGraph = Graph.of(this.simpleStackName(stack.stackName, stage.stageName), { type: 'stack-group', stack }); - const prepareNode: AGraphNode | undefined = this.prepareStep ? GraphNode.of('Prepare', { type: 'prepare', stack }) : undefined; - const deployNode: AGraphNode = GraphNode.of('Deploy', { + const prepareNode: AGraphNode | undefined = this.prepareStep ? aGraphNode('Prepare', { type: 'prepare', stack }) : undefined; + const deployNode: AGraphNode = aGraphNode('Deploy', { type: 'execute', stack, captureOutputs: this.queries.stackOutputsReferenced(stack).length > 0, + withoutChangeSet: prepareNode === undefined, }); retGraph.add(stackGraph); @@ -157,9 +158,9 @@ export class PipelineGraph { // add changeset steps at the stack level if (stack.changeSet.length > 0) { if (prepareNode) { - this.addChangeSet(stack.changeSet, prepareNode, deployNode, stackGraph); + this.addChangeSetNode(stack.changeSet, prepareNode, deployNode, stackGraph); } else { - throw new Error('Your pipeline engine does not support changeSet steps'); + throw new Error(`Cannot use \'changeSet\' steps for stack \'${stack.stackName}\': the pipeline does not support them or they have been disabled`); } } @@ -217,7 +218,7 @@ export class PipelineGraph { return retGraph; } - private addChangeSet(changeSet: Step[], prepareNode: AGraphNode, deployNode: AGraphNode, graph: AGraph) { + private addChangeSetNode(changeSet: Step[], prepareNode: AGraphNode, deployNode: AGraphNode, graph: AGraph) { for (const c of changeSet) { const changeSetNode = this.addAndRecurse(c, graph); changeSetNode?.dependOn(prepareNode); @@ -255,7 +256,7 @@ export class PipelineGraph { const previous = this.added.get(step); if (previous) { return previous; } - const node: AGraphNode = GraphNode.of(step.id, { type: 'step', step }); + const node: AGraphNode = aGraphNode(step.id, { type: 'step', step }); // If the step is a source step, change the parent to a special "Source" stage // (CodePipeline wants it that way) @@ -299,7 +300,7 @@ export class PipelineGraph { ? (this.singlePublisher ? 'FileAsset' : `FileAsset${++this._fileAssetCtr}`) : (this.singlePublisher ? 'DockerAsset' : `DockerAsset${++this._dockerAssetCtr}`); - assetNode = GraphNode.of(id, { type: 'publish-assets', assets: [] }); + assetNode = aGraphNode(id, { type: 'publish-assets', assets: [] }); assetsGraph.add(assetNode); assetNode.dependOn(this.lastPreparationNode); @@ -311,6 +312,7 @@ export class PipelineGraph { if (data?.type !== 'publish-assets') { throw new Error(`${assetNode} has the wrong data.type: ${data?.type}`); } + if (!data.assets.some(a => a.assetSelector === stackAsset.assetSelector)) { data.assets.push(stackAsset); } @@ -327,20 +329,48 @@ export class PipelineGraph { } type GraphAnnotation = - { readonly type: 'group' } + | { readonly type: 'group' } | { readonly type: 'stack-group'; readonly stack: StackDeployment } | { readonly type: 'publish-assets'; readonly assets: StackAsset[] } | { readonly type: 'step'; readonly step: Step; isBuildStep?: boolean } | { readonly type: 'self-update' } | { readonly type: 'prepare'; readonly stack: StackDeployment } - | { readonly type: 'execute'; readonly stack: StackDeployment; readonly captureOutputs: boolean } + | ExecuteAnnotation + // Explicitly disable exhaustiveness checking on GraphAnnotation. This forces all consumers to adding + // a 'default' clause which allows us to extend this list in the future. + // The code below looks weird, 'type' must be a non-enumerable type that is not assignable to 'string'. + | { readonly type: { error: 'you must add a default case to your switch' } } ; +interface ExecuteAnnotation { + readonly type: 'execute'; + /** + * The stack to deploy + */ + readonly stack: StackDeployment; + + /** + * Whether or not outputs should be captured + */ + readonly captureOutputs: boolean; + + /** + * If this is executing a change set, or should do a direct deployment + * + * @default false + */ + readonly withoutChangeSet?: boolean; +} + // Type aliases for the graph nodes tagged with our specific annotation type // (to save on generics in the code above). export type AGraphNode = GraphNode; export type AGraph = Graph; +function aGraphNode(id: string, x: GraphAnnotation): AGraphNode { + return GraphNode.of(id, x); +} + function stripPrefix(s: string, prefix: string) { return s.startsWith(prefix) ? s.slice(prefix.length) : s; } \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts b/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts index 69391bf8b0594..747dcfcb91d77 100644 --- a/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts +++ b/packages/@aws-cdk/pipelines/test/blueprint/helpers-internal/pipeline-graph.test.ts @@ -288,7 +288,7 @@ describe('options for other engines', () => { // THEN expect(() => new PipelineGraph(blueprint, { prepareStep: false, - })).toThrow('Your pipeline engine does not support changeSet steps'); + })).toThrow(/Cannot use 'changeSet' steps/); }); }); diff --git a/packages/@aws-cdk/pipelines/test/cdk-integ.out.pipeline-with-asset-variables/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts b/packages/@aws-cdk/pipelines/test/cdk-integ.out.pipeline-with-asset-variables/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts new file mode 100644 index 0000000000000..2459d44ab1d18 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/cdk-integ.out.pipeline-with-asset-variables/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts @@ -0,0 +1,82 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { S3 } from 'aws-sdk'; + +const AUTO_DELETE_OBJECTS_TAG = 'aws-cdk:auto-delete-objects'; + +const s3 = new S3(); + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + switch (event.RequestType) { + case 'Create': + return; + case 'Update': + return onUpdate(event); + case 'Delete': + return onDelete(event.ResourceProperties?.BucketName); + } +} + +async function onUpdate(event: AWSLambda.CloudFormationCustomResourceEvent) { + const updateEvent = event as AWSLambda.CloudFormationCustomResourceUpdateEvent; + const oldBucketName = updateEvent.OldResourceProperties?.BucketName; + const newBucketName = updateEvent.ResourceProperties?.BucketName; + const bucketNameHasChanged = newBucketName != null && oldBucketName != null && newBucketName !== oldBucketName; + + /* If the name of the bucket has changed, CloudFormation will try to delete the bucket + and create a new one with the new name. So we have to delete the contents of the + bucket so that this operation does not fail. */ + if (bucketNameHasChanged) { + return onDelete(oldBucketName); + } +} + +/** + * Recursively delete all items in the bucket + * + * @param bucketName the bucket name + */ +async function emptyBucket(bucketName: string) { + const listedObjects = await s3.listObjectVersions({ Bucket: bucketName }).promise(); + const contents = [...listedObjects.Versions ?? [], ...listedObjects.DeleteMarkers ?? []]; + if (contents.length === 0) { + return; + } + + const records = contents.map((record: any) => ({ Key: record.Key, VersionId: record.VersionId })); + await s3.deleteObjects({ Bucket: bucketName, Delete: { Objects: records } }).promise(); + + if (listedObjects?.IsTruncated) { + await emptyBucket(bucketName); + } +} + +async function onDelete(bucketName?: string) { + if (!bucketName) { + throw new Error('No BucketName was provided.'); + } + if (!await isBucketTaggedForDeletion(bucketName)) { + process.stdout.write(`Bucket does not have '${AUTO_DELETE_OBJECTS_TAG}' tag, skipping cleaning.\n`); + return; + } + try { + await emptyBucket(bucketName); + } catch (e) { + if (e.code !== 'NoSuchBucket') { + throw e; + } + // Bucket doesn't exist. Ignoring + } +} + +/** + * The bucket will only be tagged for deletion if it's being deleted in the same + * deployment as this Custom Resource. + * + * If the Custom Resource is every deleted before the bucket, it must be because + * `autoDeleteObjects` has been switched to false, in which case the tag would have + * been removed before we get to this Delete event. + */ +async function isBucketTaggedForDeletion(bucketName: string) { + const response = await s3.getBucketTagging({ Bucket: bucketName }).promise(); + return response.TagSet.some(tag => tag.Key === AUTO_DELETE_OBJECTS_TAG && tag.Value === 'true'); +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts index 5cc67f6debb7c..b0ecae2286d1a 100644 --- a/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts +++ b/packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts @@ -237,6 +237,67 @@ test('CodePipeline supports use of existing role', () => { }); }); +describe('deployment of stack', () => { + test('is done with Prepare and Deploy step by default', () => { + const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); + const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', { + crossAccountKeys: true, + }); + pipeline.addStage(new FileAssetApp(pipelineStack, 'App', {})); + + // THEN + const template = Template.fromStack(pipelineStack); + + // There should be Prepare step in piepline + template.hasResourceProperties('AWS::CodePipeline::Pipeline', { + Stages: Match.arrayWith([{ + Actions: Match.arrayWith([ + Match.objectLike({ + Configuration: Match.objectLike({ + ActionMode: 'CHANGE_SET_REPLACE', + }), + Name: 'Prepare', + }), + Match.objectLike({ + Configuration: Match.objectLike({ + ActionMode: 'CHANGE_SET_EXECUTE', + }), + Name: 'Deploy', + }), + ]), + Name: 'App', + }]), + }); + }); + + test('can be done with single step', () => { + const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV }); + const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', { + crossAccountKeys: true, + useChangeSets: false, + }); + pipeline.addStage(new FileAssetApp(pipelineStack, 'App', {})); + + // THEN + const template = Template.fromStack(pipelineStack); + + // There should be Prepare step in piepline + template.hasResourceProperties('AWS::CodePipeline::Pipeline', { + Stages: Match.arrayWith([{ + Actions: Match.arrayWith([ + Match.objectLike({ + Configuration: Match.objectLike({ + ActionMode: 'CREATE_UPDATE', + }), + Name: 'Deploy', + }), + ]), + Name: 'App', + }]), + }); + }); +}); + interface ReuseCodePipelineStackProps extends cdk.StackProps { reuseCrossRegionSupportStacks?: boolean; } diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-without-prepare.ts b/packages/@aws-cdk/pipelines/test/integ.pipeline-without-prepare.ts new file mode 100644 index 0000000000000..a48438ad2cbec --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-without-prepare.ts @@ -0,0 +1,59 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +/// !cdk-integ VarablePipelineStack pragma:set-context:@aws-cdk/core:newStyleStackSynthesis=true +import * as s3 from '@aws-cdk/aws-s3'; +import { App, Stack, StackProps, RemovalPolicy, Stage, StageProps, DefaultStackSynthesizer } from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as pipelines from '../lib'; +import { PlainStackApp } from './testhelpers'; + +class MyStage extends Stage { + constructor(scope: Construct, id: string, props?: StageProps) { + super(scope, id, props); + + const stack = new Stack(this, 'Stack', { + ...props, + synthesizer: new DefaultStackSynthesizer(), + }); + + new PlainStackApp(stack, 'MyApp'); + } +} + +class PipelineStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const sourceBucket = new s3.Bucket(this, 'SourceBucket', { + removalPolicy: RemovalPolicy.DESTROY, + autoDeleteObjects: true, + }); + const pipeline = new pipelines.CodePipeline(this, 'Pipeline', { + synth: new pipelines.ShellStep('Synth', { + input: pipelines.CodePipelineSource.s3(sourceBucket, 'key'), + // input: pipelines.CodePipelineSource.gitHub('cdklabs/construct-hub-probe', 'main', { + // trigger: GitHubTrigger.POLL, + // }), + commands: ['mkdir cdk.out', 'touch cdk.out/dummy'], + }), + selfMutation: false, + useChangeSets: false, + }); + + pipeline.addStage(new MyStage(this, 'MyStage', {})); + } +} + +const app = new App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': '1', + }, +}); + +const stack = new PipelineStack(app, 'PreparelessPipelineStack'); + +new integ.IntegTest(app, 'PreparelessPipelineTest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json index 0799a1aa58659..ad85c4f0e29b6 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/PipelineStack.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { "339869b7efdb6a771fce40473ae99ce3e3174959597875b90007c83a87221f57": { "source": { diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json index 09d77bb1e7823..4b182342ba90f 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/PipelineStackBetaStack1E6541489.assets.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "files": { "8289faf53c7da377bb2b90615999171adef5e1d8f6b88810e5fef75e6ca09ba5": { "source": { diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json index 14ab527bbc9bc..6dad8563dba5a 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/assembly-PipelineStack-Beta/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "PipelineStackBetaStack1E6541489.assets": { "type": "cdk:asset-manifest", diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out index 588d7b269d34f..8ecc185e9dbee 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json index 66f5b01318d4f..00c9e8da3dbec 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "testCases": { "integ.newpipeline-with-vpc": { "stacks": [ diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json index 81748cc96388c..f265f1c8dae5d 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "21.0.0", "artifacts": { "assembly-PipelineStack-Beta": { "type": "cdk:cloud-assembly", diff --git a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json index 8ce20f7a8d7dc..6683466c9909d 100644 --- a/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json +++ b/packages/@aws-cdk/pipelines/test/newpipeline-with-vpc.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "PipelineStack": { @@ -1298,13 +1298,13 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Build": { @@ -1742,13 +1742,13 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "UpdatePipeline": { @@ -1760,13 +1760,13 @@ "path": "PipelineStack/Pipeline/Pipeline/UpdatePipeline/SelfMutate", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Assets": { @@ -1778,7 +1778,7 @@ "path": "PipelineStack/Pipeline/Pipeline/Assets/FileAsset1", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "FileAsset2": { @@ -1786,13 +1786,13 @@ "path": "PipelineStack/Pipeline/Pipeline/Assets/FileAsset2", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Beta": { @@ -1804,7 +1804,7 @@ "path": "PipelineStack/Pipeline/Pipeline/Beta/Prepare", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Deploy": { @@ -1812,13 +1812,13 @@ "path": "PipelineStack/Pipeline/Pipeline/Beta/Deploy", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}": { @@ -1838,7 +1838,7 @@ "path": "PipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}/8389e75f-0810-4838-bf64-d6f85a95cf83", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, @@ -2449,7 +2449,7 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } }, "Assets": { @@ -2978,7 +2978,7 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.78" + "version": "10.1.92" } } }, diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.assets.json new file mode 100644 index 0000000000000..0e07fad5fe474 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.assets.json @@ -0,0 +1,32 @@ +{ + "version": "21.0.0", + "files": { + "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": { + "source": { + "path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "d48998cffedf7ada707ce9f74130f229eb47e4c549355b1031589d0c4d78d9ad": { + "source": { + "path": "PreparelessPipelineStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d48998cffedf7ada707ce9f74130f229eb47e4c549355b1031589d0c4d78d9ad.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.template.json new file mode 100644 index 0000000000000..66c500812ee7a --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineStack.template.json @@ -0,0 +1,950 @@ +{ + "Resources": { + "SourceBucketDDD2130A": { + "Type": "AWS::S3::Bucket", + "Properties": { + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true" + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SourceBucketPolicy703DFBF9": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "SourceBucketAutoDeleteObjectsCustomResourceC68FC040": { + "Type": "Custom::S3AutoDeleteObjects", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn" + ] + }, + "BucketName": { + "Ref": "SourceBucketDDD2130A" + } + }, + "DependsOn": [ + "SourceBucketPolicy703DFBF9" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ] + } + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + }, + "Runtime": "nodejs14.x", + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "SourceBucketDDD2130A" + }, + " S3 bucket." + ] + ] + } + }, + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + ] + }, + "PipelineArtifactsBucketAEA9A052": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "aws:kms" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "PipelineArtifactsBucketPolicyF53CCC52": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleB27FAA37": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineRoleDefaultPolicy7BDC1ABB": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineRoleDefaultPolicy7BDC1ABB", + "Roles": [ + { + "Ref": "PipelineRoleB27FAA37" + } + ] + } + }, + "Pipeline9850B417": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "PipelineRoleB27FAA37", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "AWS", + "Provider": "S3", + "Version": "1" + }, + "Configuration": { + "S3Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "S3ObjectKey": "key" + }, + "Name": { + "Ref": "SourceBucketDDD2130A" + }, + "OutputArtifacts": [ + { + "Name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Build", + "Owner": "AWS", + "Provider": "CodeBuild", + "Version": "1" + }, + "Configuration": { + "ProjectName": { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + }, + "InputArtifacts": [ + { + "Name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "Name": "Synth", + "OutputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "RoleArn": { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "Build" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "CloudFormation", + "Version": "1" + }, + "Configuration": { + "StackName": "MyStage-Stack", + "Capabilities": "CAPABILITY_NAMED_IAM,CAPABILITY_AUTO_EXPAND", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-cfn-exec-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "ActionMode": "CREATE_UPDATE", + "TemplatePath": "Synth_Output::assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json" + }, + "InputArtifacts": [ + { + "Name": "Synth_Output" + } + ], + "Name": "Deploy", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "RunOrder": 1 + } + ], + "Name": "MyStage" + } + ], + "ArtifactStore": { + "Location": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "Type": "S3" + }, + "RestartExecutionOnUpdate": true + }, + "DependsOn": [ + "PipelineRoleDefaultPolicy7BDC1ABB", + "PipelineRoleB27FAA37" + ] + }, + "PipelineSourceS3CodePipelineActionRole83895A58": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/key" + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F", + "Roles": [ + { + "Ref": "PipelineSourceS3CodePipelineActionRole83895A58" + } + ] + } + }, + "PipelineBuildSynthCdkBuildProjectRole231EEA2A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C", + "Roles": [ + { + "Ref": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ] + } + }, + "PipelineBuildSynthCdkBuildProject6BEFA8E6": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "CODEPIPELINE" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "aws/codebuild/standard:5.0", + "ImagePullCredentialsType": "CODEBUILD", + "PrivilegedMode": false, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProjectRole231EEA2A", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"build\": {\n \"commands\": [\n \"mkdir cdk.out\",\n \"touch cdk.out/dummy\"\n ]\n }\n },\n \"artifacts\": {\n \"base-directory\": \"cdk.out\",\n \"files\": \"**/*\"\n }\n}", + "Type": "CODEPIPELINE" + }, + "Cache": { + "Type": "NO_CACHE" + }, + "Description": "Pipeline step PreparelessPipelineStack/Pipeline/Build/Synth", + "EncryptionKey": "alias/aws/s3" + } + }, + "PipelineCodeBuildActionRole226DB0CB": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Condition": { + "Bool": { + "aws:ViaAWSService": "codepipeline.amazonaws.com" + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProject6BEFA8E6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE", + "Roles": [ + { + "Ref": "PipelineCodeBuildActionRole226DB0CB" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json new file mode 100644 index 0000000000000..770de88c374a0 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.assets.json new file mode 100644 index 0000000000000..639913e637b14 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "PreparelessPipelineStackMyStageStack3DC192E7.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json new file mode 100644 index 0000000000000..2be2327136882 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "1fd92c49dfa8050744f5e5169a7c3739923777fad154ba6b7275118191534ffc": { + "source": { + "path": "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "1fd92c49dfa8050744f5e5169a7c3739923777fad154ba6b7275118191534ffc.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json new file mode 100644 index 0000000000000..fa27c2ed77a28 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json @@ -0,0 +1,43 @@ +{ + "Resources": { + "Bucket83908E77": { + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/cdk.out b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/manifest.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/manifest.json new file mode 100644 index 0000000000000..f7bf8d40a960f --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/assembly-PreparelessPipelineStack-MyStage-Stack-MyApp/manifest.json @@ -0,0 +1,59 @@ +{ + "version": "21.0.0", + "artifacts": { + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.template.json", + "validateOnSynth": true, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/1fd92c49dfa8050744f5e5169a7c3739923777fad154ba6b7275118191534ffc.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + }, + "stackName": "MyStage-MyApp-Stack" + }, + "dependencies": [ + "PreparelessPipelineStackMyStageStackMyAppStack51FBCD39.assets" + ], + "metadata": { + "/PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/Bucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Bucket83908E77" + } + ], + "/PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/cdk.out b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/manifest.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/manifest.json new file mode 100644 index 0000000000000..6887ae7fd2585 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/assembly-PreparelessPipelineStack-MyStage/manifest.json @@ -0,0 +1,60 @@ +{ + "version": "21.0.0", + "artifacts": { + "assembly-PreparelessPipelineStack-MyStage-Stack-MyApp": { + "type": "cdk:cloud-assembly", + "properties": { + "directoryName": "assembly-PreparelessPipelineStack-MyStage-Stack-MyApp", + "displayName": "PreparelessPipelineStack/MyStage/Stack/MyApp" + } + }, + "PreparelessPipelineStackMyStageStack3DC192E7.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineStackMyStageStack3DC192E7.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineStackMyStageStack3DC192E7": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineStackMyStageStack3DC192E7.template.json", + "validateOnSynth": true, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineStackMyStageStack3DC192E7.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + }, + "stackName": "MyStage-Stack" + }, + "dependencies": [ + "PreparelessPipelineStackMyStageStack3DC192E7.assets" + ], + "metadata": { + "/PreparelessPipelineStack/MyStage/Stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineStack/MyStage/Stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineStack/MyStage/Stack" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/__entrypoint__.js b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/__entrypoint__.js new file mode 100644 index 0000000000000..9df94382cc74e --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/__entrypoint__.js @@ -0,0 +1,118 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + exports.external.log('submit response to cloudformation', json); + const responseBody = JSON.stringify(json); + const parsedUrl = url.parse(event.ResponseURL); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { 'content-type': '', 'content-length': responseBody.length }, + }; + await exports.external.sendHttpRequest(req, responseBody); +} +async function defaultSendHttpRequest(options, responseBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, _ => resolve()); + request.on('error', reject); + request.write(responseBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZWpzLWVudHJ5cG9pbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJub2RlanMtZW50cnlwb2ludC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQkFBK0I7QUFDL0IsMkJBQTJCO0FBRTNCLGlCQUFpQjtBQUNKLFFBQUEsUUFBUSxHQUFHO0lBQ3RCLGVBQWUsRUFBRSxzQkFBc0I7SUFDdkMsR0FBRyxFQUFFLFVBQVU7SUFDZixrQkFBa0IsRUFBRSxJQUFJO0lBQ3hCLGdCQUFnQixFQUFFLFNBQVM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sZ0NBQWdDLEdBQUcsd0RBQXdELENBQUM7QUFDbEcsTUFBTSwwQkFBMEIsR0FBRyw4REFBOEQsQ0FBQztBQVczRixLQUFLLFVBQVUsT0FBTyxDQUFDLEtBQWtELEVBQUUsT0FBMEI7SUFDMUcsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLEtBQUssRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDeEQsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFM0QsdUVBQXVFO0lBQ3ZFLHVFQUF1RTtJQUN2RSxhQUFhO0lBQ2IsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsa0JBQWtCLEtBQUssZ0NBQWdDLEVBQUU7UUFDbkcsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztRQUN0RSxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkMsT0FBTztLQUNSO0lBRUQsSUFBSTtRQUNGLHlFQUF5RTtRQUN6RSxpRUFBaUU7UUFDakUsd0NBQXdDO1FBQ3hDLGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBWSxPQUFPLENBQUMsZ0JBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxNQUFNLE1BQU0sR0FBRyxNQUFNLFdBQVcsQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFMUQsdURBQXVEO1FBQ3ZELE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFFcEQsMkJBQTJCO1FBQzNCLE1BQU0sY0FBYyxDQUFDLFNBQVMsRUFBRSxhQUFhLENBQUMsQ0FBQztLQUNoRDtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsTUFBTSxJQUFJLEdBQWE7WUFDckIsR0FBRyxLQUFLO1lBQ1IsTUFBTSxFQUFFLGdCQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPO1NBQzFELENBQUM7UUFFRixJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFO1lBQzVCLHlFQUF5RTtZQUN6RSxtRUFBbUU7WUFDbkUsd0VBQXdFO1lBQ3hFLHFFQUFxRTtZQUNyRSxnQ0FBZ0M7WUFDaEMsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsRUFBRTtnQkFDbEMsZ0JBQVEsQ0FBQyxHQUFHLENBQUMsNEdBQTRHLENBQUMsQ0FBQztnQkFDM0gsSUFBSSxDQUFDLGtCQUFrQixHQUFHLGdDQUFnQyxDQUFDO2FBQzVEO2lCQUFNO2dCQUNMLGtFQUFrRTtnQkFDbEUsNkRBQTZEO2dCQUM3RCxnQkFBUSxDQUFDLEdBQUcsQ0FBQyw2REFBNkQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDcEc7U0FDRjtRQUVELG1FQUFtRTtRQUNuRSxNQUFNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7S0FDdEM7QUFDSCxDQUFDO0FBbkRELDBCQW1EQztBQUVELFNBQVMsY0FBYyxDQUNyQixVQUF5RixFQUN6RixrQkFBMEMsRUFBRztJQUU3QyxzRUFBc0U7SUFDdEUsdUJBQXVCO0lBQ3ZCLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDLGtCQUFrQixJQUFJLFVBQVUsQ0FBQyxrQkFBa0IsSUFBSSxVQUFVLENBQUMsU0FBUyxDQUFDO0lBRXZILGtFQUFrRTtJQUNsRSxJQUFJLFVBQVUsQ0FBQyxXQUFXLEtBQUssUUFBUSxJQUFJLGtCQUFrQixLQUFLLFVBQVUsQ0FBQyxrQkFBa0IsRUFBRTtRQUMvRixNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxVQUFVLENBQUMsa0JBQWtCLFNBQVMsZUFBZSxDQUFDLGtCQUFrQixtQkFBbUIsQ0FBQyxDQUFDO0tBQ3RLO0lBRUQsMERBQTBEO0lBQzFELE9BQU87UUFDTCxHQUFHLFVBQVU7UUFDYixHQUFHLGVBQWU7UUFDbEIsa0JBQWtCLEVBQUUsa0JBQWtCO0tBQ3ZDLENBQUM7QUFDSixDQUFDO0FBRUQsS0FBSyxVQUFVLGNBQWMsQ0FBQyxNQUE0QixFQUFFLEtBQWU7SUFDekUsTUFBTSxJQUFJLEdBQW1EO1FBQzNELE1BQU0sRUFBRSxNQUFNO1FBQ2QsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLElBQUksTUFBTTtRQUM5QixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87UUFDdEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO1FBQzFCLGtCQUFrQixFQUFFLEtBQUssQ0FBQyxrQkFBa0IsSUFBSSwwQkFBMEI7UUFDMUUsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLGlCQUFpQjtRQUMxQyxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07UUFDcEIsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO0tBQ2pCLENBQUM7SUFFRixnQkFBUSxDQUFDLEdBQUcsQ0FBQyxtQ0FBbUMsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUV4RCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFDLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQy9DLE1BQU0sR0FBRyxHQUFHO1FBQ1YsUUFBUSxFQUFFLFNBQVMsQ0FBQyxRQUFRO1FBQzVCLElBQUksRUFBRSxTQUFTLENBQUMsSUFBSTtRQUNwQixNQUFNLEVBQUUsS0FBSztRQUNiLE9BQU8sRUFBRSxFQUFFLGNBQWMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsWUFBWSxDQUFDLE1BQU0sRUFBRTtLQUN2RSxDQUFDO0lBRUYsTUFBTSxnQkFBUSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVELEtBQUssVUFBVSxzQkFBc0IsQ0FBQyxPQUE2QixFQUFFLFlBQW9CO0lBQ3ZGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSTtZQUNGLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzVCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztTQUNmO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7U0FDWDtJQUNILENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEdBQVcsRUFBRSxHQUFHLE1BQWE7SUFDL0Msc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xuXG4vLyBmb3IgdW5pdCB0ZXN0c1xuZXhwb3J0IGNvbnN0IGV4dGVybmFsID0ge1xuICBzZW5kSHR0cFJlcXVlc3Q6IGRlZmF1bHRTZW5kSHR0cFJlcXVlc3QsXG4gIGxvZzogZGVmYXVsdExvZyxcbiAgaW5jbHVkZVN0YWNrVHJhY2VzOiB0cnVlLFxuICB1c2VySGFuZGxlckluZGV4OiAnLi9pbmRleCcsXG59O1xuXG5jb25zdCBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUiA9ICdBV1NDREs6OkN1c3RvbVJlc291cmNlUHJvdmlkZXJGcmFtZXdvcms6OkNSRUFURV9GQUlMRUQnO1xuY29uc3QgTUlTU0lOR19QSFlTSUNBTF9JRF9NQVJLRVIgPSAnQVdTQ0RLOjpDdXN0b21SZXNvdXJjZVByb3ZpZGVyRnJhbWV3b3JrOjpNSVNTSU5HX1BIWVNJQ0FMX0lEJztcblxuZXhwb3J0IHR5cGUgUmVzcG9uc2UgPSBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50ICYgSGFuZGxlclJlc3BvbnNlO1xuZXhwb3J0IHR5cGUgSGFuZGxlciA9IChldmVudDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCwgY29udGV4dDogQVdTTGFtYmRhLkNvbnRleHQpID0+IFByb21pc2U8SGFuZGxlclJlc3BvbnNlIHwgdm9pZD47XG5leHBvcnQgdHlwZSBIYW5kbGVyUmVzcG9uc2UgPSB1bmRlZmluZWQgfCB7XG4gIERhdGE/OiBhbnk7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZztcbiAgUmVhc29uPzogc3RyaW5nO1xuICBOb0VjaG8/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIGNvbnN0IHNhbml0aXplZEV2ZW50ID0geyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH07XG4gIGV4dGVybmFsLmxvZyhKU09OLnN0cmluZ2lmeShzYW5pdGl6ZWRFdmVudCwgdW5kZWZpbmVkLCAyKSk7XG5cbiAgLy8gaWdub3JlIERFTEVURSBldmVudCB3aGVuIHRoZSBwaHlzaWNhbCByZXNvdXJjZSBJRCBpcyB0aGUgbWFya2VyIHRoYXRcbiAgLy8gaW5kaWNhdGVzIHRoYXQgdGhpcyBERUxFVEUgaXMgYSBzdWJzZXF1ZW50IERFTEVURSB0byBhIGZhaWxlZCBDUkVBVEVcbiAgLy8gb3BlcmF0aW9uLlxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnICYmIGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCA9PT0gQ1JFQVRFX0ZBSUxFRF9QSFlTSUNBTF9JRF9NQVJLRVIpIHtcbiAgICBleHRlcm5hbC5sb2coJ2lnbm9yaW5nIERFTEVURSBldmVudCBjYXVzZWQgYnkgYSBmYWlsZWQgQ1JFQVRFIGV2ZW50Jyk7XG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCBldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBpbnZva2UgdGhlIHVzZXIgaGFuZGxlci4gdGhpcyBpcyBpbnRlbnRpb25hbGx5IGluc2lkZSB0aGUgdHJ5LWNhdGNoIHRvXG4gICAgLy8gZW5zdXJlIHRoYXQgaWYgdGhlcmUgaXMgYW4gZXJyb3IgaXQncyByZXBvcnRlZCBhcyBhIGZhaWx1cmUgdG9cbiAgICAvLyBjbG91ZGZvcm1hdGlvbiAob3RoZXJ3aXNlIGNmbiB3YWl0cykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCB1c2VySGFuZGxlcjogSGFuZGxlciA9IHJlcXVpcmUoZXh0ZXJuYWwudXNlckhhbmRsZXJJbmRleCkuaGFuZGxlcjtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB1c2VySGFuZGxlcihzYW5pdGl6ZWRFdmVudCwgY29udGV4dCk7XG5cbiAgICAvLyB2YWxpZGF0ZSB1c2VyIHJlc3BvbnNlIGFuZCBjcmVhdGUgdGhlIGNvbWJpbmVkIGV2ZW50XG4gICAgY29uc3QgcmVzcG9uc2VFdmVudCA9IHJlbmRlclJlc3BvbnNlKGV2ZW50LCByZXN1bHQpO1xuXG4gICAgLy8gc3VibWl0IHRvIGNmbiBhcyBzdWNjZXNzXG4gICAgYXdhaXQgc3VibWl0UmVzcG9uc2UoJ1NVQ0NFU1MnLCByZXNwb25zZUV2ZW50KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnN0IHJlc3A6IFJlc3BvbnNlID0ge1xuICAgICAgLi4uZXZlbnQsXG4gICAgICBSZWFzb246IGV4dGVybmFsLmluY2x1ZGVTdGFja1RyYWNlcyA/IGUuc3RhY2sgOiBlLm1lc3NhZ2UsXG4gICAgfTtcblxuICAgIGlmICghcmVzcC5QaHlzaWNhbFJlc291cmNlSWQpIHtcbiAgICAgIC8vIHNwZWNpYWwgY2FzZTogaWYgQ1JFQVRFIGZhaWxzLCB3aGljaCB1c3VhbGx5IGltcGxpZXMsIHdlIHVzdWFsbHkgZG9uJ3RcbiAgICAgIC8vIGhhdmUgYSBwaHlzaWNhbCByZXNvdXJjZSBpZC4gaW4gdGhpcyBjYXNlLCB0aGUgc3Vic2VxdWVudCBERUxFVEVcbiAgICAgIC8vIG9wZXJhdGlvbiBkb2VzIG5vdCBoYXZlIGFueSBtZWFuaW5nLCBhbmQgd2lsbCBsaWtlbHkgZmFpbCBhcyB3ZWxsLiB0b1xuICAgICAgLy8gYWRkcmVzcyB0aGlzLCB3ZSB1c2UgYSBtYXJrZXIgc28gdGhlIHByb3ZpZGVyIGZyYW1ld29yayBjYW4gc2ltcGx5XG4gICAgICAvLyBpZ25vcmUgdGhlIHN1YnNlcXVlbnQgREVMRVRFLlxuICAgICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJykge1xuICAgICAgICBleHRlcm5hbC5sb2coJ0NSRUFURSBmYWlsZWQsIHJlc3BvbmRpbmcgd2l0aCBhIG1hcmtlciBwaHlzaWNhbCByZXNvdXJjZSBpZCBzbyB0aGF0IHRoZSBzdWJzZXF1ZW50IERFTEVURSB3aWxsIGJlIGlnbm9yZWQnKTtcbiAgICAgICAgcmVzcC5QaHlzaWNhbFJlc291cmNlSWQgPSBDUkVBVEVfRkFJTEVEX1BIWVNJQ0FMX0lEX01BUktFUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgaWYgUGh5c2ljYWxSZXNvdXJjZUlkIGlzIG5vdCBzcGVjaWZpZWQsIHNvbWV0aGluZyBpc1xuICAgICAgICAvLyB0ZXJyaWJseSB3cm9uZyBiZWNhdXNlIGFsbCBvdGhlciBldmVudHMgc2hvdWxkIGhhdmUgYW4gSUQuXG4gICAgICAgIGV4dGVybmFsLmxvZyhgRVJST1I6IE1hbGZvcm1lZCBldmVudC4gXCJQaHlzaWNhbFJlc291cmNlSWRcIiBpcyByZXF1aXJlZDogJHtKU09OLnN0cmluZ2lmeShldmVudCl9YCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gdGhpcyBpcyBhbiBhY3R1YWwgZXJyb3IsIGZhaWwgdGhlIGFjdGl2aXR5IGFsdG9nZXRoZXIgYW5kIGV4aXN0LlxuICAgIGF3YWl0IHN1Ym1pdFJlc3BvbnNlKCdGQUlMRUQnLCByZXNwKTtcbiAgfVxufVxuXG5mdW5jdGlvbiByZW5kZXJSZXNwb25zZShcbiAgY2ZuUmVxdWVzdDogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VFdmVudCAmIHsgUGh5c2ljYWxSZXNvdXJjZUlkPzogc3RyaW5nIH0sXG4gIGhhbmRsZXJSZXNwb25zZTogdm9pZCB8IEhhbmRsZXJSZXNwb25zZSA9IHsgfSk6IFJlc3BvbnNlIHtcblxuICAvLyBpZiBwaHlzaWNhbCBJRCBpcyBub3QgcmV0dXJuZWQsIHdlIGhhdmUgc29tZSBkZWZhdWx0cyBmb3IgeW91IGJhc2VkXG4gIC8vIG9uIHRoZSByZXF1ZXN0IHR5cGUuXG4gIGNvbnN0IHBoeXNpY2FsUmVzb3VyY2VJZCA9IGhhbmRsZXJSZXNwb25zZS5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5QaHlzaWNhbFJlc291cmNlSWQgPz8gY2ZuUmVxdWVzdC5SZXF1ZXN0SWQ7XG5cbiAgLy8gaWYgd2UgYXJlIGluIERFTEVURSBhbmQgcGh5c2ljYWwgSUQgd2FzIGNoYW5nZWQsIGl0J3MgYW4gZXJyb3IuXG4gIGlmIChjZm5SZXF1ZXN0LlJlcXVlc3RUeXBlID09PSAnRGVsZXRlJyAmJiBwaHlzaWNhbFJlc291cmNlSWQgIT09IGNmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBERUxFVEU6IGNhbm5vdCBjaGFuZ2UgdGhlIHBoeXNpY2FsIHJlc291cmNlIElEIGZyb20gXCIke2NmblJlcXVlc3QuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIHRvIFwiJHtoYW5kbGVyUmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkfVwiIGR1cmluZyBkZWxldGlvbmApO1xuICB9XG5cbiAgLy8gbWVyZ2UgcmVxdWVzdCBldmVudCBhbmQgcmVzdWx0IGV2ZW50IChyZXN1bHQgcHJldmFpbHMpLlxuICByZXR1cm4ge1xuICAgIC4uLmNmblJlcXVlc3QsXG4gICAgLi4uaGFuZGxlclJlc3BvbnNlLFxuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICB9O1xufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRSZXNwb25zZShzdGF0dXM6ICdTVUNDRVNTJyB8ICdGQUlMRUQnLCBldmVudDogUmVzcG9uc2UpIHtcbiAgY29uc3QganNvbjogQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSA9IHtcbiAgICBTdGF0dXM6IHN0YXR1cyxcbiAgICBSZWFzb246IGV2ZW50LlJlYXNvbiA/PyBzdGF0dXMsXG4gICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICBSZXF1ZXN0SWQ6IGV2ZW50LlJlcXVlc3RJZCxcbiAgICBQaHlzaWNhbFJlc291cmNlSWQ6IGV2ZW50LlBoeXNpY2FsUmVzb3VyY2VJZCB8fCBNSVNTSU5HX1BIWVNJQ0FMX0lEX01BUktFUixcbiAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgTm9FY2hvOiBldmVudC5Ob0VjaG8sXG4gICAgRGF0YTogZXZlbnQuRGF0YSxcbiAgfTtcblxuICBleHRlcm5hbC5sb2coJ3N1Ym1pdCByZXNwb25zZSB0byBjbG91ZGZvcm1hdGlvbicsIGpzb24pO1xuXG4gIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KGpzb24pO1xuICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UoZXZlbnQuUmVzcG9uc2VVUkwpO1xuICBjb25zdCByZXEgPSB7XG4gICAgaG9zdG5hbWU6IHBhcnNlZFVybC5ob3N0bmFtZSxcbiAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICBtZXRob2Q6ICdQVVQnLFxuICAgIGhlYWRlcnM6IHsgJ2NvbnRlbnQtdHlwZSc6ICcnLCAnY29udGVudC1sZW5ndGgnOiByZXNwb25zZUJvZHkubGVuZ3RoIH0sXG4gIH07XG5cbiAgYXdhaXQgZXh0ZXJuYWwuc2VuZEh0dHBSZXF1ZXN0KHJlcSwgcmVzcG9uc2VCb2R5KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFNlbmRIdHRwUmVxdWVzdChvcHRpb25zOiBodHRwcy5SZXF1ZXN0T3B0aW9ucywgcmVzcG9uc2VCb2R5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLnJlcXVlc3Qob3B0aW9ucywgXyA9PiByZXNvbHZlKCkpO1xuICAgICAgcmVxdWVzdC5vbignZXJyb3InLCByZWplY3QpO1xuICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZWplY3QoZSk7XG4gICAgfVxuICB9KTtcbn1cblxuZnVuY3Rpb24gZGVmYXVsdExvZyhmbXQ6IHN0cmluZywgLi4ucGFyYW1zOiBhbnlbXSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICBjb25zb2xlLmxvZyhmbXQsIC4uLnBhcmFtcyk7XG59XG4iXX0= \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.d.ts b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.d.ts new file mode 100644 index 0000000000000..3554dc94d4617 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.d.ts @@ -0,0 +1 @@ +export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent): Promise; diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.js b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.js new file mode 100644 index 0000000000000..7ce4156d4ba41 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.js @@ -0,0 +1,78 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handler = void 0; +// eslint-disable-next-line import/no-extraneous-dependencies +const aws_sdk_1 = require("aws-sdk"); +const AUTO_DELETE_OBJECTS_TAG = 'aws-cdk:auto-delete-objects'; +const s3 = new aws_sdk_1.S3(); +async function handler(event) { + switch (event.RequestType) { + case 'Create': + return; + case 'Update': + return onUpdate(event); + case 'Delete': + return onDelete(event.ResourceProperties?.BucketName); + } +} +exports.handler = handler; +async function onUpdate(event) { + const updateEvent = event; + const oldBucketName = updateEvent.OldResourceProperties?.BucketName; + const newBucketName = updateEvent.ResourceProperties?.BucketName; + const bucketNameHasChanged = newBucketName != null && oldBucketName != null && newBucketName !== oldBucketName; + /* If the name of the bucket has changed, CloudFormation will try to delete the bucket + and create a new one with the new name. So we have to delete the contents of the + bucket so that this operation does not fail. */ + if (bucketNameHasChanged) { + return onDelete(oldBucketName); + } +} +/** + * Recursively delete all items in the bucket + * + * @param bucketName the bucket name + */ +async function emptyBucket(bucketName) { + const listedObjects = await s3.listObjectVersions({ Bucket: bucketName }).promise(); + const contents = [...listedObjects.Versions ?? [], ...listedObjects.DeleteMarkers ?? []]; + if (contents.length === 0) { + return; + } + const records = contents.map((record) => ({ Key: record.Key, VersionId: record.VersionId })); + await s3.deleteObjects({ Bucket: bucketName, Delete: { Objects: records } }).promise(); + if (listedObjects?.IsTruncated) { + await emptyBucket(bucketName); + } +} +async function onDelete(bucketName) { + if (!bucketName) { + throw new Error('No BucketName was provided.'); + } + if (!await isBucketTaggedForDeletion(bucketName)) { + process.stdout.write(`Bucket does not have '${AUTO_DELETE_OBJECTS_TAG}' tag, skipping cleaning.\n`); + return; + } + try { + await emptyBucket(bucketName); + } + catch (e) { + if (e.code !== 'NoSuchBucket') { + throw e; + } + // Bucket doesn't exist. Ignoring + } +} +/** + * The bucket will only be tagged for deletion if it's being deleted in the same + * deployment as this Custom Resource. + * + * If the Custom Resource is every deleted before the bucket, it must be because + * `autoDeleteObjects` has been switched to false, in which case the tag would have + * been removed before we get to this Delete event. + */ +async function isBucketTaggedForDeletion(bucketName) { + const response = await s3.getBucketTagging({ Bucket: bucketName }).promise(); + return response.TagSet.some(tag => tag.Key === AUTO_DELETE_OBJECTS_TAG && tag.Value === 'true'); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2REFBNkQ7QUFDN0QscUNBQTZCO0FBRTdCLE1BQU0sdUJBQXVCLEdBQUcsNkJBQTZCLENBQUM7QUFFOUQsTUFBTSxFQUFFLEdBQUcsSUFBSSxZQUFFLEVBQUUsQ0FBQztBQUViLEtBQUssVUFBVSxPQUFPLENBQUMsS0FBa0Q7SUFDOUUsUUFBUSxLQUFLLENBQUMsV0FBVyxFQUFFO1FBQ3pCLEtBQUssUUFBUTtZQUNYLE9BQU87UUFDVCxLQUFLLFFBQVE7WUFDWCxPQUFPLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN6QixLQUFLLFFBQVE7WUFDWCxPQUFPLFFBQVEsQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsVUFBVSxDQUFDLENBQUM7S0FDekQ7QUFDSCxDQUFDO0FBVEQsMEJBU0M7QUFFRCxLQUFLLFVBQVUsUUFBUSxDQUFDLEtBQWtEO0lBQ3hFLE1BQU0sV0FBVyxHQUFHLEtBQTBELENBQUM7SUFDL0UsTUFBTSxhQUFhLEdBQUcsV0FBVyxDQUFDLHFCQUFxQixFQUFFLFVBQVUsQ0FBQztJQUNwRSxNQUFNLGFBQWEsR0FBRyxXQUFXLENBQUMsa0JBQWtCLEVBQUUsVUFBVSxDQUFDO0lBQ2pFLE1BQU0sb0JBQW9CLEdBQUcsYUFBYSxJQUFJLElBQUksSUFBSSxhQUFhLElBQUksSUFBSSxJQUFJLGFBQWEsS0FBSyxhQUFhLENBQUM7SUFFL0c7O3NEQUVrRDtJQUNsRCxJQUFJLG9CQUFvQixFQUFFO1FBQ3hCLE9BQU8sUUFBUSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0tBQ2hDO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxLQUFLLFVBQVUsV0FBVyxDQUFDLFVBQWtCO0lBQzNDLE1BQU0sYUFBYSxHQUFHLE1BQU0sRUFBRSxDQUFDLGtCQUFrQixDQUFDLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDcEYsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFHLGFBQWEsQ0FBQyxRQUFRLElBQUksRUFBRSxFQUFFLEdBQUcsYUFBYSxDQUFDLGFBQWEsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN6RixJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQ3pCLE9BQU87S0FDUjtJQUVELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFXLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFDLEdBQUcsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNsRyxNQUFNLEVBQUUsQ0FBQyxhQUFhLENBQUMsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFFdkYsSUFBSSxhQUFhLEVBQUUsV0FBVyxFQUFFO1FBQzlCLE1BQU0sV0FBVyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0tBQy9CO0FBQ0gsQ0FBQztBQUVELEtBQUssVUFBVSxRQUFRLENBQUMsVUFBbUI7SUFDekMsSUFBSSxDQUFDLFVBQVUsRUFBRTtRQUNmLE1BQU0sSUFBSSxLQUFLLENBQUMsNkJBQTZCLENBQUMsQ0FBQztLQUNoRDtJQUNELElBQUksQ0FBQyxNQUFNLHlCQUF5QixDQUFDLFVBQVUsQ0FBQyxFQUFFO1FBQ2hELE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLHlCQUF5Qix1QkFBdUIsNkJBQTZCLENBQUMsQ0FBQztRQUNwRyxPQUFPO0tBQ1I7SUFDRCxJQUFJO1FBQ0YsTUFBTSxXQUFXLENBQUMsVUFBVSxDQUFDLENBQUM7S0FDL0I7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNWLElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxjQUFjLEVBQUU7WUFDN0IsTUFBTSxDQUFDLENBQUM7U0FDVDtRQUNELGlDQUFpQztLQUNsQztBQUNILENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsS0FBSyxVQUFVLHlCQUF5QixDQUFDLFVBQWtCO0lBQ3pELE1BQU0sUUFBUSxHQUFHLE1BQU0sRUFBRSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDN0UsT0FBTyxRQUFRLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssdUJBQXVCLElBQUksR0FBRyxDQUFDLEtBQUssS0FBSyxNQUFNLENBQUMsQ0FBQztBQUNsRyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llc1xuaW1wb3J0IHsgUzMgfSBmcm9tICdhd3Mtc2RrJztcblxuY29uc3QgQVVUT19ERUxFVEVfT0JKRUNUU19UQUcgPSAnYXdzLWNkazphdXRvLWRlbGV0ZS1vYmplY3RzJztcblxuY29uc3QgczMgPSBuZXcgUzMoKTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQpIHtcbiAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgICByZXR1cm47XG4gICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgIHJldHVybiBvblVwZGF0ZShldmVudCk7XG4gICAgY2FzZSAnRGVsZXRlJzpcbiAgICAgIHJldHVybiBvbkRlbGV0ZShldmVudC5SZXNvdXJjZVByb3BlcnRpZXM/LkJ1Y2tldE5hbWUpO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIG9uVXBkYXRlKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50KSB7XG4gIGNvbnN0IHVwZGF0ZUV2ZW50ID0gZXZlbnQgYXMgQVdTTGFtYmRhLkNsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VVcGRhdGVFdmVudDtcbiAgY29uc3Qgb2xkQnVja2V0TmFtZSA9IHVwZGF0ZUV2ZW50Lk9sZFJlc291cmNlUHJvcGVydGllcz8uQnVja2V0TmFtZTtcbiAgY29uc3QgbmV3QnVja2V0TmFtZSA9IHVwZGF0ZUV2ZW50LlJlc291cmNlUHJvcGVydGllcz8uQnVja2V0TmFtZTtcbiAgY29uc3QgYnVja2V0TmFtZUhhc0NoYW5nZWQgPSBuZXdCdWNrZXROYW1lICE9IG51bGwgJiYgb2xkQnVja2V0TmFtZSAhPSBudWxsICYmIG5ld0J1Y2tldE5hbWUgIT09IG9sZEJ1Y2tldE5hbWU7XG5cbiAgLyogSWYgdGhlIG5hbWUgb2YgdGhlIGJ1Y2tldCBoYXMgY2hhbmdlZCwgQ2xvdWRGb3JtYXRpb24gd2lsbCB0cnkgdG8gZGVsZXRlIHRoZSBidWNrZXRcbiAgICAgYW5kIGNyZWF0ZSBhIG5ldyBvbmUgd2l0aCB0aGUgbmV3IG5hbWUuIFNvIHdlIGhhdmUgdG8gZGVsZXRlIHRoZSBjb250ZW50cyBvZiB0aGVcbiAgICAgYnVja2V0IHNvIHRoYXQgdGhpcyBvcGVyYXRpb24gZG9lcyBub3QgZmFpbC4gKi9cbiAgaWYgKGJ1Y2tldE5hbWVIYXNDaGFuZ2VkKSB7XG4gICAgcmV0dXJuIG9uRGVsZXRlKG9sZEJ1Y2tldE5hbWUpO1xuICB9XG59XG5cbi8qKlxuICogUmVjdXJzaXZlbHkgZGVsZXRlIGFsbCBpdGVtcyBpbiB0aGUgYnVja2V0XG4gKlxuICogQHBhcmFtIGJ1Y2tldE5hbWUgdGhlIGJ1Y2tldCBuYW1lXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGVtcHR5QnVja2V0KGJ1Y2tldE5hbWU6IHN0cmluZykge1xuICBjb25zdCBsaXN0ZWRPYmplY3RzID0gYXdhaXQgczMubGlzdE9iamVjdFZlcnNpb25zKHsgQnVja2V0OiBidWNrZXROYW1lIH0pLnByb21pc2UoKTtcbiAgY29uc3QgY29udGVudHMgPSBbLi4ubGlzdGVkT2JqZWN0cy5WZXJzaW9ucyA/PyBbXSwgLi4ubGlzdGVkT2JqZWN0cy5EZWxldGVNYXJrZXJzID8/IFtdXTtcbiAgaWYgKGNvbnRlbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHJlY29yZHMgPSBjb250ZW50cy5tYXAoKHJlY29yZDogYW55KSA9PiAoeyBLZXk6IHJlY29yZC5LZXksIFZlcnNpb25JZDogcmVjb3JkLlZlcnNpb25JZCB9KSk7XG4gIGF3YWl0IHMzLmRlbGV0ZU9iamVjdHMoeyBCdWNrZXQ6IGJ1Y2tldE5hbWUsIERlbGV0ZTogeyBPYmplY3RzOiByZWNvcmRzIH0gfSkucHJvbWlzZSgpO1xuXG4gIGlmIChsaXN0ZWRPYmplY3RzPy5Jc1RydW5jYXRlZCkge1xuICAgIGF3YWl0IGVtcHR5QnVja2V0KGJ1Y2tldE5hbWUpO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIG9uRGVsZXRlKGJ1Y2tldE5hbWU/OiBzdHJpbmcpIHtcbiAgaWYgKCFidWNrZXROYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdObyBCdWNrZXROYW1lIHdhcyBwcm92aWRlZC4nKTtcbiAgfVxuICBpZiAoIWF3YWl0IGlzQnVja2V0VGFnZ2VkRm9yRGVsZXRpb24oYnVja2V0TmFtZSkpIHtcbiAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShgQnVja2V0IGRvZXMgbm90IGhhdmUgJyR7QVVUT19ERUxFVEVfT0JKRUNUU19UQUd9JyB0YWcsIHNraXBwaW5nIGNsZWFuaW5nLlxcbmApO1xuICAgIHJldHVybjtcbiAgfVxuICB0cnkge1xuICAgIGF3YWl0IGVtcHR5QnVja2V0KGJ1Y2tldE5hbWUpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgaWYgKGUuY29kZSAhPT0gJ05vU3VjaEJ1Y2tldCcpIHtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICAgIC8vIEJ1Y2tldCBkb2Vzbid0IGV4aXN0LiBJZ25vcmluZ1xuICB9XG59XG5cbi8qKlxuICogVGhlIGJ1Y2tldCB3aWxsIG9ubHkgYmUgdGFnZ2VkIGZvciBkZWxldGlvbiBpZiBpdCdzIGJlaW5nIGRlbGV0ZWQgaW4gdGhlIHNhbWVcbiAqIGRlcGxveW1lbnQgYXMgdGhpcyBDdXN0b20gUmVzb3VyY2UuXG4gKlxuICogSWYgdGhlIEN1c3RvbSBSZXNvdXJjZSBpcyBldmVyeSBkZWxldGVkIGJlZm9yZSB0aGUgYnVja2V0LCBpdCBtdXN0IGJlIGJlY2F1c2VcbiAqIGBhdXRvRGVsZXRlT2JqZWN0c2AgaGFzIGJlZW4gc3dpdGNoZWQgdG8gZmFsc2UsIGluIHdoaWNoIGNhc2UgdGhlIHRhZyB3b3VsZCBoYXZlXG4gKiBiZWVuIHJlbW92ZWQgYmVmb3JlIHdlIGdldCB0byB0aGlzIERlbGV0ZSBldmVudC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gaXNCdWNrZXRUYWdnZWRGb3JEZWxldGlvbihidWNrZXROYW1lOiBzdHJpbmcpIHtcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBzMy5nZXRCdWNrZXRUYWdnaW5nKHsgQnVja2V0OiBidWNrZXROYW1lIH0pLnByb21pc2UoKTtcbiAgcmV0dXJuIHJlc3BvbnNlLlRhZ1NldC5zb21lKHRhZyA9PiB0YWcuS2V5ID09PSBBVVRPX0RFTEVURV9PQkpFQ1RTX1RBRyAmJiB0YWcuVmFsdWUgPT09ICd0cnVlJyk7XG59Il19 \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts new file mode 100644 index 0000000000000..2459d44ab1d18 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26/index.ts @@ -0,0 +1,82 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { S3 } from 'aws-sdk'; + +const AUTO_DELETE_OBJECTS_TAG = 'aws-cdk:auto-delete-objects'; + +const s3 = new S3(); + +export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent) { + switch (event.RequestType) { + case 'Create': + return; + case 'Update': + return onUpdate(event); + case 'Delete': + return onDelete(event.ResourceProperties?.BucketName); + } +} + +async function onUpdate(event: AWSLambda.CloudFormationCustomResourceEvent) { + const updateEvent = event as AWSLambda.CloudFormationCustomResourceUpdateEvent; + const oldBucketName = updateEvent.OldResourceProperties?.BucketName; + const newBucketName = updateEvent.ResourceProperties?.BucketName; + const bucketNameHasChanged = newBucketName != null && oldBucketName != null && newBucketName !== oldBucketName; + + /* If the name of the bucket has changed, CloudFormation will try to delete the bucket + and create a new one with the new name. So we have to delete the contents of the + bucket so that this operation does not fail. */ + if (bucketNameHasChanged) { + return onDelete(oldBucketName); + } +} + +/** + * Recursively delete all items in the bucket + * + * @param bucketName the bucket name + */ +async function emptyBucket(bucketName: string) { + const listedObjects = await s3.listObjectVersions({ Bucket: bucketName }).promise(); + const contents = [...listedObjects.Versions ?? [], ...listedObjects.DeleteMarkers ?? []]; + if (contents.length === 0) { + return; + } + + const records = contents.map((record: any) => ({ Key: record.Key, VersionId: record.VersionId })); + await s3.deleteObjects({ Bucket: bucketName, Delete: { Objects: records } }).promise(); + + if (listedObjects?.IsTruncated) { + await emptyBucket(bucketName); + } +} + +async function onDelete(bucketName?: string) { + if (!bucketName) { + throw new Error('No BucketName was provided.'); + } + if (!await isBucketTaggedForDeletion(bucketName)) { + process.stdout.write(`Bucket does not have '${AUTO_DELETE_OBJECTS_TAG}' tag, skipping cleaning.\n`); + return; + } + try { + await emptyBucket(bucketName); + } catch (e) { + if (e.code !== 'NoSuchBucket') { + throw e; + } + // Bucket doesn't exist. Ignoring + } +} + +/** + * The bucket will only be tagged for deletion if it's being deleted in the same + * deployment as this Custom Resource. + * + * If the Custom Resource is every deleted before the bucket, it must be because + * `autoDeleteObjects` has been switched to false, in which case the tag would have + * been removed before we get to this Delete event. + */ +async function isBucketTaggedForDeletion(bucketName: string) { + const response = await s3.getBucketTagging({ Bucket: bucketName }).promise(); + return response.TagSet.some(tag => tag.Key === AUTO_DELETE_OBJECTS_TAG && tag.Value === 'true'); +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/cdk.out b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/integ.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/integ.json new file mode 100644 index 0000000000000..240c501c9dba6 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "PreparelessPipelineTest/DefaultTest": { + "stacks": [ + "PreparelessPipelineStack" + ], + "assertionStack": "PreparelessPipelineTest/DefaultTest/DeployAssert", + "assertionStackName": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/manifest.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..595a5fc4aa045 --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/manifest.json @@ -0,0 +1,214 @@ +{ + "version": "21.0.0", + "artifacts": { + "assembly-PreparelessPipelineStack-MyStage": { + "type": "cdk:cloud-assembly", + "properties": { + "directoryName": "assembly-PreparelessPipelineStack-MyStage", + "displayName": "PreparelessPipelineStack/MyStage" + } + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "PreparelessPipelineStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineStack.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d48998cffedf7ada707ce9f74130f229eb47e4c549355b1031589d0c4d78d9ad.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineStack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "PreparelessPipelineStack.assets" + ], + "metadata": { + "/PreparelessPipelineStack/SourceBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketDDD2130A" + } + ], + "/PreparelessPipelineStack/SourceBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketPolicy703DFBF9" + } + ], + "/PreparelessPipelineStack/SourceBucket/AutoDeleteObjectsCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SourceBucketAutoDeleteObjectsCustomResourceC68FC040" + } + ], + "/PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092" + } + ], + "/PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucketAEA9A052" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineArtifactsBucketPolicyF53CCC52" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineRoleB27FAA37" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineRoleDefaultPolicy7BDC1ABB" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Pipeline9850B417" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineSourceS3CodePipelineActionRole83895A58" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C" + } + ], + "/PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ], + "/PreparelessPipelineStack/Pipeline/CodeBuildActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineCodeBuildActionRole226DB0CB" + } + ], + "/PreparelessPipelineStack/Pipeline/CodeBuildActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE" + } + ], + "/PreparelessPipelineStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineStack" + }, + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "PreparelessPipelineTestDefaultTestDeployAssert7B7DD2C6.assets" + ], + "metadata": { + "/PreparelessPipelineTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/PreparelessPipelineTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "PreparelessPipelineTest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/tree.json b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/tree.json new file mode 100644 index 0000000000000..05ba185d3d1dd --- /dev/null +++ b/packages/@aws-cdk/pipelines/test/pipeline-without-prepare.integ.snapshot/tree.json @@ -0,0 +1,1402 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "PreparelessPipelineStack": { + "id": "PreparelessPipelineStack", + "path": "PreparelessPipelineStack", + "children": { + "SourceBucket": { + "id": "SourceBucket", + "path": "PreparelessPipelineStack/SourceBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/SourceBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "aws-cdk:auto-delete-objects", + "value": "true" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "PreparelessPipelineStack/SourceBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/SourceBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn" + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketPolicy", + "version": "0.0.0" + } + }, + "AutoDeleteObjectsCustomResource": { + "id": "AutoDeleteObjectsCustomResource", + "path": "PreparelessPipelineStack/SourceBucket/AutoDeleteObjectsCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "PreparelessPipelineStack/SourceBucket/AutoDeleteObjectsCustomResource/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.Bucket", + "version": "0.0.0" + } + }, + "Custom::S3AutoDeleteObjectsCustomResourceProvider": { + "id": "Custom::S3AutoDeleteObjectsCustomResourceProvider", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "PreparelessPipelineStack/Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResourceProvider", + "version": "0.0.0" + } + }, + "Pipeline": { + "id": "Pipeline", + "path": "PreparelessPipelineStack/Pipeline", + "children": { + "Pipeline": { + "id": "Pipeline", + "path": "PreparelessPipelineStack/Pipeline/Pipeline", + "children": { + "ArtifactsBucket": { + "id": "ArtifactsBucket", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": { + "bucketEncryption": { + "serverSideEncryptionConfiguration": [ + { + "serverSideEncryptionByDefault": { + "sseAlgorithm": "aws:kms" + } + } + ] + }, + "publicAccessBlockConfiguration": { + "blockPublicAcls": true, + "blockPublicPolicy": true, + "ignorePublicAcls": true, + "restrictPublicBuckets": true + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/ArtifactsBucket/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::BucketPolicy", + "aws:cdk:cloudformation:props": { + "bucket": { + "Ref": "PipelineArtifactsBucketAEA9A052" + }, + "policyDocument": { + "Statement": [ + { + "Action": "s3:*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": { + "AWS": "*" + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + }, + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.BucketPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.Bucket", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineRoleDefaultPolicy7BDC1ABB", + "roles": [ + { + "Ref": "PipelineRoleB27FAA37" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodePipeline::Pipeline", + "aws:cdk:cloudformation:props": { + "roleArn": { + "Fn::GetAtt": [ + "PipelineRoleB27FAA37", + "Arn" + ] + }, + "stages": [ + { + "name": "Source", + "actions": [ + { + "name": { + "Ref": "SourceBucketDDD2130A" + }, + "outputArtifacts": [ + { + "name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "actionTypeId": { + "category": "Source", + "version": "1", + "owner": "AWS", + "provider": "S3" + }, + "configuration": { + "S3Bucket": { + "Ref": "SourceBucketDDD2130A" + }, + "S3ObjectKey": "key" + }, + "runOrder": 1, + "roleArn": { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole83895A58", + "Arn" + ] + } + } + ] + }, + { + "name": "Build", + "actions": [ + { + "name": "Synth", + "inputArtifacts": [ + { + "name": "c8506b445957b8105ede7b68ebe35e9406d642cd0c_Source" + } + ], + "outputArtifacts": [ + { + "name": "Synth_Output" + } + ], + "actionTypeId": { + "category": "Build", + "version": "1", + "owner": "AWS", + "provider": "CodeBuild" + }, + "configuration": { + "ProjectName": { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + }, + "runOrder": 1, + "roleArn": { + "Fn::GetAtt": [ + "PipelineCodeBuildActionRole226DB0CB", + "Arn" + ] + } + } + ] + }, + { + "name": "MyStage", + "actions": [ + { + "name": "Deploy", + "inputArtifacts": [ + { + "name": "Synth_Output" + } + ], + "actionTypeId": { + "category": "Deploy", + "version": "1", + "owner": "AWS", + "provider": "CloudFormation" + }, + "configuration": { + "StackName": "MyStage-Stack", + "Capabilities": "CAPABILITY_NAMED_IAM,CAPABILITY_AUTO_EXPAND", + "RoleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-cfn-exec-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + }, + "ActionMode": "CREATE_UPDATE", + "TemplatePath": "Synth_Output::assembly-PreparelessPipelineStack-MyStage/PreparelessPipelineStackMyStageStack3DC192E7.template.json" + }, + "runOrder": 1, + "roleArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + } + ] + } + ], + "artifactStore": { + "type": "S3", + "location": { + "Ref": "PipelineArtifactsBucketAEA9A052" + } + }, + "restartExecutionOnUpdate": true + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codepipeline.CfnPipeline", + "version": "0.0.0" + } + }, + "Source": { + "id": "Source", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source", + "children": { + "S3": { + "id": "S3", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3", + "children": { + "CodePipelineActionRole": { + "id": "CodePipelineActionRole", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Source/S3/CodePipelineActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SourceBucketDDD2130A", + "Arn" + ] + }, + "/key" + ] + ] + } + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineSourceS3CodePipelineActionRoleDefaultPolicyB176A07F", + "roles": [ + { + "Ref": "PipelineSourceS3CodePipelineActionRole83895A58" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "Build": { + "id": "Build", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build", + "children": { + "Synth": { + "id": "Synth", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth", + "children": { + "CdkBuildProject": { + "id": "CdkBuildProject", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject", + "children": { + "Role": { + "id": "Role", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codebuild.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + } + ] + ] + } + ] + }, + { + "Action": [ + "codebuild:BatchPutCodeCoverages", + "codebuild:BatchPutTestCases", + "codebuild:CreateReport", + "codebuild:CreateReportGroup", + "codebuild:UpdateReport" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":codebuild:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":report-group/", + { + "Ref": "PipelineBuildSynthCdkBuildProject6BEFA8E6" + }, + "-*" + ] + ] + } + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "PipelineArtifactsBucketAEA9A052", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineBuildSynthCdkBuildProjectRoleDefaultPolicyFB6C941C", + "roles": [ + { + "Ref": "PipelineBuildSynthCdkBuildProjectRole231EEA2A" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/Build/Synth/CdkBuildProject/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeBuild::Project", + "aws:cdk:cloudformation:props": { + "artifacts": { + "type": "CODEPIPELINE" + }, + "environment": { + "type": "LINUX_CONTAINER", + "image": "aws/codebuild/standard:5.0", + "imagePullCredentialsType": "CODEBUILD", + "privilegedMode": false, + "computeType": "BUILD_GENERAL1_SMALL" + }, + "serviceRole": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProjectRole231EEA2A", + "Arn" + ] + }, + "source": { + "type": "CODEPIPELINE", + "buildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"build\": {\n \"commands\": [\n \"mkdir cdk.out\",\n \"touch cdk.out/dummy\"\n ]\n }\n },\n \"artifacts\": {\n \"base-directory\": \"cdk.out\",\n \"files\": \"**/*\"\n }\n}" + }, + "cache": { + "type": "NO_CACHE" + }, + "description": "Pipeline step PreparelessPipelineStack/Pipeline/Build/Synth", + "encryptionKey": "alias/aws/s3" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codebuild.CfnProject", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codebuild.PipelineProject", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "MyStage": { + "id": "MyStage", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MyStage", + "children": { + "Deploy": { + "id": "Deploy", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MyStage/Deploy", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}": { + "id": "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}": { + "id": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "children": { + "8389e75f-0810-4838-bf64-d6f85a95cf83": { + "id": "8389e75f-0810-4838-bf64-d6f85a95cf83", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}/8389e75f-0810-4838-bf64-d6f85a95cf83", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}": { + "id": "MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/MutableRolearn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}": { + "id": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "path": "PreparelessPipelineStack/Pipeline/Pipeline/arn:${AWS::Partition}:iam::${AWS::AccountId}:role--cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codepipeline.Pipeline", + "version": "0.0.0" + } + }, + "CodeBuildActionRole": { + "id": "CodeBuildActionRole", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Condition": { + "Bool": { + "aws:ViaAWSService": "codepipeline.amazonaws.com" + } + }, + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/Pipeline/CodeBuildActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:StopBuild" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "PipelineBuildSynthCdkBuildProject6BEFA8E6", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "PipelineCodeBuildActionRoleDefaultPolicy1D62A6FE", + "roles": [ + { + "Ref": "PipelineCodeBuildActionRole226DB0CB" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/pipelines.CodePipeline", + "version": "0.0.0" + } + }, + "MyStage": { + "id": "MyStage", + "path": "PreparelessPipelineStack/MyStage", + "children": { + "Stack": { + "id": "Stack", + "path": "PreparelessPipelineStack/MyStage/Stack", + "children": { + "MyApp": { + "id": "MyApp", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp", + "children": { + "Stack": { + "id": "Stack", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack", + "children": { + "Bucket": { + "id": "Bucket", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/Bucket", + "children": { + "Resource": { + "id": "Resource", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/Bucket/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.CfnBucket", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-s3.Bucket", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/MyApp/Stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stage", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "PreparelessPipelineStack/MyStage/Stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stage", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "PreparelessPipelineTest": { + "id": "PreparelessPipelineTest", + "path": "PreparelessPipelineTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "PreparelessPipelineTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "PreparelessPipelineTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.92" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "PreparelessPipelineTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts b/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts index 24e29b2625929..fca2cd26f04d4 100644 --- a/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts +++ b/packages/@aws-cdk/pipelines/test/testhelpers/test-app.ts @@ -223,4 +223,10 @@ export class PlainStackApp extends Stage { } } - +export class MultiStackApp extends Stage { + constructor(scope: Construct, id: string, props?: StageProps) { + super(scope, id, props); + new BucketStack(this, 'Stack1'); + new BucketStack(this, 'Stack2'); + } +}