diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index e33478256fb7f..1adbf52a6d17d 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -1,4 +1,49 @@ removed:@aws-cdk/aws-ec2.Port.toRuleJSON +change-return-type:@aws-cdk/aws-codebuild.LinuxBuildImage.fromAsset +removed:@aws-cdk/aws-codebuild.LinuxBuildImage.fromDockerHub +change-return-type:@aws-cdk/aws-codebuild.LinuxBuildImage.fromEcrRepository +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_OPEN_JDK_9 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.STANDARD_1_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.STANDARD_2_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_ANDROID_JAVA8_24_4_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_ANDROID_JAVA8_26_1_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_BASE +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_DOCKER_17_09_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_DOCKER_18_09_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_DOTNET_CORE_1_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_DOTNET_CORE_2_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_DOTNET_CORE_2_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_GOLANG_1_10 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_GOLANG_1_11 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_10_1_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_10_14_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_6_3_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_8_11_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_OPEN_JDK_11 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_OPEN_JDK_8 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PHP_5_6 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PHP_7_0 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PHP_7_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PYTHON_2_7_12 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PYTHON_3_3_6 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PYTHON_3_4_5 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PYTHON_3_5_2 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PYTHON_3_6_5 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_PYTHON_3_7_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_RUBY_2_2_5 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_RUBY_2_3_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_RUBY_2_5_1 +changed-type:@aws-cdk/aws-codebuild.LinuxBuildImage.UBUNTU_14_04_RUBY_2_5_3 +change-return-type:@aws-cdk/aws-codebuild.WindowsBuildImage.fromAsset +removed:@aws-cdk/aws-codebuild.WindowsBuildImage.fromDockerHub +change-return-type:@aws-cdk/aws-codebuild.WindowsBuildImage.fromEcrRepository +changed-type:@aws-cdk/aws-codebuild.WindowsBuildImage.WIN_SERVER_CORE_2016_BASE +change-return-type:@aws-cdk/aws-codebuild.Source.bitBucket +change-return-type:@aws-cdk/aws-codebuild.Source.codeCommit +change-return-type:@aws-cdk/aws-codebuild.Source.gitHub +change-return-type:@aws-cdk/aws-codebuild.Source.gitHubEnterprise +change-return-type:@aws-cdk/aws-codebuild.Source.s3 +change-return-type:@aws-cdk/aws-codebuild.Artifacts.s3 change-return-type:@aws-cdk/aws-codebuild.PipelineProject.addSecondaryArtifact change-return-type:@aws-cdk/aws-codebuild.Project.addSecondaryArtifact removed:@aws-cdk/aws-ec2.Connections.allowFromAnyIPv4 diff --git a/packages/@aws-cdk/aws-codebuild/README.md b/packages/@aws-cdk/aws-codebuild/README.md index 9b634febe5fc1..071bb226ce43e 100644 --- a/packages/@aws-cdk/aws-codebuild/README.md +++ b/packages/@aws-cdk/aws-codebuild/README.md @@ -186,8 +186,7 @@ of the constants such as `WindowsBuildImage.WIN_SERVER_CORE_2016_BASE` or Alternatively, you can specify a custom image using one of the static methods on `XxxBuildImage`: -* Use `.fromDockerHub(image)` to reference an image publicly available in Docker - Hub. +* Use `.fromDockerRegistry(image[, { secretsManagerCredentials }])` to reference an image in any public or private Docker registry. * Use `.fromEcrRepository(repo[, tag])` to reference an image available in an ECR repository. * Use `.fromAsset(directory)` to use an image created from a @@ -201,6 +200,10 @@ The following example shows how to define an image from an ECR repository: [ECR example](./test/integ.ecr.lit.ts) +The following example shows how to define an image from a private docker registry: + +[Docker Registry example](./test/integ.docker-registry.lit.ts) + ## Events CodeBuild projects can be used either as a source for events or be triggered diff --git a/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts b/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts index a751372821c00..10741cc33f72a 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/artifacts.ts @@ -53,7 +53,7 @@ export interface ArtifactsProps { * Artifacts definition for a CodeBuild Project. */ export abstract class Artifacts implements IArtifacts { - public static s3(props: S3ArtifactsProps): Artifacts { + public static s3(props: S3ArtifactsProps): IArtifacts { return new S3Artifacts(props); } diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index 2d44917c69eac..ab0d33a0fca9e 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -5,6 +5,7 @@ import { DockerImageAsset, DockerImageAssetProps } from '@aws-cdk/aws-ecr-assets import events = require('@aws-cdk/aws-events'); import iam = require('@aws-cdk/aws-iam'); import kms = require('@aws-cdk/aws-kms'); +import secretsmanager = require('@aws-cdk/aws-secretsmanager'); import { Aws, CfnResource, Construct, Duration, IResource, Lazy, PhysicalName, Resource, Stack } from '@aws-cdk/core'; import { IArtifacts } from './artifacts'; import { BuildSpec } from './build-spec'; @@ -797,9 +798,32 @@ export class Project extends ProjectBase { throw new Error("Invalid CodeBuild environment: " + errors.join('\n')); } + const imagePullPrincipalType = this.buildImage.imagePullPrincipalType === ImagePullPrincipalType.CODEBUILD + ? undefined + : ImagePullPrincipalType.SERVICE_ROLE; + if (this.buildImage.repository) { + if (imagePullPrincipalType === ImagePullPrincipalType.SERVICE_ROLE) { + this.buildImage.repository.grantPull(this); + } else { + const statement = new iam.PolicyStatement({ + principals: [new iam.ServicePrincipal('codebuild.amazonaws.com')], + actions: ['ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage', 'ecr:BatchCheckLayerAvailability'], + }); + statement.sid = 'CodeBuild'; + this.buildImage.repository.addToResourcePolicy(statement); + } + } + return { type: this.buildImage.type, image: this.buildImage.imageId, + imagePullCredentialsType: imagePullPrincipalType, + registryCredential: this.buildImage.secretsManagerCredentials + ? { + credentialProvider: 'SECRETS_MANAGER', + credential: this.buildImage.secretsManagerCredentials.secretArn, + } + : undefined, privilegedMode: env.privileged || false, computeType: env.computeType || this.buildImage.defaultComputeType, environmentVariables: !hasEnvironmentVars ? undefined : Object.keys(vars).map(name => ({ @@ -924,6 +948,25 @@ export enum ComputeType { LARGE = 'BUILD_GENERAL1_LARGE' } +/** + * The type of principal CodeBuild will use to pull your build Docker image. + */ +export enum ImagePullPrincipalType { + /** + * CODEBUILD specifies that CodeBuild uses its own identity when pulling the image. + * This means the resource policy of the ECR repository that hosts the image will be modified to trust + * CodeBuild's service principal. + * This is the required principal type when using CodeBuild's pre-defined images. + */ + CODEBUILD = 'CODEBUILD', + + /** + * SERVICE_ROLE specifies that AWS CodeBuild uses the project's role when pulling the image. + * The role will be granted pull permissions on the ECR repository hosting the image. + */ + SERVICE_ROLE = 'SERVICE_ROLE' +} + export interface BuildEnvironment { /** * The image used for the builds. @@ -982,6 +1025,27 @@ export interface IBuildImage { */ readonly defaultComputeType: ComputeType; + /** + * The type of principal that CodeBuild will use to pull this build Docker image. + * + * @default ImagePullPrincipalType.SERVICE_ROLE + */ + readonly imagePullPrincipalType?: ImagePullPrincipalType; + + /** + * The secretsManagerCredentials for access to a private registry. + * + * @default no credentials will be used + */ + readonly secretsManagerCredentials?: secretsmanager.ISecret; + + /** + * An optional ECR repository that the image is hosted in. + * + * @default no repository + */ + readonly repository?: ecr.IRepository; + /** * Allows the image a chance to validate whether the passed configuration is correct. * @@ -995,6 +1059,33 @@ export interface IBuildImage { runScriptBuildspec(entrypoint: string): BuildSpec; } +/** + * The options when creating a CodeBuild Docker build image + * using {@link LinuxBuildImage.fromDockerRegistry} + * or {@link WindowsBuildImage.fromDockerRegistry}. + */ +export interface DockerImageOptions { + /** + * The credentials, stored in Secrets Manager, + * used for accessing the repository holding the image, + * if the repository is private. + * + * @default no credentials will be used (we assume the repository is public) + */ + readonly secretsManagerCredentials?: secretsmanager.ISecret; +} + +/** + * Construction properties of {@link LinuxBuildImage}. + * Module-private, as the constructor of {@link LinuxBuildImage} is private. + */ +interface LinuxBuildImageProps { + readonly imageId: string; + readonly imagePullPrincipalType?: ImagePullPrincipalType; + readonly secretsManagerCredentials?: secretsmanager.ISecret; + readonly repository?: ecr.IRepository; +} + /** * A CodeBuild image running Linux. * @@ -1002,7 +1093,7 @@ export interface IBuildImage { * * You can also specify a custom image using one of the static methods: * - * - LinuxBuildImage.fromDockerHub(image) + * - LinuxBuildImage.fromDockerRegistry(image[, { secretsManagerCredentials }]) * - LinuxBuildImage.fromEcrRepository(repo[, tag]) * - LinuxBuildImage.fromAsset(parent, id, props) * @@ -1010,44 +1101,48 @@ export interface IBuildImage { * @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html */ export class LinuxBuildImage implements IBuildImage { - public static readonly STANDARD_1_0 = new LinuxBuildImage('aws/codebuild/standard:1.0'); - public static readonly STANDARD_2_0 = new LinuxBuildImage('aws/codebuild/standard:2.0'); - public static readonly UBUNTU_14_04_BASE = new LinuxBuildImage('aws/codebuild/ubuntu-base:14.04'); - public static readonly UBUNTU_14_04_ANDROID_JAVA8_24_4_1 = new LinuxBuildImage('aws/codebuild/android-java-8:24.4.1'); - public static readonly UBUNTU_14_04_ANDROID_JAVA8_26_1_1 = new LinuxBuildImage('aws/codebuild/android-java-8:26.1.1'); - public static readonly UBUNTU_14_04_DOCKER_17_09_0 = new LinuxBuildImage('aws/codebuild/docker:17.09.0'); - public static readonly UBUNTU_14_04_DOCKER_18_09_0 = new LinuxBuildImage('aws/codebuild/docker:18.09.0'); - public static readonly UBUNTU_14_04_GOLANG_1_10 = new LinuxBuildImage('aws/codebuild/golang:1.10'); - public static readonly UBUNTU_14_04_GOLANG_1_11 = new LinuxBuildImage('aws/codebuild/golang:1.11'); - public static readonly UBUNTU_14_04_OPEN_JDK_8 = new LinuxBuildImage('aws/codebuild/java:openjdk-8'); - public static readonly UBUNTU_14_04_OPEN_JDK_9 = new LinuxBuildImage('aws/codebuild/java:openjdk-9'); - public static readonly UBUNTU_14_04_OPEN_JDK_11 = new LinuxBuildImage('aws/codebuild/java:openjdk-11'); - public static readonly UBUNTU_14_04_NODEJS_10_14_1 = new LinuxBuildImage('aws/codebuild/nodejs:10.14.1'); - public static readonly UBUNTU_14_04_NODEJS_10_1_0 = new LinuxBuildImage('aws/codebuild/nodejs:10.1.0'); - public static readonly UBUNTU_14_04_NODEJS_8_11_0 = new LinuxBuildImage('aws/codebuild/nodejs:8.11.0'); - public static readonly UBUNTU_14_04_NODEJS_6_3_1 = new LinuxBuildImage('aws/codebuild/nodejs:6.3.1'); - public static readonly UBUNTU_14_04_PHP_5_6 = new LinuxBuildImage('aws/codebuild/php:5.6'); - public static readonly UBUNTU_14_04_PHP_7_0 = new LinuxBuildImage('aws/codebuild/php:7.0'); - public static readonly UBUNTU_14_04_PHP_7_1 = new LinuxBuildImage('aws/codebuild/php:7.1'); - public static readonly UBUNTU_14_04_PYTHON_3_7_1 = new LinuxBuildImage('aws/codebuild/python:3.7.1'); - public static readonly UBUNTU_14_04_PYTHON_3_6_5 = new LinuxBuildImage('aws/codebuild/python:3.6.5'); - public static readonly UBUNTU_14_04_PYTHON_3_5_2 = new LinuxBuildImage('aws/codebuild/python:3.5.2'); - public static readonly UBUNTU_14_04_PYTHON_3_4_5 = new LinuxBuildImage('aws/codebuild/python:3.4.5'); - public static readonly UBUNTU_14_04_PYTHON_3_3_6 = new LinuxBuildImage('aws/codebuild/python:3.3.6'); - public static readonly UBUNTU_14_04_PYTHON_2_7_12 = new LinuxBuildImage('aws/codebuild/python:2.7.12'); - public static readonly UBUNTU_14_04_RUBY_2_5_3 = new LinuxBuildImage('aws/codebuild/ruby:2.5.3'); - public static readonly UBUNTU_14_04_RUBY_2_5_1 = new LinuxBuildImage('aws/codebuild/ruby:2.5.1'); - public static readonly UBUNTU_14_04_RUBY_2_3_1 = new LinuxBuildImage('aws/codebuild/ruby:2.3.1'); - public static readonly UBUNTU_14_04_RUBY_2_2_5 = new LinuxBuildImage('aws/codebuild/ruby:2.2.5'); - public static readonly UBUNTU_14_04_DOTNET_CORE_1_1 = new LinuxBuildImage('aws/codebuild/dot-net:core-1'); - public static readonly UBUNTU_14_04_DOTNET_CORE_2_0 = new LinuxBuildImage('aws/codebuild/dot-net:core-2.0'); - public static readonly UBUNTU_14_04_DOTNET_CORE_2_1 = new LinuxBuildImage('aws/codebuild/dot-net:core-2.1'); + public static readonly STANDARD_1_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/standard:1.0'); + public static readonly STANDARD_2_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/standard:2.0'); + public static readonly UBUNTU_14_04_BASE = LinuxBuildImage.codeBuildImage('aws/codebuild/ubuntu-base:14.04'); + public static readonly UBUNTU_14_04_ANDROID_JAVA8_24_4_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/android-java-8:24.4.1'); + public static readonly UBUNTU_14_04_ANDROID_JAVA8_26_1_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/android-java-8:26.1.1'); + public static readonly UBUNTU_14_04_DOCKER_17_09_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/docker:17.09.0'); + public static readonly UBUNTU_14_04_DOCKER_18_09_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/docker:18.09.0'); + public static readonly UBUNTU_14_04_GOLANG_1_10 = LinuxBuildImage.codeBuildImage('aws/codebuild/golang:1.10'); + public static readonly UBUNTU_14_04_GOLANG_1_11 = LinuxBuildImage.codeBuildImage('aws/codebuild/golang:1.11'); + public static readonly UBUNTU_14_04_OPEN_JDK_8 = LinuxBuildImage.codeBuildImage('aws/codebuild/java:openjdk-8'); + public static readonly UBUNTU_14_04_OPEN_JDK_9 = LinuxBuildImage.codeBuildImage('aws/codebuild/java:openjdk-9'); + public static readonly UBUNTU_14_04_OPEN_JDK_11 = LinuxBuildImage.codeBuildImage('aws/codebuild/java:openjdk-11'); + public static readonly UBUNTU_14_04_NODEJS_10_14_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/nodejs:10.14.1'); + public static readonly UBUNTU_14_04_NODEJS_10_1_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/nodejs:10.1.0'); + public static readonly UBUNTU_14_04_NODEJS_8_11_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/nodejs:8.11.0'); + public static readonly UBUNTU_14_04_NODEJS_6_3_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/nodejs:6.3.1'); + public static readonly UBUNTU_14_04_PHP_5_6 = LinuxBuildImage.codeBuildImage('aws/codebuild/php:5.6'); + public static readonly UBUNTU_14_04_PHP_7_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/php:7.0'); + public static readonly UBUNTU_14_04_PHP_7_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/php:7.1'); + public static readonly UBUNTU_14_04_PYTHON_3_7_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/python:3.7.1'); + public static readonly UBUNTU_14_04_PYTHON_3_6_5 = LinuxBuildImage.codeBuildImage('aws/codebuild/python:3.6.5'); + public static readonly UBUNTU_14_04_PYTHON_3_5_2 = LinuxBuildImage.codeBuildImage('aws/codebuild/python:3.5.2'); + public static readonly UBUNTU_14_04_PYTHON_3_4_5 = LinuxBuildImage.codeBuildImage('aws/codebuild/python:3.4.5'); + public static readonly UBUNTU_14_04_PYTHON_3_3_6 = LinuxBuildImage.codeBuildImage('aws/codebuild/python:3.3.6'); + public static readonly UBUNTU_14_04_PYTHON_2_7_12 = LinuxBuildImage.codeBuildImage('aws/codebuild/python:2.7.12'); + public static readonly UBUNTU_14_04_RUBY_2_5_3 = LinuxBuildImage.codeBuildImage('aws/codebuild/ruby:2.5.3'); + public static readonly UBUNTU_14_04_RUBY_2_5_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/ruby:2.5.1'); + public static readonly UBUNTU_14_04_RUBY_2_3_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/ruby:2.3.1'); + public static readonly UBUNTU_14_04_RUBY_2_2_5 = LinuxBuildImage.codeBuildImage('aws/codebuild/ruby:2.2.5'); + public static readonly UBUNTU_14_04_DOTNET_CORE_1_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/dot-net:core-1'); + public static readonly UBUNTU_14_04_DOTNET_CORE_2_0 = LinuxBuildImage.codeBuildImage('aws/codebuild/dot-net:core-2.0'); + public static readonly UBUNTU_14_04_DOTNET_CORE_2_1 = LinuxBuildImage.codeBuildImage('aws/codebuild/dot-net:core-2.1'); /** * @returns a Linux build image from a Docker Hub image. */ - public static fromDockerHub(name: string): LinuxBuildImage { - return new LinuxBuildImage(name); + public static fromDockerRegistry(name: string, options: DockerImageOptions = {}): IBuildImage { + return new LinuxBuildImage({ + ...options, + imageId: name, + imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE, + }); } /** @@ -1061,30 +1156,45 @@ export class LinuxBuildImage implements IBuildImage { * @param repository The ECR repository * @param tag Image tag (default "latest") */ - public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): LinuxBuildImage { - const image = new LinuxBuildImage(repository.repositoryUriForTag(tag)); - repository.addToResourcePolicy(ecrAccessForCodeBuildService()); - return image; + public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): IBuildImage { + return new LinuxBuildImage({ + imageId: repository.repositoryUriForTag(tag), + imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE, + repository, + }); } /** * Uses an Docker image asset as a Linux build image. */ - public static fromAsset(scope: Construct, id: string, props: DockerImageAssetProps): LinuxBuildImage { + public static fromAsset(scope: Construct, id: string, props: DockerImageAssetProps): IBuildImage { const asset = new DockerImageAsset(scope, id, props); - const image = new LinuxBuildImage(asset.imageUri); - - // allow this codebuild to pull this image (CodeBuild doesn't use a role, so - // we can't use `asset.grantUseImage()`. - asset.repository.addToResourcePolicy(ecrAccessForCodeBuildService()); + return new LinuxBuildImage({ + imageId: asset.imageUri, + imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE, + repository: asset.repository, + }); + } - return image; + private static codeBuildImage(name: string): IBuildImage { + return new LinuxBuildImage({ + imageId: name, + imagePullPrincipalType: ImagePullPrincipalType.CODEBUILD, + }); } public readonly type = 'LINUX_CONTAINER'; public readonly defaultComputeType = ComputeType.SMALL; - - private constructor(public readonly imageId: string) { + public readonly imageId: string; + public readonly imagePullPrincipalType?: ImagePullPrincipalType; + public readonly secretsManagerCredentials?: secretsmanager.ISecret; + public readonly repository?: ecr.IRepository; + + private constructor(props: LinuxBuildImageProps) { + this.imageId = props.imageId; + this.imagePullPrincipalType = props.imagePullPrincipalType; + this.secretsManagerCredentials = props.secretsManagerCredentials; + this.repository = props.repository; } public validate(_: BuildEnvironment): string[] { @@ -1120,6 +1230,17 @@ export class LinuxBuildImage implements IBuildImage { } } +/** + * Construction properties of {@link WindowsBuildImage}. + * Module-private, as the constructor of {@link WindowsBuildImage} is private. + */ +interface WindowsBuildImageProps { + readonly imageId: string; + readonly imagePullPrincipalType?: ImagePullPrincipalType; + readonly secretsManagerCredentials?: secretsmanager.ISecret; + readonly repository?: ecr.IRepository; +} + /** * A CodeBuild image running Windows. * @@ -1127,20 +1248,27 @@ export class LinuxBuildImage implements IBuildImage { * * You can also specify a custom image using one of the static methods: * - * - WindowsBuildImage.fromDockerHub(image) + * - WindowsBuildImage.fromDockerRegistry(image[, { secretsManagerCredentials }]) * - WindowsBuildImage.fromEcrRepository(repo[, tag]) * - WindowsBuildImage.fromAsset(parent, id, props) * * @see https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html */ export class WindowsBuildImage implements IBuildImage { - public static readonly WIN_SERVER_CORE_2016_BASE = new WindowsBuildImage('aws/codebuild/windows-base:1.0'); + public static readonly WIN_SERVER_CORE_2016_BASE: IBuildImage = new WindowsBuildImage({ + imageId: 'aws/codebuild/windows-base:1.0', + imagePullPrincipalType: ImagePullPrincipalType.CODEBUILD, + }); /** * @returns a Windows build image from a Docker Hub image. */ - public static fromDockerHub(name: string): WindowsBuildImage { - return new WindowsBuildImage(name); + public static fromDockerRegistry(name: string, options: DockerImageOptions): IBuildImage { + return new WindowsBuildImage({ + ...options, + imageId: name, + imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE, + }); } /** @@ -1154,29 +1282,38 @@ export class WindowsBuildImage implements IBuildImage { * @param repository The ECR repository * @param tag Image tag (default "latest") */ - public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): WindowsBuildImage { - const image = new WindowsBuildImage(repository.repositoryUriForTag(tag)); - repository.addToResourcePolicy(ecrAccessForCodeBuildService()); - return image; + public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): IBuildImage { + return new WindowsBuildImage({ + imageId: repository.repositoryUriForTag(tag), + imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE, + repository, + }); } /** * Uses an Docker image asset as a Windows build image. */ - public static fromAsset(scope: Construct, id: string, props: DockerImageAssetProps): WindowsBuildImage { + public static fromAsset(scope: Construct, id: string, props: DockerImageAssetProps): IBuildImage { const asset = new DockerImageAsset(scope, id, props); - const image = new WindowsBuildImage(asset.imageUri); - - // allow this codebuild to pull this image (CodeBuild doesn't use a role, so - // we can't use `asset.grantUseImage()`. - asset.repository.addToResourcePolicy(ecrAccessForCodeBuildService()); - - return image; + return new WindowsBuildImage({ + imageId: asset.imageUri, + imagePullPrincipalType: ImagePullPrincipalType.SERVICE_ROLE, + repository: asset.repository, + }); } + public readonly type = 'WINDOWS_CONTAINER'; public readonly defaultComputeType = ComputeType.MEDIUM; - - private constructor(public readonly imageId: string) { + public readonly imageId: string; + public readonly imagePullPrincipalType?: ImagePullPrincipalType; + public readonly secretsManagerCredentials?: secretsmanager.ISecret; + public readonly repository?: ecr.IRepository; + + private constructor(props: WindowsBuildImageProps) { + this.imageId = props.imageId; + this.imagePullPrincipalType = props.imagePullPrincipalType; + this.secretsManagerCredentials = props.secretsManagerCredentials; + this.repository = props.repository; } public validate(buildEnvironment: BuildEnvironment): string[] { @@ -1238,12 +1375,3 @@ export enum BuildEnvironmentVariableType { */ PARAMETER_STORE = 'PARAMETER_STORE' } - -function ecrAccessForCodeBuildService(): iam.PolicyStatement { - const s = new iam.PolicyStatement({ - principals: [new iam.ServicePrincipal('codebuild.amazonaws.com')], - actions: ['ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage', 'ecr:BatchCheckLayerAvailability'], - }); - s.sid = 'CodeBuild'; - return s; -} diff --git a/packages/@aws-cdk/aws-codebuild/lib/source.ts b/packages/@aws-cdk/aws-codebuild/lib/source.ts index 1bc03d1cdd127..3cd171287b572 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/source.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/source.ts @@ -50,23 +50,23 @@ export interface SourceProps { * Source provider definition for a CodeBuild Project. */ export abstract class Source implements ISource { - public static s3(props: S3SourceProps): Source { + public static s3(props: S3SourceProps): ISource { return new S3Source(props); } - public static codeCommit(props: CodeCommitSourceProps): Source { + public static codeCommit(props: CodeCommitSourceProps): ISource { return new CodeCommitSource(props); } - public static gitHub(props: GitHubSourceProps): Source { + public static gitHub(props: GitHubSourceProps): ISource { return new GitHubSource(props); } - public static gitHubEnterprise(props: GitHubEnterpriseSourceProps): Source { + public static gitHubEnterprise(props: GitHubEnterpriseSourceProps): ISource { return new GitHubEnterpriseSource(props); } - public static bitBucket(props: BitBucketSourceProps): Source { + public static bitBucket(props: BitBucketSourceProps): ISource { return new BitBucketSource(props); } diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 00f927c63ffaf..53b1c87a824fc 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -90,6 +90,7 @@ "@aws-cdk/aws-kms": "^0.37.0", "@aws-cdk/aws-s3": "^0.37.0", "@aws-cdk/aws-s3-assets": "^0.37.0", + "@aws-cdk/aws-secretsmanager": "^0.37.0", "@aws-cdk/core": "^0.37.0" }, "homepage": "https://github.com/awslabs/aws-cdk", @@ -105,6 +106,7 @@ "@aws-cdk/aws-kms": "^0.37.0", "@aws-cdk/aws-s3": "^0.37.0", "@aws-cdk/aws-s3-assets": "^0.37.0", + "@aws-cdk/aws-secretsmanager": "^0.37.0", "@aws-cdk/core": "^0.37.0" }, "engines": { @@ -118,4 +120,4 @@ ] }, "stability": "stable" -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json index d4b6b0e10e84b..5a66818ed4c7f 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-asset.lit.expected.json @@ -39,33 +39,6 @@ ] } ] - }, - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability" - ], - "Effect": "Allow", - "Principal": { - "Service": { - "Fn::Join": [ - "", - [ - "codebuild.", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - } - }, - "Sid": "CodeBuild" - } - ], - "Version": "2012-10-17" } }, "DependsOn": [ @@ -262,6 +235,45 @@ "Properties": { "PolicyDocument": { "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ecr:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":repository/", + { + "Fn::GetAtt": [ + "MyImageAdoptRepository6CA902F6", + "RepositoryName" + ] + } + ] + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + }, { "Action": [ "logs:CreateLogGroup", @@ -439,6 +451,7 @@ ] ] }, + "ImagePullCredentialsType": "SERVICE_ROLE", "PrivilegedMode": false, "Type": "LINUX_CONTAINER" }, @@ -455,4 +468,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.expected.json new file mode 100644 index 0000000000000..b8cd00a66ffb2 --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.expected.json @@ -0,0 +1,148 @@ +{ + "Resources": { + "MyProjectRole9BBE5233": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "codebuild.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyProjectRoleDefaultPolicyB19B7C29": { + "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": "MyProject39F7B0AE" + } + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/codebuild/", + { + "Ref": "MyProject39F7B0AE" + }, + ":*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyProjectRoleDefaultPolicyB19B7C29", + "Roles": [ + { + "Ref": "MyProjectRole9BBE5233" + } + ] + } + }, + "MyProject39F7B0AE": { + "Type": "AWS::CodeBuild::Project", + "Properties": { + "Artifacts": { + "Type": "NO_ARTIFACTS" + }, + "Environment": { + "ComputeType": "BUILD_GENERAL1_SMALL", + "Image": "my-registry/my-repo", + "ImagePullCredentialsType": "SERVICE_ROLE", + "PrivilegedMode": false, + "RegistryCredential": { + "Credential": { + "Fn::Join": [ + "", + [ + "arn:aws:secretsmanager:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":secret:my-secrets-123456" + ] + ] + }, + "CredentialProvider": "SECRETS_MANAGER" + }, + "Type": "LINUX_CONTAINER" + }, + "ServiceRole": { + "Fn::GetAtt": [ + "MyProjectRole9BBE5233", + "Arn" + ] + }, + "Source": { + "BuildSpec": "{\n \"version\": \"0.2\",\n \"phases\": {\n \"build\": {\n \"commands\": [\n \"ls\"\n ]\n }\n }\n}", + "Type": "NO_SOURCE" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.ts b/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.ts new file mode 100644 index 0000000000000..69639cd196efe --- /dev/null +++ b/packages/@aws-cdk/aws-codebuild/test/integ.docker-registry.lit.ts @@ -0,0 +1,36 @@ +import secretsmanager = require('@aws-cdk/aws-secretsmanager'); +import cdk = require('@aws-cdk/core'); +import codebuild = require('../lib'); + +class TestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const secrets = secretsmanager.Secret.fromSecretArn(this, "MySecrets", + `arn:aws:secretsmanager:${this.region}:${this.account}:secret:my-secrets-123456`); + + new codebuild.Project(this, 'MyProject', { + buildSpec: codebuild.BuildSpec.fromObject({ + version: "0.2", + phases: { + build: { + commands: [ 'ls' ] + } + } + }), + /// !show + environment: { + buildImage: codebuild.LinuxBuildImage.fromDockerRegistry('my-registry/my-repo', { + secretsManagerCredentials: secrets, + }), + }, + /// !hide + }); + } +} + +const app = new cdk.App(); + +new TestStack(app, 'test-codebuild-docker-asset'); + +app.synth(); diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.expected.json index 5bac318649a12..ae939917ab996 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.ecr.lit.expected.json @@ -1,37 +1,8 @@ { "Resources": { "MyRepoF4F48043": { - "DeletionPolicy": "Retain", "Type": "AWS::ECR::Repository", - "Properties": { - "RepositoryPolicyText": { - "Statement": [ - { - "Action": [ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability" - ], - "Effect": "Allow", - "Principal": { - "Service": { - "Fn::Join": [ - "", - [ - "codebuild.", - { - "Ref": "AWS::URLSuffix" - } - ] - ] - } - }, - "Sid": "CodeBuild" - } - ], - "Version": "2012-10-17" - } - } + "DeletionPolicy": "Retain" }, "MyProjectRole9BBE5233": { "Type": "AWS::IAM::Role", @@ -65,6 +36,25 @@ "Properties": { "PolicyDocument": { "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyRepoF4F48043", + "Arn" + ] + } + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + }, { "Action": [ "logs:CreateLogGroup", @@ -186,6 +176,7 @@ ] ] }, + "ImagePullCredentialsType": "SERVICE_ROLE", "PrivilegedMode": false, "Type": "LINUX_CONTAINER" }, @@ -202,4 +193,4 @@ } } } -} \ No newline at end of file +}