4

I would like to update the cloudfront distribution with the latest lambda@edge function using CLI.

I saw this documentation, but could not figure out how to update the lambda ARN only.

Can some one help?

Itai Klapholtz
  • 196
  • 1
  • 2
  • 15
cloudbud
  • 2,948
  • 5
  • 28
  • 54

4 Answers4

10

Here is the script, that is doing exactly that. It is implemented based on @cloudbud answer. There is no argument checking. It would be executed like this: ./script QF234ASD342FG my-lambda-at-edge-function us-east-1. In my case, the execution time is less than 10 sec. See update-distribution for details.

#!/bin/bash

set -xeuo pipefail
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin

distribution_id="$1"
function_name="$2"
region="$3"

readonly lambda_arn=$(
  aws lambda list-versions-by-function \
    --function-name "$function_name" \
    --region "$region" \
    --query "max_by(Versions, &to_number(to_number(Version) || '0'))" \
  | jq -r '.FunctionArn'
)

readonly tmp1=$(mktemp)
readonly tmp2=$(mktemp)

aws cloudfront get-distribution-config \
  --id "$distribution_id" \
> "$tmp1"

readonly etag=$(jq -r '.ETag' < "$tmp1")

cat "$tmp1" \
| jq '(.DistributionConfig.CacheBehaviors.Items[] | select(.PathPattern=="dist/sxf/*") | .LambdaFunctionAssociations.Items[] | select(.EventType=="origin-request") | .LambdaFunctionARN ) |= "'"$lambda_arn"'"' \
| jq '.DistributionConfig' \
> "$tmp2"

# the dist config has to be in the file
# and be referred in specific way.
aws cloudfront update-distribution \
  --id "$distribution_id" \
  --distribution-config "file://$tmp2" \
  --if-match "$etag"

rm -f "$tmp1" "$tmp2"
Rax
  • 470
  • 5
  • 15
  • 3
    very good, just need to adapt the path for myself (from .DistributionConfig.CacheBehaviors.Items[] to DistributionConfig.DefaultCacheBehavior) and everything worked like a charm. thank you so much – Jaoued Zahraoui Mar 08 '21 at 16:40
2

could not figure out how to update the lambda arn only.

The link that you provided explains the process:

The update process includes getting the current distribution configuration, updating the XML document that is returned to make your changes, and then submitting an UpdateDistribution request to make the updates.

This means that you can't just update lambda arn directly. You have:

  1. Call get-distribution-config to obtain full current configuration.

  2. Change the lambda arn in the configuration data obtained.

  3. Upload the entire new configuration using update-distribution.

The process requires extra attention which is also explained in the docs under Warning:

You must strip out the ETag parameter that is returned.

Additional fields are required when you update a distribution.

and more.

The process is indeed complex. Thus if you can I would recommend trying this on some test/dummy CloudFront distribution rather than directly on the production version.

Marcin
  • 215,873
  • 14
  • 235
  • 294
  • what do you mean stripping the etag parameter? – cloudbud Jun 30 '20 at 13:14
  • @cloudbud Hi. Have you run the command? I'm unable to replicate your setup. Only you can test it as you have your distribution. – Marcin Jun 30 '20 at 21:47
  • I have ran the commands and it updates the lambda function version, what do you mean. by strip the ETAG parameter? – cloudbud Jul 01 '20 at 08:11
  • @cloudbud That's what the linked instruction say. Maybe its not needed in your case if its working. – Marcin Jul 01 '20 at 08:12
  • The distribution has been updated but i dont see the sme version as deployed when i check the lambda function – cloudbud Jul 01 '20 at 11:48
1

Something like this:

#!/bin/bash
set -x
TEMPDIR=$(mktemp -d)
CONFIG=$(aws cloudfront get-distribution-config --id CGSKSKLSLSM)
ETAG=$(echo "${CONFIG}" | jq -r '.ETag')
echo "${CONFIG}" | jq '.DistributionConfig' > ${TEMPDIR}/orig.json
echo "${CONFIG}" | jq '.DistributionConfig | .DefaultCacheBehavior.LambdaFunctionAssociations.Items[0].LambdaFunctionARN= "arn:aws:lambda:us-east-1:xxxxx:function:test-func:3"' > ${TEMPDIR}/updated.json
aws cloudfront update-distribution --id CGSKSKLSLSM --distribution-config file://${TEMPDIR}/updated.json --if-match "${ETAG}"
cloudbud
  • 2,948
  • 5
  • 28
  • 54
  • 1
    You can't set the 0 index for the LambdaFunctionAssociations array. You need to update regarding the entrypoint of the lambda@edge – Mr_DeLeTeD Oct 04 '21 at 12:51
0

In case you need a Jenkins job to do this, I ended up with the following

#!groovy

def functionName = "<insert yours here>"
def versions = []

pipeline {
    stages {
        stage('Resolve lambda versions') {
            steps {
                script {
                    withAWS(role: "${JENKINS_ROLE}", roleAccount: "${AWS_ACCOUNT}", region: "us-east-1") {
                        versions = sh(returnStdout: true, script: "aws lambda list-versions-by-function " +
                                "--function-name ${functionName} --query 'Versions[].Version' --output text").replaceAll('\\$LATEST', "")
                                .trim()
                                .split("\t")
                                .collect { it as int }
                                .sort()
                                .reverse()
                        echo "Versions found ${versions}"
                    }
                }
            }
        }

        stage('Update CloudFront distribution') {
            steps {
                script {
                    withAWS(role: "${JENKINS_ROLE}", roleAccount: "${AWS_ACCOUNT}", region: "us-east-1") {
                        def distributionId = "<insert yours here>"
                        def config = UUID.randomUUID().toString()
                        def distributionConfig = UUID.randomUUID().toString()
                        sh "aws cloudfront get-distribution-config --id ${distributionId} > ${config}"
                        def etag = sh(returnStdout: true, script: "cat ${config} | jq -r .ETag").trim()
                        sh """
                            sed -i 's/\\("LambdaFunctionARN": \".*:${functionName}:\\)[^:]*\"/\\1${versions.first()}"/' ${config}
                        """
                        sh "cat ${config} | jq -r '.DistributionConfig' > ${distributionConfig}"
                        sh "aws cloudfront update-distribution --id ${distributionId} --if-match ${etag} --distribution-config file://${distributionConfig}"
                    }
                }
            }
        }

        stage('Remove old lambda versions') {
            steps {
                script {
                    withAWS(role: "${JENKINS_ROLE}", roleAccount: "${AWS_ACCOUNT}", region: "us-east-1") {
                        if (versions.size() < 3) {
                            echo "Not deleting any versions!"
                        } else {
                            versions[2..-1].each { version ->
                                sh "aws lambda delete-function --function-name ${functionName}:${version}"
                            }
                        }
                    }
                }
            }
        }
    }
}

Last stage is optional, but it is generally a good idea not to pile up older lambda versions

Enigo
  • 3,685
  • 5
  • 29
  • 54