constructor()

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() });
  }