Skip to content

Commit

Permalink
Commit of codepipeline cross account deployment example
Browse files Browse the repository at this point in the history
  • Loading branch information
adcreare committed Jan 28, 2018
1 parent 857db53 commit f35fec1
Show file tree
Hide file tree
Showing 6 changed files with 500 additions and 0 deletions.
98 changes: 98 additions & 0 deletions code-pipeline-cross-account/README.md
@@ -0,0 +1,98 @@
# AWS Code Pipeline to Cross Account Deployments
---
AWS Code Pipeline is a powerful CI/DC tool that can perform deployments across AWS accounts.
Sadly however the current available documentation/examples for doing this are complete rubbish.

This is half a reference to myself on getting this working and a resource for others attempting to do this.
![Diagram showing proposed architecture](doc-images/diagram.png)
In this example we have 1 account that runs the pipeline, gets the code, performs the build. The another account that we deploy into. In this example we only have one account, but fairly easily this could be expanded to many accounts.

---
### Getting this working

At time of writing there is **ZERO** support in the AWS Console for doing this with code pipeline.
Options are CLI and cloudformation, here I'm using yml based cloudformation templates.

*warning, in some places I've used overly board permissions (ie admin * * ) which is bad! Please tune these to match the services your using and deploying to.*

The templates are broken into two folders and should be deployed in the older listed here
#### 1. Deploy Account:
contains templates that need to be used in the following order
1. *kms-cross-account.yml* - creates the KMS key and allows access from the Production account
2. *code-build.yml* - sets up the codebuild job and exports the name via CFN export - you will need to customise this to your application build
3. *code-pipeline.yml* - Sets up the code pipeline job itself (it won't work yet however)

#### 2. Production Account: (actually all and any other accounts we want to deploy into)
1. *iam-codepipeline-cross-account-deploy.yml* - Sets up two IAM roles, one for codepipleine to access from the deploy account and another thats used to run the cloudformation deploy.

#### 3 One more thing
Yes I said its all CF. Well its mostly CF, because this bit won't work with CF unless I create you a new bucket and well that's probably not ideal.

You need to add a cross account bucket policy to your S3 bucket (the artifact store).

The JSON below shows that bucket policy, Replace **YOURBUCKETNAMEHERE** with your artifact bucket name and **ProductionAccountNumber** with the number of your Production account.

Obviously if you had multiple accounts that you were going to deploy into, make sure each one of them is represented here.

```JSON
{
"Version": "2012-10-17",
"Id": "SSEAndSSLPolicy",
"Statement": [
{
"Sid": "DenyUnEncryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::YOURBUCKETNAMEHERE/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
},
{
"Sid": "DenyInsecureConnections",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::YOURBUCKETNAMEHERE/*",
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ProductionAccountNumber:root"
},
"Action": [
"s3:Get*",
"s3:Put*"
],
"Resource": "arn:aws:s3:::YOURBUCKETNAMEHERE/*"
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ProductionAccountNumber:root"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::YOURBUCKETNAMEHERE"
}
]
}
```

---

### Other resources
At the time of writing 2018-01-27 I'd found the following resources from AWS:

* https://github.com/awslabs/aws-refarch-cross-account-pipeline - This is actually helpful except there are errors in their templates and it won't deploy (I found issues with their KMS template specifically). Some of the templates also treat the account id as a number, which is great except when your acc id starts with a 0 like several of my accounts do! Its also overly complicated, its a good reference. I do wish they had a better diagram for how the roles worked, because that's the hard part.

* https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-create-cross-account.html - The official documentation. Its SO dense, no diagrams, I don't consider myself a beginner and frankly this document wasn't readable by me.
82 changes: 82 additions & 0 deletions code-pipeline-cross-account/deploy-account/code-build.yml
@@ -0,0 +1,82 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: "code-build-services"

Parameters:
DockerImage:
Type: String
Description: The docker repo path to build image
Default: node:6

KMSKeyArn:
Description: ARN of the KMS key
Type: String
Default: 'arn:aws:kms:us-east-1:011111122222:key/606723ff-6e07-4f7a-b3b8-7e5b51aa2a28'


Resources:
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Ref AWS::StackName
ServiceRole: !Ref CodeBuildRole
EncryptionKey: !Ref KMSKeyArn
Source:
Type: CODEPIPELINE
BuildSpec:
!Sub |
version: 0.2
phases:
install:
commands:
- npm -g install yarn
- 'echo "//registry.npmjs.org/:_authToken=${!NPM_TOKEN}" > ~/.npmrc'
pre_build:
commands:
- yarn
build:
commands:
- yarn build
artifacts:
files:
- build/*
discard-paths: yes

Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: !Ref DockerImage

TimeoutInMinutes: 15


CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: ['sts:AssumeRole']
Effect: Allow
Principal:
Service: [codebuild.amazonaws.com]
Version: '2012-10-17'
Path: /
Policies:
- PolicyName: CodeBuildAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- '*'
Effect: Allow
Resource: '*'


Outputs:

CodeBuild:
Description: "FN export value for other "
Value: !Ref AWS::StackName
Export:
Name: 'code-build-xaccount-export'
157 changes: 157 additions & 0 deletions code-pipeline-cross-account/deploy-account/code-pipeline.yml
@@ -0,0 +1,157 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: "Code pipeline Example Cross Account"

Parameters:

RepoName:
Description: "Name of the repo to clone"
Type: String
Default: 'Test2'

CodePipeLineBucket:
Description: "The S3 bucket to use for codepipeline - bucket must already exist - example: codepipeline-us-east-1-773922028333"
Type: String
Default: 'codepipeline-us-east-1-011111122222'

BranchName:
Description: "The git branch to operate from"
Type: String
Default: 'master'

ProdAccNumber:
Description: "The account number of the account to perform the deployment into"
Type: String
Default: '33333322222'

KMSKeyArn:
Description: ARN of the KMS key to use
Type: String
Default: 'arn:aws:kms:us-east-1:011111122222:key/606723cf-6e08-4f7a-b4b8-7e5b50ee2a28'


Resources:
AppPipeline:
Type: "AWS::CodePipeline::Pipeline"
Properties:
Name: !Ref AWS::StackName
RoleArn: !GetAtt CodePipeLineRole.Arn
Stages:
-
Name: Source
Actions:
-
Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
OutputArtifacts:
-
Name: SourceCode
Configuration:
PollForSourceChanges: true
BranchName: master
RepositoryName: !Ref RepoName
RunOrder: 1
-
Name: BuildVersions
Actions:
-
Name: CallCodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
InputArtifacts:
-
Name: SourceCode
OutputArtifacts:
-
Name: DeployableArtifact
Configuration:
ProjectName: !ImportValue code-build-xaccount-export #
# ^name of the code build job (this is deployed separately and we import from cloudformation export vals
RunOrder: 1

-
Name: DeployProd # Add addtional accounts by cloning this and calling it something else (like DeployStg)
Actions: # Don't forget the additional account number parameter either
-
Name: cfnCreateChangeSet
RoleArn: !Sub 'arn:aws:iam::${ProdAccNumber}:role/CodePipeline-Cross-Account-Role-Access'
InputArtifacts:
-
Name: DeployableArtifact
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_REPLACE
Capabilities: CAPABILITY_IAM
ChangeSetName: 'myawesomestack2-changeset'
RoleArn: !Sub 'arn:aws:iam::${ProdAccNumber}:role/CodePipeline-cloudformation-deploy-role'
StackName: 'myawesomestack2'
TemplatePath: DeployableArtifact::template.json
RunOrder: 1
-
Name: cfnExecuteChangeSet
RoleArn: arn:aws:iam::945437139977:role/CP-Cross-Account-Access
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_EXECUTE
ChangeSetName: 'myawesomestack2-changeset'
StackName: 'myawesomestack2'
RoleArn: !Sub 'arn:aws:iam::${ProdAccNumber}:role/CodePipeline-cloudformation-deploy-role'
RunOrder: 2


ArtifactStore:
Type: S3
Location: !Ref CodePipeLineBucket
EncryptionKey:
Id: !Ref KMSKeyArn
Type: KMS

CodePipeLineRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "codepipeline.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
Policies:
-
PolicyName: "code-pipeline-access"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "*"
Resource: "*"

Outputs:

StackName:
Description: "Stack Name"
Value: !Ref AWS::StackName

CodePipeLineJob:
Description: "Name of the code pipeline job created"
Value: !Ref AppPipeline
50 changes: 50 additions & 0 deletions code-pipeline-cross-account/deploy-account/kms-cross-account.yml
@@ -0,0 +1,50 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: Creates a KMS key and grants access to another account for access
Parameters:

ProductionAccount:
Description: AWS Account Number for production
Type: String
Default: 33333322222

Resources:
KMSKey:
Type: AWS::KMS::Key
Properties:
Description: Used by Assumed Roles in Dev/Test/Prod accounts to Encrypt/Decrypt code
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Id: !Ref AWS::StackName
Statement:
-
Sid: Allows admin of the key
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
Action:
- kms:*
Resource: "*"
-
Sid: Allow use of key in another account
Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${ProductionAccount}:root
# If adding additional accounts put them just in here! and add the parameter up the top!
Action:
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
- kms:DescribeKey
Resource: "*"
KMSAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/codepipeline-crossaccount-key
TargetKeyId: !Ref KMSKey

Outputs:
KMSKeyArn:
Value: !GetAtt [KMSKey,Arn]
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f35fec1

Please sign in to comment.