Last active
November 15, 2024 18:15
-
-
Save chrisj-au/594f8acf94791814820f8faabe5221f5 to your computer and use it in GitHub Desktop.
Jenkins pipelines for deploying image to ECS via CodeDeploy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 = "[email protected]: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) | |
] | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" | |
} | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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