Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CodeBuild S3 cache doesn't generate ImportValue #3554

Closed
1 task done
rafalwrzeszcz opened this issue Aug 6, 2019 · 1 comment · Fixed by #3569
Closed
1 task done

CodeBuild S3 cache doesn't generate ImportValue #3554

rafalwrzeszcz opened this issue Aug 6, 2019 · 1 comment · Fixed by #3569
Assignees
Labels
bug This issue is a bug.

Comments

@rafalwrzeszcz
Copy link

  • I'm submitting a ...

    • 🪲 bug report
  • What is the current behavior?
    I want to create a CodeBuild project that utilizes buckets from a different stack (I define buckets in InfrastructureStack and then have a CodeBuild project in FrontendStack). For all of the references to those buckets CDK generates ImportValue statements, except for Cache - it creates a Ref call with just a resource ID from another stack (same bucket is used in FrontendStack to generate IAM policy and there an ImportValue is generated).

My InfrastructureStack Kotlin source:

class Infrastructure(props: Props) : AppStack("InfrastructureStack") {

    val artifactsBucket: IBucket

    val cacheBucket: IBucket

    init {
        artifactsBucket = Bucket(
            this,
            "ArtifactsBucket",
            BucketProps.builder()
                .withBlockPublicAccess(BlockPublicAccess.BLOCK_ALL)
                .build()
        )

        cacheBucket = Bucket(
            this,
            "CacheBucket",
            BucketProps.builder()
                .withBlockPublicAccess(BlockPublicAccess.BLOCK_ALL)
                .build()
        )

My FrontendStack:

class EclCheckoutProject(parent: AppStack, id: String, props: Props) : AppStackConstruct(parent, id) {
    init {
        val buildRole = Role(
            this,
            "CheckoutCodebuildRole",
            RoleProps.builder()
                .withAssumedBy(ServicePrincipal("codebuild.amazonaws.com"))
                .withInlinePolicies(
                    mapOf(
                        "CodeBuildCheckout" to PolicyDocument().apply {
                            addStatements(
                                PolicyStatement().apply {
                                    effect = Effect.ALLOW
                                    addActions(
                                        "s3:PutObject"
                                    )
                                    addResources(
                                        props.artifactBucket.bucketArn,
                                        props.artifactBucket.bucketArn + "/*"
                                    )
                                },
                                PolicyStatement().apply {
                                    effect = Effect.ALLOW
                                    addActions(
                                        "s3:GetObject",
                                        "s3:GetObjectVersion",
                                        "s3:ListBucket",
                                        "s3:ListObjects",
                                        "s3:PutObject"
                                    )
                                    addResources(
                                        props.cacheBucket.bucketArn,
                                        props.cacheBucket.bucketArn + "/*"
                                    )
                                }
                            )
                        }
                    )
                )
                .build()
        )

        val codebuildProject = Project(
            this,
            "CheckoutProject",
            ProjectProps.builder()
                .withSource(
                    Source.gitHubEnterprise(
                        GitHubEnterpriseSourceProps.builder()
                            .withHttpsCloneUrl(props.static.githubRepoUrl)
                            .withCloneDepth(1)
                            .withReportBuildStatus(props.static.reportBuildStatic)
                            .withIgnoreSslErrors(false)
                            .build()
                    )
                )
                .withArtifacts(
                    Artifacts.s3(
                        S3ArtifactsProps.builder()
                            .withBucket(props.artifactBucket)
                            .withPath(props.static.artifactPath)
                            .withName(props.static.artifactFilename)
                            .withPackageZip(true)
                            .withEncryption(false)
                            .build()
                    )
                )
                .withCache(Cache.bucket(props.cacheBucket))
                .withEnvironment(
                    BuildEnvironment.builder()
                        .withComputeType(props.static.computeType)
                        .withBuildImage(props.static.buildImage)
                        .build()
                )
                .withBuildSpec(BuildSpec.fromSourceFilename(props.static.buildspecFilename))
                .withRole(buildRole)
                .withEnvironmentVariables(props.static.environmentVariables)
                .build()
        )

And this is a coupling part:

    val infrastructure = Infrastructure(Infrastructure.Props(config))

    Frontend(
        Frontend.Props(
            config = config,
            artifactsBucket = infrastructure.artifactsBucket,
            cacheBucket = infrastructure.cacheBucket
        )
    )

Note that there are two buckets, both created and passed the same way, and even the cacheBucket reference is inserted as import value into IAM policy, the artifactsBucket reference is inserted also as import value into artifacts store definition. But cache definition of CodeBuild is created as Ref:

    "CrowdDeliveryDevDeployProjectBuildCodebuildRoleDefaultPolicyFD72BE6D": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyDocument": {
          "Statement": [
            {
              "Action": [
                "s3:GetObject*",
                "s3:GetBucket*",
                "s3:List*",
                "s3:DeleteObject*",
                "s3:PutObject*",
                "s3:Abort*"
              ],
              "Effect": "Allow",
              "Resource": [
                {
                  "Fn::ImportValue": "InfrastructureStack:ExportsOutputFnGetAttCacheBucket41D9D0B0Arn077A2DAC"
                },
                {
                  "Fn::Join": [
                    "",
                    [
                      {
                        "Fn::ImportValue": "InfrastructureStack:ExportsOutputFnGetAttCacheBucket41D9D0B0Arn077A2DAC"
                      },
                      "/*"
                    ]
                  ]
                }
              ]
            },
            
            {
              "Action": [
                "s3:GetObject*",
                "s3:GetBucket*",
                "s3:List*",
                "s3:DeleteObject*",
                "s3:PutObject*",
                "s3:Abort*"
              ],
              "Effect": "Allow",
              "Resource": [
                {
                  "Fn::ImportValue": "InfrastructureStack:ExportsOutputFnGetAttArtifactsBucket2AAC5544ArnD57FAE19"
                },
                { 
                  "Fn::Join": [
                    "",
                    [
                      {
                        "Fn::ImportValue": "InfrastructureStack:ExportsOutputFnGetAttArtifactsBucket2AAC5544ArnD57FAE19"
                      },
                      "/*"
                    ]
                  ]
                }
 
    "CrowdDeliveryDevDeployProjectPipelineProjectD9CCDF14": {
      "Type": "AWS::CodeBuild::Project",
      "Properties": {
        "Artifacts": {
          "Type": "CODEPIPELINE"
        },
        "Environment": {
          "ComputeType": "BUILD_GENERAL1_SMALL",
          "Image": "aws/codebuild/standard:2.0",
          "PrivilegedMode": false,
          "Type": "LINUX_CONTAINER"
        },
        "ServiceRole": {
          "Fn::GetAtt": [
            "CrowdDeliveryDevDeployProjectBuildCodebuildRole97FB2410",
            "Arn"
          ]
        },
        "Source": {
          "BuildSpec": "buildspec_deploy.yml",
          "Type": "CODEPIPELINE"
        },
        "Cache": {
          "Location": {
            "Fn::Join": [
              "/",
              [
                {
                  "Ref": "CacheBucket41D9D0B0"
                },
                {
                  "Ref": "AWS::NoValue"
                }
              ]
            ]
          }, 
          "Type": "S3"
        }
      },
      "Metadata": {
        "aws:cdk:path": "FrontendStack/CrowdDeliveryDevDeployProject/PipelineProject/Resource"
      }
    } 

I even tried to create a Bucket reference using Bucket.fromBucketArn, but this is what is generated:

        "Cache": {
          "Location": {
            "Fn::Join": [
              "/",
              [
                {
                  "Fn::Select": [
                    0,
                    {
                      "Fn::Split": [
                        "/",
                        {
                          "Fn::Select": [
                            5,
                            {
                              "Fn::Split": [
                                ":",
                                {
                                  "Fn::GetAtt": [
                                    "CacheBucket41D9D0B0",
                                    "Arn"
                                  ]
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                },
                { 
                  "Ref": "AWS::NoValue"
                }
              ]
            ]
          },
          "Type": "S3"
        }
  • What is the expected behavior (or behavior of feature suggested)?
    S3 cache for CodeBuild will contain cross-stack reference not a resource ID reference.

  • Please tell us about your environment:

    • CDK CLI Version: 1.3.0.DEVPREVIEW
    • Module Version: 1.3.0.DEVPREVIEW
    • OS: Debian
    • Language: Java
@rafalwrzeszcz rafalwrzeszcz added the needs-triage This issue or PR still needs to be triaged. label Aug 6, 2019
@skinny85 skinny85 added bug This issue is a bug. and removed needs-triage This issue or PR still needs to be triaged. labels Aug 6, 2019
@skinny85
Copy link
Contributor

skinny85 commented Aug 6, 2019

Thanks for opening the issue @rafalwrzeszcz . I can confirm I was able to reproduce the problem. Here's a minimal repro:

import { App, Construct, Stack, StackProps } from '@aws-cdk/core';
import codebuild = require('@aws-cdk/aws-codebuild');
import s3 = require('@aws-cdk/aws-s3');

class Infrastructure extends Stack {
    public readonly cacheBucket: s3.IBucket;

    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);

        this.cacheBucket = new s3.Bucket(this, 'CacheBucket', {
            blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
        });
    }
}

export interface FrontendProps extends StackProps {
    readonly cacheBucket: s3.IBucket;
}

class Frontend extends Stack {
    constructor(scope: Construct, id: string, props: FrontendProps) {
        super(scope, id, props);

        new codebuild.PipelineProject(this, 'CheckoutProject', {
            cache: codebuild.Cache.bucket(props.cacheBucket),
        });
    }
}

const app = new App();

const infrastructure = new Infrastructure(app, 'InfrastructureStack');
new Frontend(app, 'Frontend', {
    cacheBucket: infrastructure.cacheBucket,
});

app.synth();

The relevant part of the Frontend template:

    "CheckoutProjectB8572A47": {
      "Type": "AWS::CodeBuild::Project",
      "Properties": {
        "Artifacts": {
          "Type": "CODEPIPELINE"
        },
        "Environment": {
          "ComputeType": "BUILD_GENERAL1_SMALL",
          "Image": "aws/codebuild/standard:1.0",
          "PrivilegedMode": false,
          "Type": "LINUX_CONTAINER"
        },
        "ServiceRole": {
          "Fn::GetAtt": [
            "CheckoutProjectRole5E13DE3D",
            "Arn"
          ]
        },
        "Source": {
          "Type": "CODEPIPELINE"
        },
        "Cache": {
          "Location": {
            "Fn::Join": [
              "/",
              [
                {
                  "Ref": "CacheBucket41D9D0B0" // <--- this should be "Fn::ImportValue": "..."
                },
                {
                  "Ref": "AWS::NoValue"
                }
              ]
            ]
          },
          "Type": "S3"
        }
      },

@rix0rrr rix0rrr self-assigned this Aug 7, 2019
rix0rrr added a commit that referenced this issue Aug 7, 2019
References used in Fn.join() will now be properly substituted if
they're cross-stack references.

Fixes #3554.
@mergify mergify bot closed this as completed in #3569 Aug 7, 2019
mergify bot pushed a commit that referenced this issue Aug 7, 2019
References used in Fn.join() will now be properly substituted if
they're cross-stack references.

Fixes #3554.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants