Skip to content

Instantly share code, notes, and snippets.

@petrabarus
Last active March 23, 2020 16:07
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 petrabarus/8accb7b61f895ff683f07819a2109b67 to your computer and use it in GitHub Desktop.
Save petrabarus/8accb7b61f895ff683f07819a2109b67 to your computer and use it in GitHub Desktop.
CI/CD Pipeline AWS CDK
#!/usr/bin/env node
/*********************************
* AWS CDK script to provision the resources.
*/
import 'source-map-support/register';
import cdk = require('@aws-cdk/core');
import { WebApp } from './webapp';
import { Pipeline } from './pipeline';
import { Cluster } from './cluster';
class WebStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const cluster = new Cluster(this, 'Cluster');
const webapp = new WebApp(this, 'WebApp', {
cluster: cluster
});
const pipeline = new Pipeline(this, 'Pipeline', {
webapp: webapp
})
}
}
const app = new cdk.App();
new WebStack(app, 'WebTestStack');
app.synth();
import cdk = require('@aws-cdk/core');
import ecs = require("@aws-cdk/aws-ecs");
import ecr = require('@aws-cdk/aws-ecr');
import { CfnOutput } from '@aws-cdk/core';
class Cluster extends cdk.Construct {
readonly ecsCluster: ecs.Cluster;
constructor(scope: cdk.Construct, id: string) {
super(scope, id);
this.ecsCluster = new ecs.Cluster(this, 'EcsCluster');
this.output();
}
output() {
new CfnOutput(this, 'ECSCluster_ARN', {value: this.ecsCluster.clusterArn});
}
}
export {Cluster};
import cdk = require('@aws-cdk/core');
import codebuild = require('@aws-cdk/aws-codebuild');
import codepipeline = require('@aws-cdk/aws-codepipeline');
import codepipeline_actions = require('@aws-cdk/aws-codepipeline-actions');
import ssm = require("@aws-cdk/aws-ssm");
import ecr = require('@aws-cdk/aws-ecr');
import ecs = require('@aws-cdk/aws-ecs');
import { WebApp } from './webapp';
interface PipelineProps {
readonly webapp: WebApp;
}
class Pipeline extends cdk.Construct {
private readonly webapp: WebApp;
readonly service: ecs.IBaseService;
readonly containerName: string;
readonly ecrRepo: ecr.Repository;
public readonly pipeline: codepipeline.Pipeline;
constructor(scope: cdk.Construct, id: string, props: PipelineProps) {
super(scope, id);
this.webapp = props.webapp;
this.service = this.webapp.service;
this.ecrRepo = this.webapp.ecrRepo;
this.containerName = this.webapp.containerName;
this.pipeline = this.createPipeline();
this.output();
}
private createPipeline(): codepipeline.Pipeline {
const sourceOutput = new codepipeline.Artifact();
const buildOutput = new codepipeline.Artifact();
return new codepipeline.Pipeline(this, 'Pipeline', {
stages: [
this.createSourceStage('Source', sourceOutput),
this.createImageBuildStage('Build', sourceOutput, buildOutput),
this.createDeployStage('Deploy', buildOutput),
]
});
}
private createSourceStage(stageName: string, output: codepipeline.Artifact): codepipeline.StageProps {
const secret = cdk.SecretValue.secretsManager('/myapp/dev/GITHUB_TOKEN');
const repo = ssm.StringParameter.valueForStringParameter(this, '/myapp/dev/GITHUB_REPO');
const owner = ssm.StringParameter.valueForStringParameter(this, '/myapp/dev/GITHUB_OWNER');
const githubAction = new codepipeline_actions.GitHubSourceAction({
actionName: 'Github_Source',
owner: owner,
repo: repo,
oauthToken: secret,
output: output,
});
return {
stageName: stageName,
actions: [githubAction],
};
}
private createImageBuildStage(
stageName: string,
input: codepipeline.Artifact,
output: codepipeline.Artifact
): codepipeline.StageProps {
const project = new codebuild.PipelineProject(
this,
'Project',
{
buildSpec: this.createBuildSpec(),
environment: {
buildImage: codebuild.LinuxBuildImage.STANDARD_2_0,
privileged: true,
},
environmentVariables: {
REPOSITORY_URI: {value: this.ecrRepo.repositoryUri},
CONTAINER_NAME: {value: this.containerName}
}
}
);
this.ecrRepo.grantPullPush(project.grantPrincipal);
const codebuildAction = new codepipeline_actions.CodeBuildAction({
actionName: 'CodeBuild_Action',
input: input,
outputs: [output],
project: project,
});
return {
stageName: stageName,
actions: [codebuildAction],
};
}
createDeployStage(stageName: string, input: codepipeline.Artifact): codepipeline.StageProps {
const ecsDeployAction = new codepipeline_actions.EcsDeployAction({
actionName: 'ECSDeploy_Action',
input: input,
service: this.service,
});
return {
stageName: stageName,
actions: [ecsDeployAction],
}
}
createBuildSpec(): codebuild.BuildSpec {
return codebuild.BuildSpec.fromObject({
version: '0.2',
phases: {
install: {
'runtime-versions': {
'nodejs': '10',
'php': '7.3'
},
commands: [
'npm install',
'composer install',
],
},
pre_build: {
commands: [
'aws --version',
'$(aws ecr get-login --region ${AWS_DEFAULT_REGION} --no-include-email | sed \'s|https://||\')',
'COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)',
'IMAGE_TAG=${COMMIT_HASH:=latest}'
]
},
build: {
commands: [
'docker build -t $REPOSITORY_URI:latest .',
'docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG',
]
},
post_build: {
commands: [
'docker push $REPOSITORY_URI:latest',
'docker push $REPOSITORY_URI:$IMAGE_TAG',
'printf "[{\\"name\\":\\"${CONTAINER_NAME}\\",\\"imageUri\\":\\"${REPOSITORY_URI}:latest\\"}]" > imagedefinitions.json'
]
}
},
artifacts: {
files: [
'imagedefinitions.json'
]
}
});
}
output() {
new cdk.CfnOutput(this, 'Pipeline ARN', {value: this.pipeline.pipelineArn})
}
}
export {Pipeline, PipelineProps};
import cdk = require('@aws-cdk/core');
import ecs = require("@aws-cdk/aws-ecs");
import ecsPatterns = require("@aws-cdk/aws-ecs-patterns");
import ecr = require('@aws-cdk/aws-ecr');
import { CfnOutput } from '@aws-cdk/core';
import { Cluster } from './cluster';
interface WebAppProps {
readonly cluster: Cluster;
}
class WebApp extends cdk.Construct {
private fargateService: ecsPatterns.ApplicationLoadBalancedFargateService;
public readonly service: ecs.IBaseService;
public readonly containerName: string;
public readonly ecrRepo: ecr.Repository;
constructor(scope: cdk.Construct, id: string, props: WebAppProps) {
super(scope, id);
this.fargateService = this.createService(props.cluster.ecsCluster);
this.ecrRepo = new ecr.Repository(this, 'ECRRepo');
this.ecrRepo.grantPull(this.fargateService.taskDefinition.executionRole!);
this.service = this.fargateService.service;
this.containerName = this.fargateService.taskDefinition.defaultContainer!.containerName;
this.addAutoScaling();
this.output();
}
private createService(cluster: ecs.Cluster) {
return new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
cluster: cluster,
taskImageOptions: {
image: ecs.ContainerImage.fromAsset('.'),
}
});
}
private addAutoScaling() {
const autoScalingGroup = this.fargateService.service.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 10
});
autoScalingGroup.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 50,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(60),
});
}
private output() {
new CfnOutput(this, 'ECRRepo_ARN', {value: this.ecrRepo.repositoryArn});
new CfnOutput(this, 'ContainerName', {value: this.containerName});
}
}
export {WebApp, WebAppProps};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment