Jenkins Amazon ECR token update
With the use of the Jenkins Docker Pipeline plugin, it’s easy to build and push Docker images.
For example, building in a Jenkinsfile:
script { dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}", "${args}") }
And push:
script { docker.withRegistry("${env.DOCKER_PRIVATE_REGISTRY_URL}", "docker-private-registry-${env.DEPLOY_ENVIRONMENT}") { dockerImage.push("${env.DOCKER_IMAGE_SHORT_PUSH_TAG}") } }
The Docker registry username and password are provided by the credential ID “docker-private-registry-${env.DEPLOY_ENVIRONMENT}”. However Amazon ECR uses tokens that are only valid for 12 hours. So the password that you specify when creating the credential will only work in the example above for a short period of time.
With the following script it’s easy to update the password / token periodically. It’s tested on Jenkins version 2.138.2.
Install the AWS CLI
AWS CLI installation
Make sure your AWS credentials are in $JENKINS_HOME/.aws/credentials and the region is in $JENKINS_HOME/.aws/config.
Example $JENKINS_HOME/.aws/credentials file:
[default] aws_access_key_id = MyAccessKey2I2JA aws_secret_access_key = ksdfm3N2o1wnwnsbaqqadummy982
Example $JENKINS_HOME/.aws/config file:
[default] region = eu-west-1
Install the groovy plugin
Install it with the Jenkins update center / manage plugins page. The plugin page is at https://plugins.jenkins.io/groovy.
Create a freestyle job
Job parameters
- CREDENTIAL_ID: This is the ID of the username/password credential created in Jenkins
- REGION: The AWS region
Build periodically
Build every 10 hours with “H H/10 * * *”.
System Groovy build step
// Add the content of this script as inline script of a "Execute system Groovy script" build step. // That build step also has the option to execute from a script file, but that has too much security restrictions. // The job must have a job parameter "CREDENTIAL_ID" and "REGION". import jenkins.model.* import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl def changePassword = { id, new_password -> def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials( com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class, Jenkins.instance) def c = creds.findResult { it.id == id ? it : null } if (c) { println "Found credential with ID \"${c.id}\"" def credentials_store = Jenkins.instance.getExtensionList( 'com.cloudbees.plugins.credentials.SystemCredentialsProvider' )[0].getStore() def result = credentials_store.updateCredentials( com.cloudbees.plugins.credentials.domains.Domain.global(), c, new UsernamePasswordCredentialsImpl(c.scope, id, c.description, c.username, new_password)) if (result) { println "Changed password of credential \"${id}\"" } else { println "Failed to change password of credential \"${id}\"" } } else { println "Could not find credential ID \"${id}\"" } } // Get job parameters def credentialId = build.buildVariableResolver.resolve('CREDENTIAL_ID') def region = build.buildVariableResolver.resolve('REGION') println "Credential ID \"${credentialId}\"" println "Region \"${region}\"" println "Calling AWS for docker login" def prs = "/usr/bin/aws ecr get-login --no-include-email --region ${region}".execute() prs.waitFor() def logintext = prs.text if (prs.exitValue()) { println "Got error from aws cli" throw new Exception() } else { def password = logintext.split(" ")[5] println "Updating password" changePassword(credentialId, password) }
Create ECR repository if not existing
If a Docker image is pushed to a regular Docker registry, the repository is created on first time push if it doesn’t exist yet. This is not the case with Amazon ECR. You have to make sure the repository exists before push.
Create in your Jenkinsfile something like:
if (env.DOCKER_PRIVATE_REGISTRY_URL.contains('.ecr.')) { // Docker registry automatically creates a repository on first time push, but Amazon ECR // requires a separate creation step first sh "${env.JENKINS_HOME}/custom/aws/ecr-ensure-repository.sh ${env.DOCKER_IMAGE}" }
The content from ecr-ensure-repository.sh:
#!/usr/bin/env bash repository_name=$1 if [ -z "$repository_name" ]; then echo 'Repository name is required as first argument' exit 1 fi aws ecr describe-repositories | grep repositoryName | grep "\"$repository_name\"" > /dev/null exit_code=$? if [ ${exit_code} -gt 0 ]; then echo "Repository $repository_name not found, creating..." aws ecr create-repository --repository-name ${repository_name} fi