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 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 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 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.
- A role to assume in the other account. (cross-account-role, configured on action level)
- 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 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 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 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)