How to reference your AWS Elastic Beanstalk application version to your CodePipeline Commit
An easy way to get a reference from your application version in Elastic Beanstalk to the commit data in CodePipeline
Whats the problem?
Every Commit from our developer team triggers the CodePipeline which builds and deploys the application to Elastic Beanstalk. In Elastic Beanstalk we get cryptical build outputs in the application version-label for e.g.:
code-pipeline-4322432423424-build_output-d0h3j24h234-b742–4668-abb6–0f24e443424r8d31
and the description of the version for e.g.:
build_output-d0h3j24h234-b742–4668-abb6–0f24e443424r8d31
So we had no chance without taking a quick look in the s3 source data or CodeCommit repository to see the actual version and latest commit message.
Lambda function for providing what AWS should provide
The first step is upgrading our Continuous Deployment CodePipeline with a Lambda stage.
Source: AWS CodeCommit
Build: AWS CodeBuild
Approval: Manual Approval
Deploy: AWS Elastic Beanstalk
Lambda: AWS Lambda
After a successful deploy the Lambda stage is starting. We set build_output as input artifact and create #{source_variables.CommitMessage} as User Parameter, so we can access them in the Lambda function. Now we are ready to start with Lambda.
As we did the first try with a basic Lambda function we did not know whats inside of the given event from CodePipeline in detail, so we printed the event for understanding the input.
def lambda_handler(event, context):
print(“Event: {}”.format(event))
# Set up Boto3 client to CodePipeline
cp_client = boto3.client('codepipeline')
# Put Info to Code Pipeline, if Code succeeded
cp_client.put_job_success_result(jobId=job_id)
return
A event JSON with our configuration looks like the following.
{
“CodePipeline.job”: {
“id”: “xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”,
“accountId”: “xxxxxxxxxxx”,
“data”: {
“actionConfiguration”: {
“configuration”: {
“FunctionName”: “string”,
“UserParameters”: “CommitMessage”
}
},
"inputArtifacts": [{
"name": "build_output",
"revision": null,
"location": {
"type": "S3",
"s3Location": {
"bucketName": "xxxxxxxxxxx-bucket",
"objectKey": "xxxx/build_outp/74sD8d"
}
}
}],
"outputArtifacts": [],
"artifactCredentials": {
"accessKeyId": "xxxxxxxxxxx",
"secretAccessKey": "xxxxxxxxxxx",
"sessionToken": "xxxxxxxxxxx",
"expirationTime": 1627916317000
}
}
}
}
As we knew the details of the event we could start coding. The first step was setting up the Boto3 clients
eb_client = boto3.client(‘elasticbeanstalk’)
cp_client = boto3.client(‘codepipeline’)
s3_resource = boto3.resource(‘s3’)
Now we can read out the needed data from the incoming event.
try:
# Get variables from event input
commit_message = event['CodePipeline.job']['data']['actionConfiguration']['configuration']['UserParameters']
s3_bucket_name = event['CodePipeline.job']['data']['inputArtifacts'][1]['location']['s3Location']['bucketName']
s3_object_key = event['CodePipeline.job']['data']['inputArtifacts'][1]['location']['s3Location']['objectKey']
except Exception as e:
print("Error event")
cp_client.put_job_failure_result(jobId=job_id, failureDetails={'message': ('Function exception: ' + str(e)), 'type': 'JobFailed'})
return
With s3 bucket name and key from above we can read out the package.json from our build_output on the s3 bucket.
try:
# Read out data from package.json on s3 zip object
with tempfile.TemporaryFile() as f:
s3_resource.meta.client.download_fileobj(s3_bucket_name, s3_object_key, f)
archive = zipfile.ZipFile(f)
package_json = archive.open(‘package.json’)
data = package_json.read()
json_data = json.loads(data)
application_version = json_data[‘version’]
application_name = “{}-terraform”.format(json_data[‘name’])
pipeline_name = “{}-pipeline”.format(json_data[‘name’])
except Exception as e:
print(“Error s3”)
cp_client.put_job_failure_result(jobId=job_id, failureDetails={‘message’: (‘Function exception: ‘ + str(e)), ‘type’: ‘JobFailed’})
return
And now we have all the information, so we can update the Elastic Beanstalk application version.
try:
# Read out newest version-label from environemt
environment = eb_client.describe_environments(ApplicationName=application_name)
version_label = environment[‘Environments’][0][‘VersionLabel’]
# Build description string
description = ‘Version: {} — Commit Message: {}’.format(application_version, commit_message)
# Write new Description to given elastic Beanstalk Application version
eb_response = eb_client.update_application_version(
ApplicationName=application_name,
VersionLabel=version_label,
Description=description
)
except Exception as e:
print(“Error Eb”)
cp_client.put_job_failure_result(jobId=job_id, failureDetails={‘message’: (‘Function exception: ‘ + str(e)), ‘type’: ‘JobFailed’})
return
After a successful run we have to give feedback to CodePipeline
# Put Info to Code Pipeline, if Code succeeded
cp_client.put_job_success_result(jobId=job_id)
That’s it, now we have a reference from our application versions to source code version and commits from our developer team.
Conclusion
If we now have to switch back to a previous version, we have in Elastic Beanstalk a direct overview of which commit and which version are included in which application version. Hopefully this will help some decide which referencing options fit to their project. You can also check out my code in GitHub.