Enhance your project Makefile
in the project directory with the following content:
BITBUCKET_HOST := ...
BITBUCKET_PROJECT := ...
BITBUCKET_REPOSITORY := ...
BITBUCKET_BRANCH := master
###############################################################################################
.PHONY: pipeline
pipeline: guard-BITBUCKET_TOKEN
aws cloudformation deploy \
--no-fail-on-empty-changeset \
--template-file cloudformation/pipeline.yml \
--stack-name $(SERVICE)-pipeline \
--parameter-overrides BitbucketHost=$(BITBUCKET_HOST) \
BitbucketProject=$(BITBUCKET_PROJECT) \
BitbucketRepository=$(BITBUCKET_REPOSITORY) \
BitbucketBranch=$(BITBUCKET_BRANCH) \
BitbucketToken=$(BITBUCKET_TOKEN) \
--capabilities CAPABILITY_IAM \
--region $(AWS_REGION)
touch trigger && zip trigger.zip trigger
aws s3api put-object --bucket $(SERVICE)-pipeline-$(shell aws sts get-caller-identity --query 'Account' --output text) --key trigger --body 'trigger.zip'
rm trigger && rm trigger.zip
.PHONY: guard-%
guard-%:
$(if $(value ${*}),,$(error "Variable ${*} not set!"))
Create a new cloudformation template in cloudformation/pipeline.yml
and define your pipeline in there:
---
AWSTemplateFormatVersion: 2010-09-09
Description: Deployment pipeline
Parameters:
BitbucketHost:
Type: String
BitbucketProject:
Type: String
BitbucketRepository:
Type: String
BitbucketBranch:
Type: String
BitbucketToken:
Type: String
Resources:
CodePipelineSourceBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Sub '${AWS::StackName}-${AWS::AccountId}'
VersioningConfiguration:
Status: Enabled
CodePipelineArtifactStoreBucket:
Type: 'AWS::S3::Bucket'
CodePipelineArtifactStoreBucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref CodePipelineArtifactStoreBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: DenyUnEncryptedObjectUploads
Effect: Deny
Principal: '*'
Action: 's3:PutObject'
Resource: !Join
- ''
- - !GetAtt
- CodePipelineArtifactStoreBucket
- Arn
- /*
Condition:
StringNotEquals:
's3:x-amz-server-side-encryption': 'aws:kms'
- Sid: DenyInsecureConnections
Effect: Deny
Principal: '*'
Action: 's3:*'
Resource: !Join
- ''
- - !GetAtt
- CodePipelineArtifactStoreBucket
- Arn
- /*
Condition:
Bool:
'aws:SecureTransport': false
AppPipeline:
Type: 'AWS::CodePipeline::Pipeline'
Properties:
Name: !Ref AWS::StackName
RoleArn: !GetAtt
- CodePipelineServiceRole
- Arn
Stages:
- Name: Source
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: S3
OutputArtifacts:
- Name: NoOp
Configuration:
S3Bucket: !Ref CodePipelineSourceBucket
S3ObjectKey: 'trigger'
PollForSourceChanges: true
- Name: GitClone
Actions:
- Name: GitClone
InputArtifacts:
- Name: NoOp
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
OutputArtifacts:
- Name: SourceOutput
Configuration:
ProjectName: !Ref AppGitClone
- Name: Build
Actions:
- Name: Build
InputArtifacts:
- Name: SourceOutput
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
OutputArtifacts:
- Name: BuildOutput
Configuration:
ProjectName: !Ref AppCodeBuild
ArtifactStore:
Type: S3
Location: !Ref CodePipelineArtifactStoreBucket
AppGitClone:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub '${AWS::StackName}-clone'
ServiceRole: !Ref CodeBuildServiceRole
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_MEDIUM
Image: aws/codebuild/standard:2.0
Type: LINUX_CONTAINER
PrivilegedMode: true
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
phases:
install:
runtime-versions:
java: openjdk8
build:
commands:
- "curl -sfLl -H 'Authorization: Bearer ${BitbucketToken}' -o repo.tar.gz 'https://${BitbucketHost}/rest/api/latest/projects/${BitbucketProject}/repos/${BitbucketRepository}/archive?at=refs%2Fheads%2F${BitbucketBranch}&format=tar.gz&filename=repo.tar.gz'"
- mkdir -p ${BitbucketRepository}
- tar zxvf repo.tar.gz -C ${BitbucketRepository}
artifacts:
files:
- '**/*'
base-directory: ${BitbucketRepository}
AppCodeBuild:
Type: AWS::CodeBuild::Project
Properties:
Name: !Ref AWS::StackName
ServiceRole: !Ref CodeBuildServiceRole
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_LARGE
Image: aws/codebuild/standard:2.0
Type: LINUX_CONTAINER
PrivilegedMode: true
Source:
Type: CODEPIPELINE
BuildSpec: buildspec.yml
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
CodePipelineServiceRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action: 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: AWS-CodePipeline-Service-3
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'codecommit:CancelUploadArchive'
- 'codecommit:GetBranch'
- 'codecommit:GetCommit'
- 'codecommit:GetUploadArchiveStatus'
- 'codecommit:UploadArchive'
Resource: '*'
- Effect: Allow
Action:
- 'codedeploy:CreateDeployment'
- 'codedeploy:GetApplicationRevision'
- 'codedeploy:GetDeployment'
- 'codedeploy:GetDeploymentConfig'
- 'codedeploy:RegisterApplicationRevision'
Resource: '*'
- Effect: Allow
Action:
- 'codebuild:BatchGetBuilds'
- 'codebuild:StartBuild'
Resource: '*'
- Effect: Allow
Action:
- 'devicefarm:ListProjects'
- 'devicefarm:ListDevicePools'
- 'devicefarm:GetRun'
- 'devicefarm:GetUpload'
- 'devicefarm:CreateUpload'
- 'devicefarm:ScheduleRun'
Resource: '*'
- Effect: Allow
Action:
- 'lambda:InvokeFunction'
- 'lambda:ListFunctions'
Resource: '*'
- Effect: Allow
Action:
- 'iam:PassRole'
Resource: '*'
- Effect: Allow
Action:
- 'elasticbeanstalk:*'
- 'ec2:*'
- 'elasticloadbalancing:*'
- 'autoscaling:*'
- 'cloudwatch:*'
- 's3:*'
- 'sns:*'
- 'cloudformation:*'
- 'rds:*'
- 'sqs:*'
- 'ecs:*'
Resource: '*'
Create the Cloudformation stack with the following command in the ci AWS account
AWS_PROFILE=myorg-ci BITBUCKET_TOKEN=... make pipeline
In order to make the pipeline work you also need to define a file buildspec.yml
. Create it with the following content:
version: 0.2
phases:
install:
runtime-versions:
java: openjdk8
docker: 18
build:
commands:
- echo "Bulding and pushing the Docker image..."
- make docker-build docker-push
Make sure to commit and push everything to the repository:
git add Makefile cloudformation/*.yml docker/ buildspec.yml
git commit -m 'Set up docker image, create ECR repository, create pipeline'
Then push to your branch
git push ...
Log in to the AWS Console, switch to the CI account and trigger your pipeline.