packages/aws-cdk-lib/aws-codepipeline-actions/lib/ecr/build-and-publish-action.ts (109 lines of code) (raw):

import { Construct } from 'constructs'; import * as codepipeline from '../../../aws-codepipeline'; import * as ecr from '../../../aws-ecr'; import * as iam from '../../../aws-iam'; import * as cdk from '../../../core'; import { Action } from '../action'; /** * The CodePipeline variables emitted by the ECR build and publish Action. */ export interface EcrBuildAndPublishVariables { /** * The sha256 digest of the image manifest. */ readonly ecrImageDigestId: string; /** * The name of the Amazon ECR repository where the image was pushed. */ readonly ecrRepositoryName: string; } /** * The type of registry to use for the EcrBuildAndPublish action. */ export enum RegistryType { /** * Private registry */ PRIVATE = 'private', /** * Public registry */ PUBLIC = 'public', } /** * Construction properties of the `EcrBuildAndPublishAction`. */ export interface EcrBuildAndPublishActionProps extends codepipeline.CommonAwsActionProps { /** * The name of the ECR repository where the image is pushed. */ readonly repositoryName: string; /** * The directory path of Dockerfile used to build the image. * * Optionally, you can provide an alternate directory path if Dockerfile is not at the root level. * * @default - the source repository root level */ readonly dockerfileDirectoryPath?: string; /** * The tags used for the image. * * @default - latest */ readonly imageTags?: string[]; /** * Specifies whether the repository is public or private. * * @default - RegistryType.PRIVATE */ readonly registryType?: RegistryType; /** * The artifact produced by the source action that contains the Dockerfile needed to build the image. */ readonly input: codepipeline.Artifact; } /** * CodePipeline build action that uses AWS EcrBuildAndPublish. */ export class EcrBuildAndPublishAction extends Action { private readonly props: EcrBuildAndPublishActionProps; constructor(props: EcrBuildAndPublishActionProps) { super({ ...props, category: codepipeline.ActionCategory.BUILD, provider: 'ECRBuildAndPublish', artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, inputs: [props.input], }); this.props = props; } /** The variables emitted by this action. */ public get variables(): EcrBuildAndPublishVariables { return { ecrImageDigestId: this.variableExpression('ECRImageDigestId'), ecrRepositoryName: this.variableExpression('ECRRepositoryName'), }; } protected bound(scope: Construct, stage: codepipeline.IStage, options: codepipeline.ActionBindOptions): codepipeline.ActionConfig { // see: https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECRBuildAndPublish.html#edit-role-ECRBuildAndPublish if (this.props.registryType === RegistryType.PUBLIC) { // Public registry const repositoryArn = cdk.Stack.of(scope).formatArn({ service: 'ecr-public', resource: 'repository', resourceName: this.props.repositoryName, region: '', }); options.role.addToPrincipalPolicy(new iam.PolicyStatement({ resources: [repositoryArn], actions: [ 'ecr-public:DescribeRepositories', 'ecr-public:InitiateLayerUpload', 'ecr-public:UploadLayerPart', 'ecr-public:CompleteLayerUpload', 'ecr-public:PutImage', 'ecr-public:BatchCheckLayerAvailability', ], })); ecr.PublicGalleryAuthorizationToken.grantRead(options.role); } else { // Private registry const repositoryArn = cdk.Stack.of(scope).formatArn({ service: 'ecr', resource: 'repository', resourceName: this.props.repositoryName, region: cdk.Stack.of(scope).region, }); options.role.addToPrincipalPolicy(new iam.PolicyStatement({ resources: [repositoryArn], actions: [ 'ecr:DescribeRepositories', 'ecr:InitiateLayerUpload', 'ecr:UploadLayerPart', 'ecr:CompleteLayerUpload', 'ecr:PutImage', 'ecr:GetDownloadUrlForLayer', 'ecr:BatchCheckLayerAvailability', ], })); ecr.AuthorizationToken.grantRead(options.role); } const logGroupArn = cdk.Stack.of(scope).formatArn({ service: 'logs', resource: 'log-group', resourceName: `/aws/codepipeline/${stage.pipeline.pipelineName}`, arnFormat: cdk.ArnFormat.COLON_RESOURCE_NAME, }); const logGroupArnWithWildcard = `${logGroupArn}:*`; options.role.addToPrincipalPolicy(new iam.PolicyStatement({ resources: [logGroupArn, logGroupArnWithWildcard], actions: [ 'logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents', ], })); // allow the Role access to the Bucket, if there are any inputs if ((this.actionProperties.inputs ?? []).length > 0) { options.bucket.grantRead(options.role); } return { configuration: { ECRRepositoryName: this.props.repositoryName, DockerFilePath: this.props.dockerfileDirectoryPath, ImageTags: this.props.imageTags !== undefined ? this.props.imageTags.join(',') : undefined, RegistryType: this.props.registryType, }, }; } }