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)