Skip to content

Commit

Permalink
Initial working version.
Browse files Browse the repository at this point in the history
  • Loading branch information
keithsharp committed May 25, 2017
0 parents commit 92bd34a
Show file tree
Hide file tree
Showing 11 changed files with 409 additions and 0 deletions.
29 changes: 29 additions & 0 deletions README.md
@@ -0,0 +1,29 @@
# Creating an AMI Bakery
I want to create my EC2 instances as [Immutable Infrastructure](https://www.oreilly.com/ideas/an-introduction-to-immutable-infrastructure) and to speed up boot time by doing as little configuration at boot time as possible. The way to do this is to pre-build, or bake, your AMIs with your application already installed.

The demo in this repository shows how to use [Packer](https://packer.io) in conjunction with [AWS CodeCommit](https://aws.amazon.com/codecommit/), [AWS CodePipeline](https://aws.amazon.com/codepipeline/), and [AWS CodeBuild](https://aws.amazon.com/codebuild/), to automatically create a new Amazon Linux AMI with all of the current updates applied based on the contents of a Git repository.

## Steps to Build the Demo
1. Create the VPC using the `vpc.yaml` CloudFormation template. Note the VPC and Subnet IDs and update `packer-files/packer.json` with the values. While you are editing packer.json make sure that the AMI ID for the `source_ami` is set for your environment (it's the Amazon Linux AMI in EU-West-1).
2. Create the Git repository using `codecommit.yaml`, checkout the empty repository, copy the files in `packer-files` into the checkout repository, then commit and push.
3. Create the ECR repository using `ecr-repository.yaml`.
4. Use the following commands to build the Docker container and push it to the repository, substitute the variables in block capitals as appropriate:

`cd container`

`docker build --rm -t <USERNAME>/packer:latest .`

`aws ecr get-login --region <AWS REGION>`

Run the `docker login` command that's output by `aws ecr ...`.

`docker tag <USERNAME>/packer:latest <AWS ACCOUNT>.dkr.ecr.<AWS REGION>.amazonaws.com/packer:latest`

`docker push <AWS ACCOUNT>.dkr.ecr.<AWS REGION>.amazonaws.com/packer:latest`

5. Create the roles for CodeBuild and CodePipeline using the files `codebuild-role.yaml` and `codepipeline-role.yaml`.
6. Edit `codebuild-project.yaml` to reference the container you just created on line 15 - I should really make this automagic using `!Sub`.
7. Create the CodeBuild project using the `codebuild-project.yaml` file.
8. Create the CodePipeline using the `codepipeline.yaml` file.

Once step 8 completes the CodePipeline should automatically detected the Git repository and start executing the pipeline. After 10 minutes of so you should be able to go to the AMI section of the EC2 page on the AWS console and see your new image.
28 changes: 28 additions & 0 deletions cloudformation/codebuild-project.yaml
@@ -0,0 +1,28 @@
---
AWSTemplateFormatVersion: "2010-09-09"

# Create a CodeBuild Project

Resources:
AmiBakeryProject:
Type: AWS::CodeBuild::Project
Properties:
Name: AMI-Bakery
Artifacts:
Type: NO_ARTIFACTS
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Image: 096228720572.dkr.ecr.eu-west-1.amazonaws.com/packer:latest
Type: LINUX_CONTAINER
ServiceRole: !ImportValue "AMI-Bakery:CodeBuildRoleArn"
TimeoutInMinutes: 15
Source:
Type: CODECOMMIT
Location: https://git-codecommit.eu-west-1.amazonaws.com/v1/repos/BakeryGit

Outputs:
CodeBuildProject:
Value: !Ref AmiBakeryProject
Description: CodeBuild Project
Export:
Name: AMI-Bakery:CodeBuildProject
99 changes: 99 additions & 0 deletions cloudformation/codebuild-role.yaml
@@ -0,0 +1,99 @@
---
AWSTemplateFormatVersion: "2010-09-09"

# Create a role for the CodeBuild service

Resources:
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: codebuild-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [codebuild.amazonaws.com]
Action: sts:AssumeRole
Path: /

CodeBuildRolePolicy:
Type: AWS::IAM::Policy
DependsOn: CodeBuildRole
Properties:
PolicyName: CodeBuildRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Resource: ["*"]
# Resource: [!ImportValue: CodeBuild-Git:GitRepoArn]
Action:
- codecommit:GitPull
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: ["*"]
- Effect: Allow
Resource: ["*"]
Action:
- s3:*
- Effect: Allow
Resource: ["*"]
Action:
- kms:GenerateDataKey*
- kms:Encrypt
- kms:Decrypt
- Effect: Allow
Resource: ["*"]
Action:
- sns:SendMessage
- Effect: Allow
Resource: ["*"]
Action:
- ec2:AttachVolume
- ec2:AuthorizeSecurityGroupIngress
- ec2:CopyImage
- ec2:CreateImage
- ec2:CreateKeypair
- ec2:CreateSecurityGroup
- ec2:CreateSnapshot
- ec2:CreateTags
- ec2:CreateVolume
- ec2:DeleteKeypair
- ec2:DeleteSecurityGroup
- ec2:DeleteSnapshot
- ec2:DeleteVolume
- ec2:DeregisterImage
- ec2:DescribeImageAttribute
- ec2:DescribeImages
- ec2:DescribeInstances
- ec2:DescribeRegions
- ec2:DescribeSecurityGroups
- ec2:DescribeSnapshots
- ec2:DescribeSubnets
- ec2:DescribeTags
- ec2:DescribeVolumes
- ec2:DetachVolume
- ec2:GetPasswordData
- ec2:ModifyImageAttribute
- ec2:ModifyInstanceAttribute
- ec2:ModifySnapshotAttribute
- ec2:RegisterImage
- ec2:RunInstances
- ec2:StopInstances
- ec2:TerminateInstances
Roles: [!Ref CodeBuildRole]

Outputs:
CodeBuildRole:
Value: !Ref CodeBuildRole
Description: CodeBuildRole
Export:
Name: AMI-Bakery:CodeBuildRole

CodeBuildRoleArn:
Value: !GetAtt CodeBuildRole.Arn
Description: CodeBuildRole ARN
Export:
Name: AMI-Bakery:CodeBuildRoleArn
24 changes: 24 additions & 0 deletions cloudformation/codecommit.yaml
@@ -0,0 +1,24 @@
---
AWSTemplateFormatVersion: "2010-09-09"

# Create a Git repository

Resources:
Git:
Type: "AWS::CodeCommit::Repository"
Properties:
RepositoryName: BakeryGit
RepositoryDescription: Git repository for AMI Bakery

Outputs:
GitRepoName:
Value: !GetAtt Git.Name
Description: Git Repository Name
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", GitRepoName ] ]

GitRepoArn:
Value: !GetAtt Git.Arn
Description: Git Repository ARN
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", GitRepoArn ] ]
32 changes: 32 additions & 0 deletions cloudformation/codepipeline-role.yaml
@@ -0,0 +1,32 @@
---
AWSTemplateFormatVersion: "2010-09-09"

# Create a role for the CodePipeline service

Resources:
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
RoleName: codepipeline-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [codepipeline.amazonaws.com]
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess

Outputs:
CodePipelineRole:
Value: !Ref CodePipelineRole
Description: CodePipelineRole
Export:
Name: AMI-Bakery:CodePipelineRole

CodePipelineRoleArn:
Value: !GetAtt CodePipelineRole.Arn
Description: CodePipelineRole ARN
Export:
Name: AMI-Bakery:CodePipelineRoleArn
50 changes: 50 additions & 0 deletions cloudformation/codepipeline.yaml
@@ -0,0 +1,50 @@
---
AWSTemplateFormatVersion: "2010-09-09"

# Create a CodePipeline to build an AMI using CodeBuild/Packer

Resources:
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
VersioningConfiguration:
Status: Enabled

BakeryPipeline:
Type: "AWS::CodePipeline::Pipeline"
Properties:
DisableInboundStageTransitions: []
Name: AMI-Bakery-Pipeline
RoleArn: !ImportValue AMI-Bakery:CodePipelineRoleArn
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
Stages:
- Name: CodeCommitSource
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
OutputArtifacts:
- Name: PackerFiles
Configuration:
BranchName: master
RepositoryName: !ImportValue CodeBuild-Git:GitRepoName
RunOrder: 1
- Name: CodeBuild
Actions:
- Name: CodeBuildAction
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
InputArtifacts:
- Name: PackerFiles
Configuration:
ProjectName: !ImportValue AMI-Bakery:CodeBuildProject
RunOrder: 1
28 changes: 28 additions & 0 deletions cloudformation/ecr-repository.yaml
@@ -0,0 +1,28 @@
---
AWSTemplateFormatVersion: "2010-09-09"

# Create an ECR Repository to upload the CodeBuild container

Resources:
CodeBuildRepository:
Type: "AWS::ECR::Repository"
Properties:
RepositoryName: "packer"
RepositoryPolicyText:
Version: "2012-10-17"
Statement:
- Sid: CodeBuildAccess
Effect: Allow
Principal:
AWS: ["codebuild.amazonaws.com"]
Action:
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:BatchCheckLayerAvailability

Outputs:
ECRRepoName:
Value: !Ref CodeBuildRepository
Description: ECR Repository Name
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", ECRRepoName ] ]
75 changes: 75 additions & 0 deletions cloudformation/vpc.yaml
@@ -0,0 +1,75 @@
---
AWSTemplateFormatVersion: "2010-09-09"

# Create a VPC with a single, private subnet.
# If I need the public subnet I'll need an Internet Gateway and some routes!

Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default

InternetGateway:
Type: AWS::EC2::InternetGateway

AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway

PublicSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC

PublicSubnetInternetRoute:
Type: AWS::EC2::Route
DependsOn: InternetGateway
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicSubnetRouteTable

PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicSubnetRouteTable
SubnetId: !Ref PublicSubnet

PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.0.0/24
MapPublicIpOnLaunch: true

# PrivateSubnet:
# Type: AWS::EC2::Subnet
# Properties:
# VpcId: !Ref VPC
# CidrBlock: 10.0.1.0/24
# MapPublicIpOnLaunch: false

Outputs:
VPCID:
Value: !Ref VPC
Description: VPC ID
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", VPCID ] ]

PublicSubnetID:
Value: !Ref PublicSubnet
Description: Public Subnet ID
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", PublicSubnetID ] ]

# PrivateSubnetID:
# Value: !Ref PrivateSubnet
# Description: Private Subnet ID
# Export:
# Name: !Join [ ":", [ !Ref "AWS::StackName", PrivateSubnetID ] ]
7 changes: 7 additions & 0 deletions container/Dockerfile
@@ -0,0 +1,7 @@
FROM ubuntu

RUN apt-get update && apt-get -y install curl unzip jq && \
curl -o packer.zip https://releases.hashicorp.com/packer/1.0.0/packer_1.0.0_linux_amd64.zip && \
unzip packer.zip

CMD ["/packer"]
18 changes: 18 additions & 0 deletions packer-files/buildspec.yml
@@ -0,0 +1,18 @@
version: 0.2

environment_variables:
plaintext:
PACKER_NO_COLOR: "1"

phases:
install:
commands:
- curl -qL 169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI > credentials.json
- mkdir -p ~/.aws
- echo "[default]" > ~/.aws/credentials
- echo "aws_access_key_id = $(cat credentials.json | jq '.AccessKeyId' | sed s'/"//g')" >> ~/.aws/credentials
- echo "aws_secret_access_key = $(cat credentials.json | jq '.SecretAccessKey' | sed s'/"//g')" >> ~/.aws/credentials
- echo "aws_session_token = $(cat credentials.json | jq '.Token' | sed s'/"//g')" >> ~/.aws/credentials
build:
commands:
- /packer build packer.json

0 comments on commit 92bd34a

Please sign in to comment.