question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[aws-codepipeline] CodePipeline with Cross Account Deployment

See original GitHub issue

❓ General Issue

We are creating a CodePipeline with CDK same as described in the official documentation. We have 2 stages which are Alpha and Prod where the resources of Alpha are created in developer account and Prod resources are created in Prod account which is a separate account.

The Question

I can successfully spin up a CodePipeline with including stages like;

Source --> (Lambda_Build & CFN_Build) --> Deploy_Alpha --> Deploy_Beta.

Now the problem is I couldn’t manage this CodePipeline to deploy in our Prod account which is another action right after Deploy_Beta stage. I also followed API Reference documentation which shows two alternative ways for dealing cross account deployments.

When I set account property for CloudFormationCreateUpdateStackAction by giving Prod account id I am getting an error. Here is the CloudFormationCreateUpdateStackAction including account parameter and the error stack trace.

StageProps.builder()
    .stageName("Deploy_Prod")
    .actions(
        Arrays.asList(
            CloudFormationCreateUpdateStackAction.Builder.create()
                .adminPermissions(true)
                .account("123123123123") // Prod account id
                .actionName("Lambda_CFN_Prod_Deploy")
                .templatePath(cdkBuildOutput.atPath("lambdaStackProd.template.json"))
                .parameterOverrides(envToCfnCodeParametersCodeMap.get("prod").assign(lambdaBuildOutput.getS3Location()))
                .extraInputs(Arrays.asList(lambdaBuildOutput))
                .stackName("lambdaStackProd")
                .build()
        )
    )
    .build()
Exception in thread "main" software.amazon.jsii.JsiiException: Pipeline stack which uses cross-environment actions must have an explicitly set account
Error: Pipeline stack which uses cross-environment actions must have an explicitly set account

On the other hand, if I assign LamdaStack instances to a variable in my Main class and pass LambdaStack instance for prod into LambdaPipelineStack then I am getting circular dependency error while creating IRole.

IRole crossAccountAssumeRole = new Role(otherAccountStack, "CDKPipelineRole",
        RoleProps.builder()
                .assumedBy(new AccountPrincipal("214839824702")) // developer account
                .roleName("CDKPipelineRole")
                .build());

And this is the circular dependency error I am getting;

Exception in thread "main" software.amazon.jsii.JsiiException: 'lambdaStackPipeline' depends on 'lambdaStackProd' (dependency added using stack.addDependency()). Adding this dependency (lambdaStackProd -> lambdaStackPipeline/lambdaPipeline/Deploy_Prod/Lambda_CFN_Prod_Deploy/Role/Resource.Arn) would create a cyclic reference.
Error: 'lambdaStackPipeline' depends on 'lambdaStackProd' (dependency added using stack.addDependency()). Adding this dependency (lambdaStackProd -> lambdaStackPipeline/lambdaPipeline/Deploy_Prod/Lambda_CFN_Prod_Deploy/Role/Resource.Arn) would create a cyclic reference.

We also created an Assume Role from Prod account and added Dev account as Trusted entity with Role Name CDKPipelineRole.

Please let me know if you need more information from my side. I really appreciate for your help. Thanks!

Environment

  • CDK CLI Version: 1.52.0
  • Module Version:
  • Node.js Version: v14.5.0
  • OS: OSX Catalina
  • Language (Version): Java (8)

Other information

Here is my Main class to create LambdaStacks and LambdaPipelineStack

LambdaApiMain.java

public final class LambdaApiMain {
    public static void main(final String args[]) {
        final App app = new App();

        Map<String, CfnParametersCode> envToCfnParameterCodeMap = new HashMap<>();
        envToCfnParameterCodeMap.put("alfa", CfnParametersCode.fromCfnParameters());
        envToCfnParameterCodeMap.put("prod", CfnParametersCode.fromCfnParameters());

        new LambdaStack(app, "LambdaStackAlfa", null, envToCfnParameterCodeMap.get("alfa"));
        new LambdaStack(app, "LambdaStackProd", null, envToCfnParameterCodeMap.get("prod"));

        new LambdaPipelineStack(app, "LambdaStackPipeline", envToCfnParameterCodeMap, "lambda-api");

        app.synth();
    }
}

LambdaPipelineStack.java

public class LambdaPipelineStack extends Stack {

    public LambdaPipelineStack(App app,
                                   String id,
                                   Map<String, CfnParametersCode> envToCfnCodeParametersCodeMap,
                                   String repoName) {
        this(app, id, null, envToCfnCodeParametersCodeMap, repoName);
    }

    public LambdaPipelineStack(App app,
                                   String id,
                                   StackProps stackProps,
                                   Map<String, CfnParametersCode> envToCfnCodeParametersCodeMap,
                                   String repoName) {
        super(app, id, stackProps);

        IRepository repository = Repository.fromRepositoryName(this, repoName, repoName);
        IBucket bucket = Bucket.fromBucketName(this, "api-codepipeline", "api-codepipeline");

        PipelineProject lambdaBuild = PipelineProject.Builder.create(this, "LambdaApiBuild")
            .buildSpec(BuildSpec.fromSourceFilename("lambda-buildspec.yml"))
            .environment(BuildEnvironment.builder().buildImage(LinuxBuildImage.STANDARD_3_0).build())
            .build();

        PipelineProject cdkBuild = PipelineProject.Builder.create(this, "LambdaApiCDKBuild")
            .buildSpec(BuildSpec.fromSourceFilename("cdk-buildspec.yml"))
            .environment(BuildEnvironment.builder().buildImage(LinuxBuildImage.STANDARD_3_0).build())
            .build();

        Artifact sourceOutput = new Artifact();
        Artifact cdkBuildOutput = new Artifact("CdkBuildOutput");
        Artifact lambdaBuildOutput = new Artifact("LambdaBuildOutput");

        Pipeline.Builder.create(this, "LambdaApiPipeline")
            .stages(Arrays.asList(
                StageProps.builder()
                    .stageName("Source")
                    .actions(
                        Arrays.asList(
                            CodeCommitSourceAction.Builder.create()
                                .actionName("Source")
                                .repository(repository)
                                .output(sourceOutput)
                                .build()
                        )
                    )
                    .build(),
                StageProps.builder()
                    .stageName("Build")
                    .actions(
                        Arrays.asList(
                            CodeBuildAction.Builder.create()
                                .actionName("Lambda_Build")
                                .project(lambdaBuild)
                                .input(sourceOutput)
                                .outputs(Arrays.asList(lambdaBuildOutput))
                                .build(),
                            CodeBuildAction.Builder.create()
                                .actionName("CDK_Build")
                                .project(cdkBuild)
                                .input(sourceOutput)
                                .outputs(Arrays.asList(cdkBuildOutput))
                                .build()
                        )
                    )
                    .build(),
                StageProps.builder()
                    .stageName("Deploy_Alpha")
                    .actions(
                        Arrays.asList(
                            CloudFormationCreateUpdateStackAction.Builder.create()
                                .actionName("Lambda_CFN_Alpha_Deploy")
                                .templatePath(cdkBuildOutput.atPath("lambdaStackAlfa.template.json"))
                                .adminPermissions(true)
                                .parameterOverrides(envToCfnCodeParametersCodeMap.get("alfa").assign(lambdaBuildOutput.getS3Location()))
                                .extraInputs(Arrays.asList(lambdaBuildOutput))
                                .stackName("lambdaStackAlfa")
                                .build()
                        )
                    )
                    .build(),
                StageProps.builder()
                    .stageName("Deploy_Prod")
                    .actions(
                        Arrays.asList(
                            CloudFormationCreateUpdateStackAction.Builder.create()
                                .adminPermissions(true)
                                .account("123123123123") // Prod account id
                                .actionName("Lambda_CFN_Prod_Deploy")
                                .templatePath(cdkBuildOutput.atPath("lambdaStackProd.template.json"))
                                .parameterOverrides(envToCfnCodeParametersCodeMap.get("prod").assign(lambdaBuildOutput.getS3Location()))
                                .extraInputs(Arrays.asList(lambdaBuildOutput))
                                .stackName("lambdaStackProd")
                                .build()
                        )
                    )
                    .build()
                 )
            )
            .pipelineName("lambdaPipeline")
            .artifactBucket(bucket)
            .restartExecutionOnUpdate(true)
            .build();
    }
}

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
skinny85commented, Jul 27, 2020

Hi @skinny85 I both tried with creating a bucket manually including a KMS or letting the pipeline to create their own. And it worked like a charm!

I’m glad 🙂.

I have two questions about following the best practices here;

  • What would be the preferred way to manage buckets if we will have multiple pipelines? Creating one main bucket or letting pipelines to create their own?

I would have one Bucket per CodePipeline (whether created by the CodePipeline construct, or explicitly by you, doesn’t really matter).

  • When deploying the stacks I see that cdk creates a support stack for the other account and we have to deploy it first. Then we need to deploy the main pipeline stack. Is there a way to combine these two operations into one? And is it also possible to make PipelineStack to handle this internally?

Unfortunately, it’s not possible. The CodePipeline needs resources created in the other account to be able to deploy to it.

However, look into our new CDK Pipelines module - it takes a different approach with the support stacks (it asks you to create them explicitly using cdk bootstrap).

I’ll resolve this issue, let me know if you need anything else from our side here.

Thanks, Adam

0reactions
flexelemcommented, Jul 25, 2020

Hi @skinny85 I both tried with creating a bucket manually including a KMS or letting the pipeline to create their own. And it worked like a charm! I have two questions about following the best practices here;

  • What would be the preferred way to manage buckets if we will have multiple pipelines? Creating one main bucket or letting pipelines to create their own?

  • When deploying the stacks I see that cdk creates a support stack for the other account and we have to deploy it first. Then we need to deploy the main pipeline stack. Is there a way to combine these two operations into one? And is it also possible to make PipelineStack to handle this internally?

Thanks a lot again! I really appreciate for your help

Read more comments on GitHub >

github_iconTop Results From Across the Web

Create a pipeline in CodePipeline that uses resources from ...
In Define Key Usage Permissions, under This Account, select the name of the service role for the pipeline (for example, CodePipeline_Service_Role). Under Other ......
Read more >
Deploy artifacts to Amazon S3 in different accounts using ...
CodePipeline uses the cross account role (prods3role) to access the KMS key and artifact bucket in the development account. Then, CodePipeline ...
Read more >
Building a Secure Cross-Account Continuous Delivery Pipeline
However, keeping a centralized account to orchestrate continuous delivery and deployment using AWS CodePipeline and AWS CodeBuild eliminates the ...
Read more >
Building a CI/CD pipeline for cross-account deployment of an ...
In this post, we showed how to integrate AWS developer tools such as CodeCommit, CodePipeline, and CodeBuild with the Serverless framework to ...
Read more >
Building a Cross-Region/Cross-Account Code Deployment ...
In this post, I will show you how you can easily build an automated cross-region code deployment solution using AWS CodePipeline (a ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found