5

Before I have Lambda version and alias, based on the API update-function-code I can successfully deploy by

aws lambda update-function-code --function-name myFunction --zip-file fileb://archive.zip

After I added version and alias, I have Lambda version

  • $LATEST (default)
  • 1
  • 2

and alias

  • staging (point to version 1)
  • prod (point to version 2)

I try to deploy to staging (version 1),


1st Try

aws lambda update-function-code --function-name arn:aws:lambda:us-west-2:123456789000:function:myFunction:staging --zip-file fileb://archive.zip

gave error

Current operation does not support versions other than $LATEST. Please set the version to $LATEST or do not set a version in your request.


2nd Try

aws lambda update-function-code --function-name myFunction --zip-file fileb://archive.zip --s3-object-version 1

or

aws lambda update-function-code --function-name myFunction --zip-file fileb://archive.zip --s3-object-version staging

gave error

Please do not provide other FunctionCode parameters when providing a ZipFile.


How to use AWS CLI to deploy lambda function to specific alias or version correctly? Thanks

3 Answers 3

14

Okay so assuming you already managed to deploy your lambda with this command :

aws lambda update-function-code --function-name $FUNCTION_NAME --zip-file fileb://lambda.zip

And that you have created 2 versions with 2 alias (staging an prod).

Now you just need to publish to the rigth version :

VERSION=1
aws lambda update-alias --function-name $FUNCTION_NAME --name staging --function-version $VERSION
VERSION=2
aws lambda update-alias --function-name $FUNCTION_NAME --name prod --function-version $VERSION

If you want to go one step further you can bind the last deployment to the latest version. So first you need to retrieve the latest version and for this I use jq but feel free to use whatever you want, and then update with this version.

VERSION=$(aws lambda publish-version --function-name $FUNCTION_NAME | jq -r .Version)
aws lambda update-alias --function-name $FUNCTION_NAME --name staging --function-version $VERSION

Here is the update-alias documentation. And here is the publish-version documention.

Sign up to request clarification or add additional context in comments.

1 Comment

jq is very helpful! You must read my mind, that is exact question came in my mind - how to get result from command!
3

based on AWS documentations

A published version is immutable. That is, you can't change the code or configuration information.

so you need to publish a new version of your function and then update the alias to point to the newly created version.

Comments

0

Lambda function, CloudFront automated deployment solution

Usage Scenario

Automatically update Lambda functions in CI, and update the Distribution configuration under Cloudfront, everything is automated.

Operating Principle

In this process, the user (U) executes a script called deploy:lambda. The script first requests the latest Lambda function version and the remote code hash from AWS. It then calculates the local code hash and compares it with the remote hash. If code changes are detected, the script publishes the updated Lambda function code and waits for the deployment to stabilize. Finally, the script updates the CloudFront distribution configuration. If no code changes are detected, the script informs the user that no deployment is needed and ends the process.

Github Action

  • release-lambda.yml
name: release-lambda

env:
  NODE_OPTIONS: '--max_old_space_size=6144'

on:
  push:
    branches:
      - master

jobs:
  release-lambda:
    runs-on: ubuntu-latest

    if: "! contains(toJSON(github.event.commits.*.message), '[skip actions]')"

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        with:
          ref: master
          token: ${{ secrets.GIT_HUB_TOKEN }}

      - uses: pnpm/action-setup@v4
        name: Install pnpm
        with:
          version: 9

      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Initialize
        run: pnpm install // or npm install, yarn install

      - name: Build
        run: npm run build

      - name: 🔑 配置 AWS 凭证
        uses: aws-actions/configure-aws-credentials@master
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ vars.AWS_REGION }}

      - name: Deploy Lambda
        run: |
          export AWS_LAMBDA_DISTRIBUTION_IDS=${{ vars.AWS_LAMBDA_DISTRIBUTION_IDS }}
          export AWS_LAMBDA_FUNCTION_NAME=${{ vars.AWS_LAMBDA_FUNCTION_NAME }}
          npm run deploy:lambda

  • package.json
  "scripts": {
    "deploy:lambda": "zx --quiet scripts/deploy-lambda.mjs"
  }
  • scripts/deploy-lambda.mjs
#!/usr/bin/env zx

import { join } from 'path';
import { cwd } from 'process';

const { writeJsonSync } = require('fs-extra'); // Ensure fs-extra is installed
const fs = require('fs');

require('dotenv').config({ path: join(cwd(), '.env.local') });

class LambdaCloudFrontDeployer {
  constructor(distributionIds, functionName) {
    this.distributionIds = distributionIds.split(',').map(id => id.trim()).filter(Boolean);
    this.functionName = functionName;
    this.functionZip = join(cwd(), 'blocklets/cloudfront-shield/dist/function.zip');
  }

  async getLatestVersion() {
    const latestVersion = await $`aws lambda list-versions-by-function --function-name ${this.functionName} --query 'Versions[-1].Version' --output text`;
    return latestVersion;
  }

  async shouldDeploy() {
    const localFunctionCodeHash = await this.getLocalFunctionCodeHash();
    const remoteFunctionCodeHash = await this.getRemoteFunctionCodeHash();

    console.log({
      remoteFunctionCodeHash,
      localFunctionCodeHash
    });

    return remoteFunctionCodeHash !== localFunctionCodeHash; 
  }

  async getRemoteFunctionCodeHash() {
    const codeSha256 = await $`aws lambda list-versions-by-function --function-name ${this.functionName} --query 'Versions[-1].CodeSize' --output text`;
    const remoteHash = parseInt(codeSha256.stdout.trim());
    return remoteHash; 
  }

  async getLocalFunctionCodeHash() {
    const stat = fs.statSync(this.functionZip);
    const localHash = stat.size; // Return the new code's hash value
    return localHash;
  }

  async deploy() {
    if (!(await this.shouldDeploy())) {
      console.log("No changes detected in the function code. Skipping function deploy.");
      return; // No code changes, return directly
    }
    
    await this.publish();
    await this.updateDistributions();
  }

  async publish() {
    console.log("Publish...");

    // Update Lambda function code
    await $`aws lambda update-function-code --function-name ${this.functionName} --zip-file fileb://${this.functionZip}`;

    await new Promise(resolve => setTimeout(resolve, 30000)); // Wait for 30 seconds

    console.log("Get latest version...");
    // Publish new version and get version number
    const { stdout: latestVersion } = await $`aws lambda publish-version --function-name ${this.functionName}`;
    const latestFunctionVersion = JSON.parse(latestVersion).Version;
    console.log(`Latest version: ${latestFunctionVersion}`);

    // Update alias
    await $`aws lambda update-alias --function-name ${this.functionName} --name "production" --function-version ${latestFunctionVersion}`;

    console.log("Deployment complete!");
  }

  async updateDistributions() {
    await Promise.all(this.distributionIds.map(id => this.updateDistribution(id)));
  }

  async updateDistribution(distributionId) {
    const latestVersion = await this.getLatestVersion();

    const output = await $`aws cloudfront get-distribution-config --id ${distributionId}`;
    console.log('updateDistribution', distributionId, output.exitCode);

    const data = JSON.parse(output.stdout);
    const etag = data.ETag;
    const config = data.DistributionConfig;
    config.CacheBehaviors.Items.forEach((item) => {
      item.LambdaFunctionAssociations.Items?.forEach((item) => {
        if (item.LambdaFunctionARN && item.LambdaFunctionARN.includes(this.functionName)) {
          console.log(item.LambdaFunctionARN);
          item.LambdaFunctionARN = `${this.functionName}:${latestVersion}`.trim();
        }
      });
    });

    const configPath = join(cwd(), `${distributionId}.config.json`);
    writeJsonSync(configPath, config);

    await $`
    aws cloudfront update-distribution --id ${distributionId} --if-match ${etag} --distribution-config file://${configPath}`;

    console.log(`updateDistribution(${distributionId}) successfully`);
  }
}

const distributionIds = process.env.AWS_LAMBDA_DISTRIBUTION_IDS;
const functionName = process.env.AWS_LAMBDA_FUNCTION_NAME;

const deployer = new LambdaCloudFrontDeployer(distributionIds, functionName);
await deployer.deploy();

console.log('done');

How to test in local

  • env.local in your project root folder
AWS_LAMBDA_DISTRIBUTION_IDS=xxx
AWS_LAMBDA_FUNCTION_NAME=arn:aws:lambda:us-east-1:xxx:function:verify
AWS_PROFILE=
  • Exec command in terminal
npm run deploy:lambda

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.