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

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
Tags: ,,,