1 | # AWS CodePipeline Construct Library
|
2 |
|
3 |
|
4 | ---
|
5 |
|
6 | ![End-of-Support](https://img.shields.io/badge/End--of--Support-critical.svg?style=for-the-badge)
|
7 |
|
8 | > AWS CDK v1 has reached End-of-Support on 2023-06-01.
|
9 | > This package is no longer being updated, and users should migrate to AWS CDK v2.
|
10 | >
|
11 | > For more information on how to migrate, see the [_Migrating to AWS CDK v2_ guide][doc].
|
12 | >
|
13 | > [doc]: https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html
|
14 |
|
15 | ---
|
16 |
|
17 |
|
18 |
|
19 | ## Pipeline
|
20 |
|
21 | To construct an empty Pipeline:
|
22 |
|
23 | ```ts
|
24 | // Construct an empty Pipeline
|
25 | const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline');
|
26 | ```
|
27 |
|
28 | To give the Pipeline a nice, human-readable name:
|
29 |
|
30 | ```ts
|
31 | // Give the Pipeline a nice, human-readable name
|
32 | const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline', {
|
33 | pipelineName: 'MyPipeline',
|
34 | });
|
35 | ```
|
36 |
|
37 | Be aware that in the default configuration, the `Pipeline` construct creates
|
38 | an AWS Key Management Service (AWS KMS) Customer Master Key (CMK) for you to
|
39 | encrypt the artifacts in the artifact bucket, which incurs a cost of
|
40 | **$1/month**. This default configuration is necessary to allow cross-account
|
41 | actions.
|
42 |
|
43 | If you do not intend to perform cross-account deployments, you can disable
|
44 | the creation of the Customer Master Keys by passing `crossAccountKeys: false`
|
45 | when defining the Pipeline:
|
46 |
|
47 | ```ts
|
48 | // Don't create Customer Master Keys
|
49 | const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline', {
|
50 | crossAccountKeys: false,
|
51 | });
|
52 | ```
|
53 |
|
54 | If you want to enable key rotation for the generated KMS keys,
|
55 | you can configure it by passing `enableKeyRotation: true` when creating the pipeline.
|
56 | Note that key rotation will incur an additional cost of **$1/month**.
|
57 |
|
58 | ```ts
|
59 | // Enable key rotation for the generated KMS key
|
60 | const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline', {
|
61 | // ...
|
62 | enableKeyRotation: true,
|
63 | });
|
64 | ```
|
65 |
|
66 | ## Stages
|
67 |
|
68 | You can provide Stages when creating the Pipeline:
|
69 |
|
70 | ```ts
|
71 | // Provide a Stage when creating a pipeline
|
72 | const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline', {
|
73 | stages: [
|
74 | {
|
75 | stageName: 'Source',
|
76 | actions: [
|
77 | // see below...
|
78 | ],
|
79 | },
|
80 | ],
|
81 | });
|
82 | ```
|
83 |
|
84 | Or append a Stage to an existing Pipeline:
|
85 |
|
86 | ```ts
|
87 | // Append a Stage to an existing Pipeline
|
88 | declare const pipeline: codepipeline.Pipeline;
|
89 | const sourceStage = pipeline.addStage({
|
90 | stageName: 'Source',
|
91 | actions: [ // optional property
|
92 | // see below...
|
93 | ],
|
94 | });
|
95 | ```
|
96 |
|
97 | You can insert the new Stage at an arbitrary point in the Pipeline:
|
98 |
|
99 | ```ts
|
100 | // Insert a new Stage at an arbitrary point
|
101 | declare const pipeline: codepipeline.Pipeline;
|
102 | declare const anotherStage: codepipeline.IStage;
|
103 | declare const yetAnotherStage: codepipeline.IStage;
|
104 |
|
105 | const someStage = pipeline.addStage({
|
106 | stageName: 'SomeStage',
|
107 | placement: {
|
108 | // note: you can only specify one of the below properties
|
109 | rightBefore: anotherStage,
|
110 | justAfter: yetAnotherStage,
|
111 | }
|
112 | });
|
113 | ```
|
114 |
|
115 | You can disable transition to a Stage:
|
116 |
|
117 | ```ts
|
118 | // Disable transition to a stage
|
119 | declare const pipeline: codepipeline.Pipeline;
|
120 |
|
121 | const someStage = pipeline.addStage({
|
122 | stageName: 'SomeStage',
|
123 | transitionToEnabled: false,
|
124 | transitionDisabledReason: 'Manual transition only', // optional reason
|
125 | })
|
126 | ```
|
127 |
|
128 | This is useful if you don't want every executions of the pipeline to flow into
|
129 | this stage automatically. The transition can then be "manually" enabled later on.
|
130 |
|
131 | ## Actions
|
132 |
|
133 | Actions live in a separate package, `@aws-cdk/aws-codepipeline-actions`.
|
134 |
|
135 | To add an Action to a Stage, you can provide it when creating the Stage,
|
136 | in the `actions` property,
|
137 | or you can use the `IStage.addAction()` method to mutate an existing Stage:
|
138 |
|
139 | ```ts
|
140 | // Use the `IStage.addAction()` method to mutate an existing Stage.
|
141 | declare const sourceStage: codepipeline.IStage;
|
142 | declare const someAction: codepipeline.Action;
|
143 | sourceStage.addAction(someAction);
|
144 | ```
|
145 |
|
146 | ## Custom Action Registration
|
147 |
|
148 | To make your own custom CodePipeline Action requires registering the action provider. Look to the `JenkinsProvider` in `@aws-cdk/aws-codepipeline-actions` for an implementation example.
|
149 |
|
150 | ```ts
|
151 | // Make a custom CodePipeline Action
|
152 | new codepipeline.CustomActionRegistration(this, 'GenericGitSourceProviderResource', {
|
153 | category: codepipeline.ActionCategory.SOURCE,
|
154 | artifactBounds: { minInputs: 0, maxInputs: 0, minOutputs: 1, maxOutputs: 1 },
|
155 | provider: 'GenericGitSource',
|
156 | version: '1',
|
157 | entityUrl: 'https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-create-custom-action.html',
|
158 | executionUrl: 'https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-create-custom-action.html',
|
159 | actionProperties: [
|
160 | {
|
161 | name: 'Branch',
|
162 | required: true,
|
163 | key: false,
|
164 | secret: false,
|
165 | queryable: false,
|
166 | description: 'Git branch to pull',
|
167 | type: 'String',
|
168 | },
|
169 | {
|
170 | name: 'GitUrl',
|
171 | required: true,
|
172 | key: false,
|
173 | secret: false,
|
174 | queryable: false,
|
175 | description: 'SSH git clone URL',
|
176 | type: 'String',
|
177 | },
|
178 | ],
|
179 | });
|
180 | ```
|
181 |
|
182 | ## Cross-account CodePipelines
|
183 |
|
184 | > Cross-account Pipeline actions require that the Pipeline has *not* been
|
185 | > created with `crossAccountKeys: false`.
|
186 |
|
187 | Most pipeline Actions accept an AWS resource object to operate on. For example:
|
188 |
|
189 | * `S3DeployAction` accepts an `s3.IBucket`.
|
190 | * `CodeBuildAction` accepts a `codebuild.IProject`.
|
191 | * etc.
|
192 |
|
193 | These resources can be either newly defined (`new s3.Bucket(...)`) or imported
|
194 | (`s3.Bucket.fromBucketAttributes(...)`) and identify the resource that should
|
195 | be changed.
|
196 |
|
197 | These resources can be in different accounts than the pipeline itself. For
|
198 | example, the following action deploys to an imported S3 bucket from a
|
199 | different account:
|
200 |
|
201 | ```ts
|
202 | // Deploy an imported S3 bucket from a different account
|
203 | declare const stage: codepipeline.IStage;
|
204 | declare const input: codepipeline.Artifact;
|
205 | stage.addAction(new codepipeline_actions.S3DeployAction({
|
206 | bucket: s3.Bucket.fromBucketAttributes(this, 'Bucket', {
|
207 | account: '123456789012',
|
208 | // ...
|
209 | }),
|
210 | input: input,
|
211 | actionName: 's3-deploy-action',
|
212 | // ...
|
213 | }));
|
214 | ```
|
215 |
|
216 | Actions that don't accept a resource object accept an explicit `account` parameter:
|
217 |
|
218 | ```ts
|
219 | // Actions that don't accept a resource objet accept an explicit `account` parameter
|
220 | declare const stage: codepipeline.IStage;
|
221 | declare const templatePath: codepipeline.ArtifactPath;
|
222 | stage.addAction(new codepipeline_actions.CloudFormationCreateUpdateStackAction({
|
223 | account: '123456789012',
|
224 | templatePath,
|
225 | adminPermissions: false,
|
226 | stackName: Stack.of(this).stackName,
|
227 | actionName: 'cloudformation-create-update',
|
228 | // ...
|
229 | }));
|
230 | ```
|
231 |
|
232 | The `Pipeline` construct automatically defines an **IAM Role** for you in the
|
233 | target account which the pipeline will assume to perform that action. This
|
234 | Role will be defined in a **support stack** named
|
235 | `<PipelineStackName>-support-<account>`, that will automatically be deployed
|
236 | before the stack containing the pipeline.
|
237 |
|
238 | If you do not want to use the generated role, you can also explicitly pass a
|
239 | `role` when creating the action. In that case, the action will operate in the
|
240 | account the role belongs to:
|
241 |
|
242 | ```ts
|
243 | // Explicitly pass in a `role` when creating an action.
|
244 | declare const stage: codepipeline.IStage;
|
245 | declare const templatePath: codepipeline.ArtifactPath;
|
246 | stage.addAction(new codepipeline_actions.CloudFormationCreateUpdateStackAction({
|
247 | templatePath,
|
248 | adminPermissions: false,
|
249 | stackName: Stack.of(this).stackName,
|
250 | actionName: 'cloudformation-create-update',
|
251 | // ...
|
252 | role: iam.Role.fromRoleArn(this, 'ActionRole', '...'),
|
253 | }));
|
254 | ```
|
255 |
|
256 | ## Cross-region CodePipelines
|
257 |
|
258 | Similar to how you set up a cross-account Action, the AWS resource object you
|
259 | pass to actions can also be in different *Regions*. For example, the
|
260 | following Action deploys to an imported S3 bucket from a different Region:
|
261 |
|
262 | ```ts
|
263 | // Deploy to an imported S3 bucket from a different Region.
|
264 | declare const stage: codepipeline.IStage;
|
265 | declare const input: codepipeline.Artifact;
|
266 | stage.addAction(new codepipeline_actions.S3DeployAction({
|
267 | bucket: s3.Bucket.fromBucketAttributes(this, 'Bucket', {
|
268 | region: 'us-west-1',
|
269 | // ...
|
270 | }),
|
271 | input: input,
|
272 | actionName: 's3-deploy-action',
|
273 | // ...
|
274 | }));
|
275 | ```
|
276 |
|
277 | Actions that don't take an AWS resource will accept an explicit `region`
|
278 | parameter:
|
279 |
|
280 | ```ts
|
281 | // Actions that don't take an AWS resource will accept an explicit `region` parameter.
|
282 | declare const stage: codepipeline.IStage;
|
283 | declare const templatePath: codepipeline.ArtifactPath;
|
284 | stage.addAction(new codepipeline_actions.CloudFormationCreateUpdateStackAction({
|
285 | templatePath,
|
286 | adminPermissions: false,
|
287 | stackName: Stack.of(this).stackName,
|
288 | actionName: 'cloudformation-create-update',
|
289 | // ...
|
290 | region: 'us-west-1',
|
291 | }));
|
292 | ```
|
293 |
|
294 | The `Pipeline` construct automatically defines a **replication bucket** for
|
295 | you in the target region, which the pipeline will replicate artifacts to and
|
296 | from. This Bucket will be defined in a **support stack** named
|
297 | `<PipelineStackName>-support-<region>`, that will automatically be deployed
|
298 | before the stack containing the pipeline.
|
299 |
|
300 | If you don't want to use these support stacks, and already have buckets in
|
301 | place to serve as replication buckets, you can supply these at Pipeline definition
|
302 | time using the `crossRegionReplicationBuckets` parameter. Example:
|
303 |
|
304 | ```ts
|
305 | // Supply replication buckets for the Pipeline instead of using the generated support stack
|
306 | const pipeline = new codepipeline.Pipeline(this, 'MyFirstPipeline', {
|
307 | // ...
|
308 |
|
309 | crossRegionReplicationBuckets: {
|
310 | // note that a physical name of the replication Bucket must be known at synthesis time
|
311 | 'us-west-1': s3.Bucket.fromBucketAttributes(this, 'UsWest1ReplicationBucket', {
|
312 | bucketName: 'my-us-west-1-replication-bucket',
|
313 | // optional KMS key
|
314 | encryptionKey: kms.Key.fromKeyArn(this, 'UsWest1ReplicationKey',
|
315 | 'arn:aws:kms:us-west-1:123456789012:key/1234-5678-9012'
|
316 | ),
|
317 | }),
|
318 | },
|
319 | });
|
320 | ```
|
321 |
|
322 | See [the AWS docs here](https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-create-cross-region.html)
|
323 | for more information on cross-region CodePipelines.
|
324 |
|
325 | ### Creating an encrypted replication bucket
|
326 |
|
327 | If you're passing a replication bucket created in a different stack,
|
328 | like this:
|
329 |
|
330 | ```ts
|
331 | // Passing a replication bucket created in a different stack.
|
332 | const app = new App();
|
333 | const replicationStack = new Stack(app, 'ReplicationStack', {
|
334 | env: {
|
335 | region: 'us-west-1',
|
336 | },
|
337 | });
|
338 | const key = new kms.Key(replicationStack, 'ReplicationKey');
|
339 | const replicationBucket = new s3.Bucket(replicationStack, 'ReplicationBucket', {
|
340 | // like was said above - replication buckets need a set physical name
|
341 | bucketName: PhysicalName.GENERATE_IF_NEEDED,
|
342 | encryptionKey: key, // does not work!
|
343 | });
|
344 |
|
345 | // later...
|
346 | new codepipeline.Pipeline(replicationStack, 'Pipeline', {
|
347 | crossRegionReplicationBuckets: {
|
348 | 'us-west-1': replicationBucket,
|
349 | },
|
350 | });
|
351 | ```
|
352 |
|
353 | When trying to encrypt it
|
354 | (and note that if any of the cross-region actions happen to be cross-account as well,
|
355 | the bucket *has to* be encrypted - otherwise the pipeline will fail at runtime),
|
356 | you cannot use a key directly - KMS keys don't have physical names,
|
357 | and so you can't reference them across environments.
|
358 |
|
359 | In this case, you need to use an alias in place of the key when creating the bucket:
|
360 |
|
361 | ```ts
|
362 | // Passing an encrypted replication bucket created in a different stack.
|
363 | const app = new App();
|
364 | const replicationStack = new Stack(app, 'ReplicationStack', {
|
365 | env: {
|
366 | region: 'us-west-1',
|
367 | },
|
368 | });
|
369 | const key = new kms.Key(replicationStack, 'ReplicationKey');
|
370 | const alias = new kms.Alias(replicationStack, 'ReplicationAlias', {
|
371 | // aliasName is required
|
372 | aliasName: PhysicalName.GENERATE_IF_NEEDED,
|
373 | targetKey: key,
|
374 | });
|
375 | const replicationBucket = new s3.Bucket(replicationStack, 'ReplicationBucket', {
|
376 | bucketName: PhysicalName.GENERATE_IF_NEEDED,
|
377 | encryptionKey: alias,
|
378 | });
|
379 | ```
|
380 |
|
381 | ## Variables
|
382 |
|
383 | The library supports the CodePipeline Variables feature.
|
384 | Each action class that emits variables has a separate variables interface,
|
385 | accessed as a property of the action instance called `variables`.
|
386 | You instantiate the action class and assign it to a local variable;
|
387 | when you want to use a variable in the configuration of a different action,
|
388 | you access the appropriate property of the interface returned from `variables`,
|
389 | which represents a single variable.
|
390 | Example:
|
391 |
|
392 | ```ts fixture=action
|
393 | // MyAction is some action type that produces variables, like EcrSourceAction
|
394 | const myAction = new MyAction({
|
395 | // ...
|
396 | actionName: 'myAction',
|
397 | });
|
398 | new OtherAction({
|
399 | // ...
|
400 | config: myAction.variables.myVariable,
|
401 | actionName: 'otherAction',
|
402 | });
|
403 | ```
|
404 |
|
405 | The namespace name that will be used will be automatically generated by the pipeline construct,
|
406 | based on the stage and action name;
|
407 | you can pass a custom name when creating the action instance:
|
408 |
|
409 | ```ts fixture=action
|
410 | // MyAction is some action type that produces variables, like EcrSourceAction
|
411 | const myAction = new MyAction({
|
412 | // ...
|
413 | variablesNamespace: 'MyNamespace',
|
414 | actionName: 'myAction',
|
415 | });
|
416 | ```
|
417 |
|
418 | There are also global variables available,
|
419 | not tied to any action;
|
420 | these are accessed through static properties of the `GlobalVariables` class:
|
421 |
|
422 | ```ts fixture=action
|
423 | // OtherAction is some action type that produces variables, like EcrSourceAction
|
424 | new OtherAction({
|
425 | // ...
|
426 | config: codepipeline.GlobalVariables.executionId,
|
427 | actionName: 'otherAction',
|
428 | });
|
429 | ```
|
430 |
|
431 | Check the documentation of the `@aws-cdk/aws-codepipeline-actions`
|
432 | for details on how to use the variables for each action class.
|
433 |
|
434 | See the [CodePipeline documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-variables.html)
|
435 | for more details on how to use the variables feature.
|
436 |
|
437 | ## Events
|
438 |
|
439 | ### Using a pipeline as an event target
|
440 |
|
441 | A pipeline can be used as a target for a CloudWatch event rule:
|
442 |
|
443 | ```ts
|
444 | // A pipeline being used as a target for a CloudWatch event rule.
|
445 | import * as targets from '@aws-cdk/aws-events-targets';
|
446 | import * as events from '@aws-cdk/aws-events';
|
447 |
|
448 | // kick off the pipeline every day
|
449 | const rule = new events.Rule(this, 'Daily', {
|
450 | schedule: events.Schedule.rate(Duration.days(1)),
|
451 | });
|
452 |
|
453 | declare const pipeline: codepipeline.Pipeline;
|
454 | rule.addTarget(new targets.CodePipeline(pipeline));
|
455 | ```
|
456 |
|
457 | When a pipeline is used as an event target, the
|
458 | "codepipeline:StartPipelineExecution" permission is granted to the AWS
|
459 | CloudWatch Events service.
|
460 |
|
461 | ### Event sources
|
462 |
|
463 | Pipelines emit CloudWatch events. To define event rules for events emitted by
|
464 | the pipeline, stages or action, use the `onXxx` methods on the respective
|
465 | construct:
|
466 |
|
467 | ```ts
|
468 | // Define event rules for events emitted by the pipeline
|
469 | import * as events from '@aws-cdk/aws-events';
|
470 |
|
471 | declare const myPipeline: codepipeline.Pipeline;
|
472 | declare const myStage: codepipeline.IStage;
|
473 | declare const myAction: codepipeline.Action;
|
474 | declare const target: events.IRuleTarget;
|
475 | myPipeline.onStateChange('MyPipelineStateChange', { target: target } );
|
476 | myStage.onStateChange('MyStageStateChange', target);
|
477 | myAction.onStateChange('MyActionStateChange', target);
|
478 | ```
|
479 |
|
480 | ## CodeStar Notifications
|
481 |
|
482 | To define CodeStar Notification rules for Pipelines, use one of the `notifyOnXxx()` methods.
|
483 | They are very similar to `onXxx()` methods for CloudWatch events:
|
484 |
|
485 | ```ts
|
486 | // Define CodeStar Notification rules for Pipelines
|
487 | import * as chatbot from '@aws-cdk/aws-chatbot';
|
488 | const target = new chatbot.SlackChannelConfiguration(this, 'MySlackChannel', {
|
489 | slackChannelConfigurationName: 'YOUR_CHANNEL_NAME',
|
490 | slackWorkspaceId: 'YOUR_SLACK_WORKSPACE_ID',
|
491 | slackChannelId: 'YOUR_SLACK_CHANNEL_ID',
|
492 | });
|
493 |
|
494 | declare const pipeline: codepipeline.Pipeline;
|
495 | const rule = pipeline.notifyOnExecutionStateChange('NotifyOnExecutionStateChange', target);
|
496 | ```
|