in packages/aws-cdk-lib/aws-codepipeline/lib/pipeline.ts [590:717]
constructor(scope: Construct, id: string, props: PipelineProps = {}) {
super(scope, id, {
physicalName: props.pipelineName,
});
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
validateName(this, 'Pipeline', this.physicalName);
// only one of artifactBucket and crossRegionReplicationBuckets can be supplied
if (props.artifactBucket && props.crossRegionReplicationBuckets) {
throw new ValidationError('Only one of artifactBucket and crossRegionReplicationBuckets can be specified!', this);
}
// The feature flag is set to true by default for new projects, otherwise false.
this.crossAccountKeys = props.crossAccountKeys
?? (FeatureFlags.of(this).isEnabled(cxapi.CODEPIPELINE_CROSS_ACCOUNT_KEYS_DEFAULT_VALUE_TO_FALSE) ? false : true);
this.enableKeyRotation = props.enableKeyRotation;
// Cross account keys must be set for key rotation to be enabled
if (this.enableKeyRotation && !this.crossAccountKeys) {
throw new ValidationError("Setting 'enableKeyRotation' to true also requires 'crossAccountKeys' to be enabled", this);
}
this.reuseCrossRegionSupportStacks = props.reuseCrossRegionSupportStacks ?? true;
this.usePipelineRoleForActions = props.usePipelineRoleForActions ?? false;
// If a bucket has been provided, use it - otherwise, create a bucket.
let propsBucket = this.getArtifactBucketFromProps(props);
if (!propsBucket) {
let encryptionKey;
if (this.crossAccountKeys) {
encryptionKey = new kms.Key(this, 'ArtifactsBucketEncryptionKey', {
// remove the key - there is a grace period of a few days before it's gone for good,
// that should be enough for any emergency access to the bucket artifacts
removalPolicy: RemovalPolicy.DESTROY,
enableKeyRotation: this.enableKeyRotation,
});
// add an alias to make finding the key in the console easier
new kms.Alias(this, 'ArtifactsBucketEncryptionKeyAlias', {
aliasName: this.generateNameForDefaultBucketKeyAlias(),
targetKey: encryptionKey,
removalPolicy: RemovalPolicy.DESTROY, // destroy the alias along with the key
});
}
propsBucket = new s3.Bucket(this, 'ArtifactsBucket', {
bucketName: PhysicalName.GENERATE_IF_NEEDED,
encryptionKey,
encryption: encryptionKey ? s3.BucketEncryption.KMS : s3.BucketEncryption.KMS_MANAGED,
enforceSSL: true,
blockPublicAccess: new s3.BlockPublicAccess(s3.BlockPublicAccess.BLOCK_ALL),
removalPolicy: RemovalPolicy.RETAIN,
});
}
this.artifactBucket = propsBucket;
// If a role has been provided, use it - otherwise, create a role.
const isRemoveRootPrincipal = FeatureFlags.of(this).isEnabled(cxapi.PIPELINE_REDUCE_CROSS_ACCOUNT_ACTION_ROLE_TRUST_SCOPE);
this.role = props.role || new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
roleName: isRemoveRootPrincipal ? PhysicalName.GENERATE_IF_NEEDED : undefined,
});
const isDefaultV2 = FeatureFlags.of(this).isEnabled(cxapi.CODEPIPELINE_DEFAULT_PIPELINE_TYPE_TO_V2);
if (!isDefaultV2 && props.pipelineType === undefined) {
Annotations.of(this).addWarningV2('@aws-cdk/aws-codepipeline:unspecifiedPipelineType', 'V1 pipeline type is implicitly selected when `pipelineType` is not set. If you want to use V2 type, set `PipelineType.V2`.');
}
this.pipelineType = props.pipelineType ?? (isDefaultV2 ? PipelineType.V2 : PipelineType.V1);
if (
props.executionMode
&& [ExecutionMode.QUEUED, ExecutionMode.PARALLEL].includes(props.executionMode)
&& this.pipelineType !== PipelineType.V2
) {
throw new ValidationError(`${props.executionMode} execution mode can only be used with V2 pipelines, \`PipelineType.V2\` must be specified for \`pipelineType\``, this);
}
this.codePipeline = new CfnPipeline(this, 'Resource', {
artifactStore: Lazy.any({ produce: () => this.renderArtifactStoreProperty() }),
artifactStores: Lazy.any({ produce: () => this.renderArtifactStoresProperty() }),
stages: Lazy.any({ produce: () => this.renderStages() }),
disableInboundStageTransitions: Lazy.any({ produce: () => this.renderDisabledTransitions() }, { omitEmptyArray: true }),
roleArn: this.role.roleArn,
restartExecutionOnUpdate: props && props.restartExecutionOnUpdate,
pipelineType: props.pipelineType ?? (isDefaultV2 ? PipelineType.V2 : undefined),
variables: Lazy.any({ produce: () => this.renderVariables() }, { omitEmptyArray: true }),
triggers: Lazy.any({ produce: () => this.renderTriggers() }, { omitEmptyArray: true }),
executionMode: props.executionMode,
name: this.physicalName,
});
// this will produce a DependsOn for both the role and the policy resources.
this.codePipeline.node.addDependency(this.role);
this.artifactBucket.grantReadWrite(this.role);
this.pipelineName = this.getResourceNameAttribute(this.codePipeline.ref);
this.pipelineVersion = this.codePipeline.attrVersion;
this.crossRegionBucketsPassed = !!props.crossRegionReplicationBuckets;
for (const [region, replicationBucket] of Object.entries(props.crossRegionReplicationBuckets || {})) {
this._crossRegionSupport[region] = {
replicationBucket,
stack: Stack.of(replicationBucket),
};
}
// Does not expose a Fn::GetAtt for the ARN so we'll have to make it ourselves
this.pipelineArn = Stack.of(this).formatArn({
service: 'codepipeline',
resource: this.pipelineName,
});
for (const stage of props.stages || []) {
this.addStage(stage);
}
for (const variable of props.variables || []) {
this.addVariable(variable);
}
for (const trigger of props.triggers || []) {
this.addTrigger(trigger);
}
this.node.addValidation({ validate: () => this.validatePipeline() });
}