Skip to content

Instantly share code, notes, and snippets.

@chrisj-au
Last active November 27, 2020 01:33
Show Gist options
  • Save chrisj-au/594f8acf94791814820f8faabe5221f5 to your computer and use it in GitHub Desktop.
Save chrisj-au/594f8acf94791814820f8faabe5221f5 to your computer and use it in GitHub Desktop.
Jenkins pipelines for deploying image to ECS via CodeDeploy
// Front end to UAT and Prod deploy pipelines (unifies parameters etc)
def git = new org.arq.git()
def GITHUB_CREDS = 'ghCredentials'
def AWS_CLUSTER_PREFIX = "ecs-" // Prefix Environment
def AWS_REGION = "ap-southeast-2"
def AWS_ROLE = "JenkinsRole"
def AWS_PROD_ACC = [
"AccountName": "aws-prod",
"AccountId": "12345",
"Environment": "production" ]
def AWS_UAT_ACC = [
"AccountName": "aws-uat",
"AccountId" : "12346",
"Environment": "uat" ]
def TARGET_ENV = []
pipeline {
agent { label 'slave-small' }
parameters {
choice (name: 'Service', description: 'Service to build',
choices: [
'frontend-service',
'cc-validation-service'
])
string (name: 'Branch', description: "Branch to build from", defaultValue: 'master')
booleanParam (defaultValue: true, description: 'Update ECS Service', name: 'Trigger CodeDeploy Deployment')
booleanParam (defaultValue: false, description: 'Bypass Deployment Hook Test', name: 'Skip Deployment Test')
choice (name: 'Environment', description: 'Selecting production will copy the in-use image from uat', choices: [
"production","uat" ])
choice (name: 'Deployment Configuration', description: '', choices: [
"Canary 10 Percent 5 Minutes",
"Canary 10 Percent 15 Minutes",
"Linear 10 Percent Every 1 Minutes",
"Linear 10 PercentEvery 3 Minutes",
"All At Once"
])
}
environment {
SERVICE_NAME = "${params.Service}"
ECS_SERVICE = "nrg-${params.Service}"
GITHUB_REPO = "git@github.com:melbourneit/nrg-${params.Service}.git"
GIT_SOURCE_BRANCH = "${params.'Branch'}"
TRIGGER_DEPLOY = "${params.'Trigger CodeDeploy Deployment'}"
DEPLOY_CONFIG = "CodeDeployDefault.ECS${params.'Deployment Configuration'.replace(' ','')}"
SKIP_DEPLOY_TEST = "${params.'Skip Deployment Test'}"
}
stages {
stage('Preparing Pipeline') {
steps {
cleanWs()
script {
failedStage=env.STAGE_NAME
buildStartedBy = whoStartedJob()
AWS_TARGET_ACC = params.Environment == "production" ? AWS_PROD_ACC : AWS_UAT_ACC
ecr_name = ECS_SERVICE
}
}
}
stage('UAT Release') {
when {
expression {
return AWS_TARGET_ACC.Environment == "uat"
}
}
steps {
script {
ecr_image_path = "${AWS_TARGET_ACC.AccountId}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ecr_name}"
cluster_name = AWS_CLUSTER_PREFIX + AWS_TARGET_ACC.Environment
build job: "uat-deploy-service", parameters: [
string(name: 'Service', value: SERVICE_NAME),
string(name: 'Branch', value: GIT_SOURCE_BRANCH),
booleanParam(name: 'TriggerCodeDeployDeployment', value: TRIGGER_DEPLOY),
booleanParam(name: 'SkipDeploymentTest', value: SKIP_DEPLOY_TEST),
string(name: 'DeploymentConfiguration', value: DEPLOY_CONFIG),
string(name: 'repo', value: GITHUB_REPO),
string(name: 'ecrImage', value: ecr_image_path),
string(name: 'accountId', value: AWS_TARGET_ACC.AccountId),
string(name: 'startedBy', value: buildStartedBy)
]
}
}
}
stage('Production Release') {
when {
expression {
return AWS_TARGET_ACC.Environment == "production"
}
}
steps {
script {
uat_cluster_name = AWS_CLUSTER_PREFIX + AWS_UAT_ACC.Environment
build job: "prod-deploy-service", parameters: [
string(name: 'Service', value: SERVICE_NAME),
booleanParam(name: 'TriggerCodeDeployDeployment', value: TRIGGER_DEPLOY),
booleanParam(name: 'SkipDeploymentTest', value: SKIP_DEPLOY_TEST),
string(name: 'DeploymentConfiguration', value: DEPLOY_CONFIG),
string(name: 'uataccountId', value: AWS_UAT_ACC.AccountId),
string(name: 'uatcluster', value: uat_cluster_name),
string(name: 'startedBy', value: buildStartedBy),
string(name: 'prodaccountId', value: AWS_PROD_ACC.AccountId)
]
}
}
}
}
}
def git = new org.lib.git()
def GITHUB_CREDS = 'ghCredentials'
def AWS_REGION = "ap-southeast-2"
def AWS_ROLE = "JenkinsRole"
pipeline {
agent { label 'slave-t2sm' }
parameters {
string (name: 'Service')
booleanParam (defaultValue: true, description: 'Update ECS Service', name: 'TriggerCodeDeployDeployment')
booleanParam (defaultValue: false, description: 'Bypass Deployment Hook Test', name: 'SkipDeploymentTest')
string (name: 'DeploymentConfiguration')
string (name: 'uataccountId')
string (name: 'prodaccountId')
string(name: 'startedBy')
string(name: 'uatcluster')
}
environment {
SERVICE_NAME = "${params.Service}"
ECS_SERVICE = "${params.Service}"
TRIGGER_DEPLOY = "${params.TriggerCodeDeployDeployment}"
SKIP_DEPLOY_TEST = "${params.SkipDeploymentTest}"
DEPLOY_CONFIG = "${params.DeploymentConfiguration}"
UAT_ACC_ID = "${params.uataccountId}"
PROD_ACC_ID = "${params.prodaccountId}"
}
stages {
stage('Check uat service') {
steps {
script {
uat_service = ecsLookupService(ECS_SERVICE, AWS_ROLE, UAT_ACC_ID, params.uatcluster)
taskDefnARN = uat_service.taskDefinition
echo "current uat task definition: ${taskDefnARN}"
uat_task = ecsLookupTaskDefinition(taskDefnARN, AWS_ROLE, UAT_ACC_ID)
echo "Current image: ${uat_task.image}"
}
}
}
stage('Pull image') {
steps {
script {
echo "Pull image"
withAWS(role:"${AWS_ROLE}", roleAccount:"${UAT_ACC_ID}", duration: 900, roleSessionName: 'jenk-session')
{
docker_creds = ecrLogin()
sh "${docker_creds}"
docker_image = docker.image(uat_task.image)
docker_image.pull()
new_tag = uat_task.image.replace(UAT_ACC_ID, PROD_ACC_ID)
}
}
}
}
stage('Copy uat image to prod') {
steps {
script {
withAWS(role:"${AWS_ROLE}", roleAccount:"${PROD_ACC_ID}", duration: 900, roleSessionName: 'jenk-session')
{
docker_creds = ecrLogin()
sh "${docker_creds}"
echo "Copying image to prod"
sh "docker tag ${uat_task.image} ${new_tag}"
sh "docker push ${new_tag}"
}
}
}
}
stage('Create new task definition') {
steps {
script {
echo "Create new task definition"
NEW_TASK = ecsNewTaskDefinition(ECS_SERVICE, new_tag, PROD_ACC_ID, AWS_ROLE, AWS_REGION)
echo "new task arn: ${NEW_TASK.taskDefinition.taskDefinitionArn}"
}
}
}
stage('Add Task Definition Tags') {
steps {
script {
def awsTags = [
triggeredBy: params.startedBy,
pipeline: BUILD_URL
]
ecsTagTaskDefinition(
AWS_ROLE,
PROD_ACC_ID,
NEW_TASK.taskDefinition.taskDefinitionArn,
awsTags)
}
}
}
stage('Initiate Deployment') {
when {
expression {
return TRIGGER_DEPLOY == "true"
}
}
steps {
script {
withAWS(role:"${AWS_ROLE}", roleAccount:"${PROD_ACC_ID}", duration: 900, roleSessionName: 'jenk-session')
{
if (SKIP_DEPLOY_TEST == "true") { test_hook = "" }
else {
test_lambda_name = "${SERVICE_NAME}-deployment-test"
try {
lambda_check = sh (script:"aws lambda get-function --function-name ${test_lambda_name}", returnStdout: true)
test_hook = ", \\\"hooks\\\":[{\\\"AfterAllowTestTraffic\\\": \\\"${test_lambda_name}\\\"}]"
} catch (err) {
echo "Lambda not found for function, skipping post-deployment test"
test_hook = ""
}
}
appspec = "\"{\\\"Resources\\\":[{\\\"TargetService\\\":{\\\"Type\\\":\\\"AWS::ECS::Service\\\", \\\"Properties\\\":{ \\\"TaskDefinition\\\":\\\"${NEW_TASK.taskDefinition.taskDefinitionArn}\\\", \\\"LoadBalancerInfo\\\":{\\\"ContainerName\\\":\\\"${ECS_SERVICE}\\\",\\\"ContainerPort\\\":\\\"80\\\"}}}}], \\\"version\\\":\\\"0.0\\\"}${test_hook} \""
deploy_output = sh (
script: "aws deploy create-deployment \
--application-name ${ECS_SERVICE} \
--deployment-group-name ${ECS_SERVICE}-dg \
--revision '{\"revisionType\": \"AppSpecContent\",\"appSpecContent\":{\"content\": ${appspec}}}' \
--deployment-config-name ${DEPLOY_CONFIG} \
--description \"Initiated by: ${params.startedBy} - ${BUILD_URL}\" \
--auto-rollback-configuration enabled=true,events=DEPLOYMENT_FAILURE",
returnStdout: true)
echo "deploy_output: ${deploy_output}"
}
}
}
}
}
}
def git = new org.lib.git()
def GITHUB_CREDS = 'ghCredentials'
def AWS_REGION = "ap-southeast-2"
def AWS_ROLE = "JenkinsRole"
pipeline {
agent { label 'small-slave' }
parameters {
string (name: 'Service')
string (name: 'Branch', description: "Branch to build from", defaultValue: 'master')
booleanParam (defaultValue: true, description: 'Update ECS Service', name: 'TriggerCodeDeployDeployment')
booleanParam (defaultValue: false, description: 'Bypass Deployment Hook Test', name: 'SkipDeploymentTest')
string (name: 'repo')
string (name: 'DeploymentConfiguration')
string (name: 'accountId')
string(name: 'ecrImage')
string(name: 'startedBy')
}
environment {
SERVICE_NAME = "${params.Service}"
ECS_SERVICE = "${params.Service}"
GITHUB_REPO = "${params.repo}"
GIT_SOURCE_BRANCH = "${params.Branch}"
TRIGGER_DEPLOY = "${params.TriggerCodeDeployDeployment}"
SKIP_DEPLOY_TEST = "${params.SkipDeploymentTest}"
DEPLOY_CONFIG = "${params.DeploymentConfiguration}"
AWS_ACC_ID = "${params.accountId}"
}
stages {
stage('Checkout Service') {
steps {
script {
failedStage=env.STAGE_NAME
gitRepo = git.checkOut(GITHUB_REPO, GITHUB_CREDS, env.GIT_SOURCE_BRANCH)
echo "REPO Stuff: ${gitRepo}"
sourceGitDetails = git.getCommitDetails()
env.GIT_SOURCE_COMMIT_HASH = sourceGitDetails.hash
env.GIT_SOURCE_COMMIT_TAG = sourceGitDetails.tag
echo "GIT Details: ${sourceGitDetails}"
// sh 'ls -R'
}
}
}
stage('Build Docker Image') {
steps {
script {
AWS_SERVICE_IMAGE = "${params.ecrImage}:build-${BUILD_NUMBER}"
docker_image = docker.build(AWS_SERVICE_IMAGE)
}
}
}
stage('Push Docker Image') {
steps {
script {
withAWS(role:"${AWS_ROLE}", roleAccount:"${AWS_ACC_ID}", duration: 900, roleSessionName: 'jenkins-session')
{
docker_creds = ecrLogin()
sh "${docker_creds}"
docker_image.push()
}
}
}
}
stage('Register New Task Definition') {
steps {
script {
NEW_TASK = ecsNewTaskDefinition(ECS_SERVICE, AWS_SERVICE_IMAGE, AWS_ACC_ID, AWS_ROLE, AWS_REGION)
echo "new task arn: ${NEW_TASK.taskDefinition.taskDefinitionArn}"
}
}
}
stage('Add Task Definition Tags') {
steps {
script {
def awsTags = [
git_hash: GIT_SOURCE_COMMIT_HASH,
git_tag: GIT_SOURCE_COMMIT_TAG,
git_repo: GITHUB_REPO,
pipeline: BUILD_URL
]
ecsTagTaskDefinition(
AWS_ROLE,
AWS_ACC_ID,
NEW_TASK.taskDefinition.taskDefinitionArn,
awsTags)
}
}
}
stage('Deploy New Task') {
when {
expression {
return TRIGGER_DEPLOY == "true"
}
}
steps {
script {
withAWS(role:"${AWS_ROLE}", roleAccount:"${AWS_ACC_ID}", duration: 900, roleSessionName: 'jenkins-session')
{
if (SKIP_DEPLOY_TEST == "true") { test_hook = "" }
else {
test_lambda_name = "${SERVICE_NAME}-deployment-test"
try {
lambda_check = sh (script:"aws lambda get-function --function-name ${test_lambda_name}", returnStdout: true)
test_hook = ", \\\"hooks\\\":[{\\\"AfterAllowTestTraffic\\\": \\\"${test_lambda_name}\\\"}]"
} catch (err) {
echo "Lambda not found for function, skipping post-deployment test"
test_hook = ""
}
}
appspec = "\"{\\\"Resources\\\":[{\\\"TargetService\\\":{\\\"Type\\\":\\\"AWS::ECS::Service\\\", \\\"Properties\\\":{ \\\"TaskDefinition\\\":\\\"${NEW_TASK.taskDefinition.taskDefinitionArn}\\\", \\\"LoadBalancerInfo\\\":{\\\"ContainerName\\\":\\\"${ECS_SERVICE}\\\",\\\"ContainerPort\\\":\\\"80\\\"}}}}], \\\"version\\\":\\\"0.0\\\"}${test_hook} \""
deploy_output = sh (
script: "aws deploy create-deployment \
--application-name ${ECS_SERVICE} \
--deployment-group-name ${ECS_SERVICE}-dg \
--revision '{\"revisionType\": \"AppSpecContent\",\"appSpecContent\":{\"content\": ${appspec}}}' \
--deployment-config-name ${DEPLOY_CONFIG} \
--description \"Initiated by: ${params.startedBy} - ${BUILD_URL}\" \
--auto-rollback-configuration enabled=true,events=DEPLOYMENT_FAILURE",
returnStdout: true)
echo "deploy_output: ${deploy_output}"
output_json = readJSON text: deploy_output
deployment_id = output_json["deploymentId"]
echo "deployment_id: ${deployment_id}"
}
}
}
}
stage('Wait for deployment to complete') {
when {
expression {
return TRIGGER_DEPLOY == "true"
}
}
steps {
script {
withAWS(role:"${AWS_ROLE}", roleAccount:"${AWS_ACC_ID}", duration: 900, roleSessionName: 'jenk-session')
{
wait_deploy_output = sh (
script: "aws deploy wait deployment-successful --deployment-id ${deployment_id}",
returnStdout: true)
echo "wait_deploy_output: ${wait_deploy_output}"
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment