I need to create a pipeline with CDK that will trigger a deploy in CloudFormation based on a branch in a CodeCommit repo. If the CodeCommit repo was in the same account as the pipeline, I would use something like:
const codecommitRepo = codecommit.Repository.fromRepositoryName(
this,
'AppRepository',
'REPO_NAME'
);
pipeline.addStage({
stageName: 'Source',
actions: [
new codepipeline_actions.CodeCommitSourceAction({
actionName: 'Source',
repository: codecommitRepo,
branch: 'BRANCH_NAME',
output: sourceArtifact,
}),
],
});
However, what if the CodeCommit repo is in a different account?
The architecture I'm trying to recreate is similar to the one shown in this article (image below), but with CDK and CloudFormation.
How can I create that?
If someone has the same problem, I managed to do it with CDK with this example. It's outdated but I applied the same logic. Probably some of the steps mentioned in this answer are not necesary because of recent changes. I found a more recent example here, but I haven't tried it yet.
Let's call ID_ACC_WITH_REPO to the AWS account ID with the CodeCommit repo and ID_ACC_WITH_PIPELINE to the account ID with the pipeline and where we want to deploy the architecture.
const repoAccStack = new cdk.Stack(app, 'RepoAccStack', {
env: {
account: ID_ACC_WITH_REPO,
region: REPO_REGION
}
});
// Create role
const crossAccRole = new iam.Role(repoAccStack, 'OtherAccRole', {
roleName: 'CrossAccountRole',
assumedBy: new iam.AccountPrincipal(pipelineAcc),
});
// Attach policies
const policy = new iam.PolicyStatement();
policy.addAllResources();
policy.addActions('s3:*', 'codecommit:*', 'kms:*');
crossAccRole.addToPolicy(policy);
const repo = codecommit.Repository.fromRepositoryArn(
repoAccStack,
'AppRepository',
`arn:aws:codecommit:${REPO_REGION}:${ID_ACC_WITH_REPO}:${REPO_NAME}`
);
const pipelineAccStack = new cdk.Stack(app, 'PipelineAccStack', {
env: {
account: ID_ACC_WITH_PIPELINE,
region: REGION_WITH_PIPELINE
}
});
EncryptionKey
used in the example is deprecated, use Key
instead.const key = new kms.Key(pipelineAccStack, 'CrossAccountKmsKey');
Actually I got a kms.model.MalformedPolicyDocumentException
error when trying to create the key, so I did it manually from the AWS Console and then imported it with kms.Key.fromKeyArn
. I probably did something wrong with my account (I got a lot of errors before getting to this solution), but if you get the same error it's a workaround. Just make sure of assigning usage permissions to the pipeline role.
HackyIdentity
used in the example is not necessary, several methods used in the class implementation are now deprecated.const artifactsBucket = new s3.Bucket(pipelineAccStack, "ArtifactsBucket", {
bucketName: BUCKET_NAME,
encryptionKey: key,
encryption: s3.BucketEncryption.KMS
});
artifactsBucket.grantReadWrite(new iam.ArnPrincipal(crossAccRole.roleArn));
// Create pipeline
const pipeline = new codepipeline.Pipeline(pipelineAccStack, 'Pipeline', {
pipelineName: 'CrossAccountPipeline',
artifactBucket: artifactsBucket
});
// Add cross-account role
const policy = new iam.PolicyStatement();
policy.addResources(crossAccRole.roleArn)
policy.addActions('s3:*', 'codecommit:*', 'kms:*');
pipeline.addToRolePolicy(policy);
// Create artifact for source code
const sourceArtifact = new codepipeline.Artifact();
// Create source stage with role
pipeline.addStage({
stageName: 'Source',
actions: [
new codepipeline_actions.CodeCommitSourceAction({
actionName: 'CodeCommit_Source',
repository: repo,
output: sourceArtifact,
branch: 'dev',
role: crossAccRole
})
]
});
// Create CodeBuild project
const buildProject = new codebuild.PipelineProject(this, 'Build', {
environment: { buildImage: codebuild.LinuxBuildImage.AMAZON_LINUX_2_2 }
});
// Create artifact for build
const buildArtifact = new codepipeline.Artifact();
// Add build stage
pipeline.addStage({
stageName: 'Build',
actions: [
new codepipeline_actions.CodeBuildAction({
actionName: 'Build',
project: buildProject,
input: sourceArtifact,
outputs: [buildArtifact],
}),
],
});
When the CDK app contains more than one stack you can't just cdk deploy
. This is explained in here. However if you try to cdk deploy '*'
there's another error: Need to perform AWS calls for account ACCOUNT_ID, but the current credentials are for ACCOUNT_ID
.
I managed to deploy the stacks with cdk deploy -e
and switching accounts with aws configure
.
There are three stacks, not two. An EventBusPolicy stack is automatically generated by CDK to create an event bus. The other two events are added (also automatically) by CDK in PipelineAccStack and RepoAccStack. Marcin's answer explain how cross-account events are configured. The EventBusPolicy stack should be created in ACC_WITH_PIPELINE. To get the exact name of the stack, use cdk list
.
Taking all of this into account, in this example I would deploy with:
# with aws configure in ACC_WITH_PIPELINE
cdk deploy -e "PipelineAccStack"
cdk deploy -e "EventBusPolicy-$ID_ACC_WITH_REPO-$REGION-$ID_ACC_WITH_PIPELINE"
# switch aws configure to ACC_WITH_REPO
cdk deploy -e "RepoAccStack"