Skip to content

Instantly share code, notes, and snippets.

@thash
Last active December 14, 2017 03:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thash/09d095eb44bea1f150caee0c1a4879df to your computer and use it in GitHub Desktop.
Save thash/09d095eb44bea1f150caee0c1a4879df to your computer and use it in GitHub Desktop.
AWSTemplateFormatVersion: 2010-09-09
Parameters:
ServiceName:
Type: String
Default: TimeService
Description: Name for the service, used in the code repository, Lambda function, and pipeline names
Resources:
# Code repository for the service
CodeRepository:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName: !Sub '${ServiceName}_repo'
RepositoryDescription: !Sub 'Repository for the ${ServiceName} service'
# CodeBuild project and resources (S3 Bucket for build artifacts, Role, Project)
BuildArtifactsBucket:
Type: AWS::S3::Bucket
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Path: /
Policies:
- PolicyName: CodeBuildAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Resource:
- !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ServiceName}_build'
- !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ServiceName}_build:*'
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
- Effect: Allow
Resource:
- !Sub 'arn:aws:s3:::${BuildArtifactsBucket}/*'
Action:
- 's3:GetObject'
- 's3:GetObjectVersion'
- 's3:PutObject'
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub '${ServiceName}_build'
Description: Build project for the Lambda service
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/eb-nodejs-4.4.6-amazonlinux-64:2.1.3
EnvironmentVariables:
- Name: BUILD_OUTPUT_BUCKET
Value: !Ref BuildArtifactsBucket
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Source:
Type: CODEPIPELINE
# Integration tests function
StartTestsFunctionExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Path: /
Policies:
- PolicyName: InvokeAsync
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- 'lambda:InvokeAsync'
- 'lambda:InvokeFunction'
- 'cloudformation:DescribeStacks'
- 'codepipeline:*'
- 'logs:*'
Effect: Allow
Resource: '*'
StartTestsFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub '${ServiceName}_start_tests'
Description: !Sub 'Starts integration tests function ${ServiceName}'
Handler: index.lambda_handler
MemorySize: 128
Role: !GetAtt StartTestsFunctionExecutionRole.Arn
Runtime: python2.7
Timeout: 10
Code:
ZipFile: >
import json
import boto3
import uuid
cp = boto3.client("codepipeline")
cf = boto3.client("cloudformation")
lambda_client = boto3.client("lambda")
def lambda_handler(event, context):
print json.dumps(event, sort_keys=True, indent=4, separators=(',', ': '))
job_id = event["CodePipeline.job"]["id"]
user_parameters = event["CodePipeline.job"]["data"]["actionConfiguration"]["configuration"]["UserParameters"]
stack_name = user_parameters.split("|")[0]
test_stack_name = user_parameters.split("|")[1]
stage = user_parameters.split("|")[2]
print("looking for ApiUrl in {} Stack".format(stack_name))
api_url, error = _get_property_from_stack(stack_name, "ApiUrl")
if not api_url:
_fail_job(job_id, error)
return
print("looking for TestFunction in {} Stack".format(test_stack_name))
test_function, error = _get_property_from_stack(test_stack_name, "TestFunction")
if not test_function:
_fail_job(job_id, error)
return
lambda_client.invoke_async(
FunctionName=test_function,
InvokeArgs=json.dumps({
"job_id": job_id,
"api_url": api_url
})
)
def _get_property_from_stack(stack_name, property_key):
stack_data = cf.describe_stacks(StackName=stack_name)
if not stack_data or len(stack_data["Stacks"]) == 0:
return None, "Could not find stack"
current_stack = stack_data["Stacks"][0]
if "Outputs" not in current_stack:
return None, "Could not find outputs in stack"
for cur_output in current_stack["Outputs"]:
if cur_output["OutputKey"] == property_key:
return cur_output["OutputValue"], None
return None, "Could not find {} output property in stack".format(property_key)
def _complete_job(job_id):
cp.put_job_success_result(jobId=job_id)
def _fail_job(job_id, failure_reason):
cp.put_job_failure_result(
jobId=job_id,
failureDetails={
'type': 'JobFailed',
'message': failure_reason,
'externalExecutionId': uuid.uuid4().hex
}
)
# CodePipeline definition and required roles
CFNPipelinePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: CloudFormation Pipeline Execution Policy
Path: "/"
PolicyDocument:
Version: '2012-10-17'
Statement:
Effect: Allow
Action:
- 'cloudformation:CreateStack'
- 'cloudformation:DescribeStacks'
- 'cloudformation:DeleteStack'
- 'cloudformation:UpdateStack'
- 'cloudformation:CreateChangeSet'
- 'cloudformation:ExecuteChangeSet'
- 'cloudformation:DeleteChangeSet'
- 'cloudformation:DescribeChangeSet'
- 'cloudformation:SetStackPolicy'
- 'cloudformation:SetStackPolicy'
- 'cloudformation:ValidateTemplate'
- 'codebuild:StartBuild'
- 'codebuild:BatchGetBuilds'
Resource: "*"
CloudFormationExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- cloudformation.amazonaws.com
Path: /
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AdministratorAccess'
PipelineExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Path: /
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AWSCodeCommitFullAccess'
- 'arn:aws:iam::aws:policy/AmazonS3FullAccess'
- !Ref CFNPipelinePolicy
Policies:
- PolicyName: CodePipelineAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- 'iam:PassRole'
- 'lambda:InvokeFunction'
- 'lambda:ListFunctions'
- 'lambda:InvokeAsyc'
Effect: Allow
Resource: '*'
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref BuildArtifactsBucket
Type: S3
Name: !Sub ${ServiceName}_pipeline
RoleArn: !GetAtt PipelineExecutionRole.Arn
Stages:
- Name: Source
Actions:
- Name: CodeCommitRepo
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
RepositoryName: !Sub '${ServiceName}_repo'
BranchName: master
OutputArtifacts:
- Name: SourceZip
RunOrder: 1
- Name: Build
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: 1
Configuration:
ProjectName: !Ref CodeBuildProject
InputArtifacts:
- Name: SourceZip
OutputArtifacts:
- Name: BuiltZip
- Name: DeployTests
Actions:
- Name: CreateChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Tests'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Tests'
TemplatePath: BuiltZip::test-output_sam.yaml
Capabilities: CAPABILITY_IAM
InputArtifacts:
- Name: BuiltZip
OutputArtifacts:
- Name: TestChangeSet
RunOrder: 1
- Name: ExecuteChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Tests'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Tests'
OutputArtifacts:
- Name: ExecutedTestChangeSet
RunOrder: 2
- Name: Beta
Actions:
- Name: CreateChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Beta'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Beta'
TemplatePath: BuiltZip::app-output_sam.yaml
Capabilities: CAPABILITY_IAM
ParameterOverrides: '{ "Stage": "beta" }'
InputArtifacts:
- Name: BuiltZip
RunOrder: 1
- Name: ExecuteChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Beta'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Beta'
OutputArtifacts:
- Name: !Sub '${ServiceName}BetaChangeSet'
RunOrder: 2
- Name: IntegrationTests
ActionTypeId:
Category: Invoke
Owner: AWS
Provider: Lambda
Version: 1
Configuration:
FunctionName: !Ref StartTestsFunction
UserParameters: !Sub '${ServiceName}-Stack-Beta|${ServiceName}-Stack-Tests|beta'
InputArtifacts:
- Name: !Sub '${ServiceName}BetaChangeSet'
RunOrder: 3
- Name: Gamma
Actions:
- Name: CreateChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Gamma'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Gamma'
TemplatePath: BuiltZip::app-output_sam.yaml
Capabilities: CAPABILITY_IAM
ParameterOverrides: '{ "Stage": "gamma" }'
InputArtifacts:
- Name: BuiltZip
RunOrder: 1
- Name: ExecuteChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Gamma'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Gamma'
OutputArtifacts:
- Name: !Sub '${ServiceName}GammaChangeSet'
RunOrder: 2
- Name: IntegrationTests
ActionTypeId:
Category: Invoke
Owner: AWS
Provider: Lambda
Version: 1
Configuration:
FunctionName: !Ref StartTestsFunction
UserParameters: !Sub '${ServiceName}-Stack-Gamma|${ServiceName}-Stack-Tests|gamma'
InputArtifacts:
- Name: !Sub '${ServiceName}GammaChangeSet'
RunOrder: 3
- Name: Prod
Actions:
- Name: DeploymentApproval
ActionTypeId:
Category: Approval
Owner: AWS
Provider: Manual
Version: 1
RunOrder: 1
- Name: CreateChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Prod'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Prod'
TemplatePath: BuiltZip::app-output_sam.yaml
Capabilities: CAPABILITY_IAM
ParameterOverrides: '{ "Stage": "prod" }'
InputArtifacts:
- Name: BuiltZip
RunOrder: 2
- Name: ExecuteChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !GetAtt CloudFormationExecutionRole.Arn
StackName: !Sub '${ServiceName}-Stack-Prod'
ChangeSetName: !Sub '${ServiceName}-ChangeSet-Prod'
OutputArtifacts:
- Name: !Sub '${ServiceName}ProdChangeSet'
RunOrder: 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment