DEV Community

Cover image for Cross account deployments using a Customer Managed KMS key
Joris Conijn for AWS Community Builders

Posted on • Originally published at xebia.com

Cross account deployments using a Customer Managed KMS key

In my previous blog I wrote about application pipelines. These CodePipeline use a S3 bucket. What if you have a need for a customer managed key instead of the Amazon managed key? And you want to deploy the CloudFormation templates cross account? In this blog I will explain how you can achieve this.

The key itself

Lets start with the key itself. When CodePipeline moves artifacts from one stage to another it uses S3 to transfer the data. For this reason we need to allow S3 and CodePipeline to decrypt and encrypt the data:

- Action: - kms:Decrypt - kms:DescribeKey - kms:Encrypt - kms:ReEncrypt* - kms:GenerateDataKey* Effect: Allow Principal: AWS: "*" Resource: "*" Condition: StringEquals: aws:PrincipalOrgID: !Ref OrgId kms:ViaService: - Fn::Sub: s3.${AWS::Region}.amazonaws.com - Fn::Sub: codepipeline.${AWS::Region}.amazonaws.com 
Enter fullscreen mode Exit fullscreen mode

Next, CloudFormation will deploy these templates across account. For this reason we need to allow CloudFormation to decrypt the data.

- Action: kms:Decrypt Effect: Allow Principal: AWS: "*" Resource: "*" Condition: StringEquals: aws:PrincipalOrgID: !Ref OrgId kms:ViaService: !Sub cloudformation.${AWS::Region}.amazonaws.com 
Enter fullscreen mode Exit fullscreen mode

The samples shown here will limit the use of the key to the AWS Services within your own AWS organization. The S3 bucket needs to use the KMS key for encryption.

The pipeline

The CodePipeline needs to know that we are using KMS as an encryption method. For this reason we need to tell what key to use.

Pipeline: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStores: - Region: eu-west-1 ArtifactStore: Location: !Sub codepipeline-artifacts-${AWS::AccountId}-eu-west-1 Type: S3 EncryptionKey: Type: KMS Id: alias/bucket-key - Region: eu-central-1 ArtifactStore: Location: !Sub codepipeline-artifacts-${AWS::AccountId}-eu-central-1 Type: S3 EncryptionKey: Type: KMS Id: alias/bucket-key 
Enter fullscreen mode Exit fullscreen mode

We are using the alias so that we do not have to deal with the key id.

Deploy cross account/region

You might have seen it already in the previous code snippet. When you deploy across region you need to configure a bucket and key per region. When you deploy across account you will need 2 roles.

  1. A role to assume in the other account. (cross-account-role, configured on action level)
  2. A role to use for the CloudFormation execution. (cloudformation-execution-role, configured in the configuration)
Actions: - Name: Ireland-CreateChangeSet Region: eu-west-1 RunOrder: 1 RoleArn: !Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role InputArtifacts: - Name: BuildArtifactAsZip ActionTypeId: Category: Deploy Owner: AWS Provider: CloudFormation Version: "1" Configuration: ActionMode: CHANGE_SET_REPLACE RoleArn: !Sub arn:aws:iam::${DevelopmentAccountId}:role/cloudformation-execution-role StackName: !Sub ${ProjectName}-$development ChangeSetName: !Sub ${ProjectName}-development-ChangeSet TemplatePath: BuildArtifactAsZip::packaged-template.yaml Capabilities: CAPABILITY_NAMED_IAM ParameterOverrides: | { "EnvType": "development" } - Name: Ireland-ExecuteChangeSet Region: eu-west-1 RunOrder: 2 RoleArn: !Sub arn:aws:iam::${DevelopmentAccountId}:role/cross-account-role ActionTypeId: Category: Deploy Owner: AWS Provider: CloudFormation Version: "1" Configuration: ActionMode: CHANGE_SET_EXECUTE RoleArn: !Sub arn:aws:iam::${DevelopmentAccountId}:role/cloudformation-execution-role StackName: !Sub ${ProjectName}-development ChangeSetName: !Sub ${ProjectName}-development-ChangeSet 
Enter fullscreen mode Exit fullscreen mode

The cross-account-role needs the ability to perform crud operations on CloudFormation. Have access to the templates and the ability to pass the cloudformation-execution-role. The BuildAccountId is the account id that holds the KMS keys, S3 buckets and pipeline. This role needs to be able to access the S3 buckets and use the KMS key for decryption.

CrossAccountRole: Type: AWS::IAM::Role Properties: RoleName: cross-account-role AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: sts:AssumeRole Effect: Allow Principal: AWS: !Sub arn:aws:iam::${BuildAccountId}:root Policies: - PolicyName: AllowCloudFormationDeployments PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: iam:PassRole Resource: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cloudformation-execution-role - Effect: Allow Action: cloudformation:DescribeStacks Resource: - !Sub arn:${AWS::Partition}:cloudformation:eu-west-1:${AWS::AccountId}:stack/* - !Sub arn:${AWS::Partition}:cloudformation:eu-central-1:${AWS::AccountId}:stack/* - Effect: Allow Action: - cloudformation:CreateStack - cloudformation:DeleteStack - cloudformation:UpdateStack - cloudformation:CreateChangeSet - cloudformation:ExecuteChangeSet - cloudformation:DeleteChangeSet - cloudformation:DescribeChangeSet - cloudformation:SetStackPolicy - cloudformation:SetStackPolicy - cloudformation:ValidateTemplate Resource: - !Sub arn:${AWS::Partition}:cloudformation:eu-west-1:${AWS::AccountId}:stack/* - !Sub arn:${AWS::Partition}:cloudformation:eu-central-1:${AWS::AccountId}:stack/* - PolicyName: AllowArtifactAccess PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - s3:GetObject - s3:GetObjectVersion Resource: - !Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${BuildAccountId}-eu-west-1/* - !Sub arn:${AWS::Partition}:s3:::codepipeline-artifacts-${BuildAccountId}-eu-central-1/* - Effect: Allow Action: kms:Decrypt Resource: - !Sub arn:${AWS::Partition}:kms:eu-west-1:${BuildAccountId}:key/02d730ed-125b-4d2e-a498-7b0187298924 - !Sub arn:${AWS::Partition}:kms:eu-central-1:${BuildAccountId}:key/e127b617-6951-42b7-83ae-12acc16d12a7 
Enter fullscreen mode Exit fullscreen mode

The execution role has all the needed rights to deploy the actual template:

CloudFormationServiceRole: Properties: RoleName: cloudformation-execution-role AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: sts:AssumeRole Effect: Allow Principal: { Service: cloudformation.amazonaws.com } ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess 
Enter fullscreen mode Exit fullscreen mode

Conclusion

When using customer managed keys you need some extra configuration. The key is self, configuration where is is used and some logical permissions. Getting all this from the AWS documentation can be challenging as it is somewhat of an edge case.

Top comments (0)