Find Lambda Functions on Deprecated Runtimes (AWS SDK v3)

Close-up of an old green computer circuit board with capacitors and chips

Photo by Umberto on Unsplash

To find Lambda functions with deprecated runtimes, call ListFunctions in every region you use and compare each function’s Runtime with the deprecation dates AWS publishes for Lambda runtimes. The script below does both, flags runtimes that are already deprecated or will be within 180 days, and skips container-image functions, which have no Runtime field. It only reads.

AWS emails the account’s primary contact before a runtime is deprecated, but that notice lists only the $LATEST version of each function, and it’s easy to lose in a shared inbox. This example is for engineers who own a Lambda estate spread over several regions and want to find Lambda functions with deprecated runtimes on demand, as a list they can sort, share and track. You’ll get a TypeScript script for the AWS SDK for JavaScript v3 that scans every enabled region and prints a table of functions to upgrade, with the dates that matter.

It belongs to our collection of runnable AWS SDK v3 examples. If you already know which functions run most often, the script to get Lambda invocation counts for the last 24 hours helps you decide which upgrades to do first.

What happens when a Lambda runtime is deprecated?

Deprecation happens in three steps, and each one has its own date in AWS’s schedule:

  1. Deprecation dateAWS may stop applying security patches to the runtime, functions using it lose eligibility for technical support, and the Lambda console stops letting you create or update functions on it. The AWS CLI, AWS SAM and CloudFormation still work.
  2. Block function createAt least 30 days later, Lambda stops accepting new functions on that runtime.
  3. Block function updateAt least 60 days after deprecation, Lambda blocks code and configuration updates to existing functions. You can still change the runtime to a supported one, but rolling back to the deprecated runtime may be blocked.

Functions on a deprecated runtime keep running and can still be invoked. The risk is quieter: no more language or OS patches, and the day you need an emergency fix, the update is blocked. The risk is higher for functions anyone can reach, which the script to find public Lambda function URLs lists. For many runtimes AWS pushed the block dates out to 1 February 2027 (create) and 3 March 2027 (update), which is why the table in the script tracks the update-block date separately. The schedule comes from the Lambda runtimes page in the AWS Lambda Developer Guide; the community tracker at endoflife.date’s AWS Lambda runtime timeline mirrors it with a change history. Dates in this article were checked in September 2026.

What does the script do?

  • Finds your regions. EC2 DescribeRegions returns the regions enabled for the account. Pass --regions to scan a fixed list instead.
  • Lists every function. paginateListFunctions in each region. With --all-versions it sets FunctionVersion: "ALL", because published versions keep the runtime they were published with, even after $LATEST is upgraded.
  • Skips container images. Functions with PackageType: "Image" have no Runtime; the base image decides. They’re counted and reported separately.
  • Compares against the schedule. A small table maps runtime identifiers such as nodejs18.x and python3.9 to their deprecation and update-block dates. Anything deprecated or deprecating within --days (default 180) goes in the report.

Prerequisites

Which IAM permissions does it need?

Two read-only actions. lambda:ListFunctions doesn’t support resource-level restrictions, so it uses *.

lambda-runtime-audit-policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListFunctionsInEveryRegion",
      "Effect": "Allow",
      "Action": [
        "lambda:ListFunctions",
        "ec2:DescribeRegions"
      ],
      "Resource": "*"
    }
  ]
}

If an organization SCP blocks some regions, the script prints the region as skipped and carries on. The walkthrough to troubleshoot AWS IAM access denied errors step by step helps when the denial is unexpected, and the IAM policy generator for TypeScript AWS code drafts a policy if you extend the script.

The full script to find Lambda functions with deprecated runtimes

find-deprecated-lambda-runtimes.ts

// find-deprecated-lambda-runtimes.ts
// Lists Lambda functions whose runtime is deprecated, or will be within --days,
// in every enabled region (or the regions you pass). Read-only: it changes nothing.
// Usage: npx tsx find-deprecated-lambda-runtimes.ts [--days 180] [--regions us-east-1,eu-west-1] [--all-versions]
import { EC2Client, DescribeRegionsCommand } from "@aws-sdk/client-ec2";
import { LambdaClient, paginateListFunctions, type FunctionConfiguration } from "@aws-sdk/client-lambda";

// Deprecation dates from the AWS Lambda runtimes page, checked September 2026.
// "blockUpdate" is when Lambda starts blocking code/config updates. Dates change: re-check before relying on them.
type Schedule = { deprecated: string; blockUpdate?: string };
const SCHEDULE: Record<string, Schedule> = {
  // Already deprecated
  "provided.al2": { deprecated: "2026-07-31", blockUpdate: "2027-03-03" },
  "nodejs20.x": { deprecated: "2026-04-30", blockUpdate: "2027-03-03" },
  "ruby3.2": { deprecated: "2026-03-31", blockUpdate: "2027-03-03" },
  "python3.9": { deprecated: "2025-12-15", blockUpdate: "2027-03-03" },
  "nodejs18.x": { deprecated: "2025-09-01", blockUpdate: "2027-03-03" },
  dotnet6: { deprecated: "2024-12-20", blockUpdate: "2027-03-03" },
  "python3.8": { deprecated: "2024-10-14", blockUpdate: "2027-03-03" },
  "nodejs16.x": { deprecated: "2024-06-12", blockUpdate: "2027-03-03" },
  java8: { deprecated: "2024-01-08", blockUpdate: "2027-03-03" },
  "go1.x": { deprecated: "2024-01-08", blockUpdate: "2027-03-03" },
  provided: { deprecated: "2024-01-08", blockUpdate: "2027-03-03" },
  "ruby2.7": { deprecated: "2023-12-07", blockUpdate: "2027-03-03" },
  "nodejs14.x": { deprecated: "2023-12-04", blockUpdate: "2027-03-03" },
  "python3.7": { deprecated: "2023-12-04", blockUpdate: "2027-03-03" },
  "dotnetcore3.1": { deprecated: "2023-04-03", blockUpdate: "2023-05-03" },
  "nodejs12.x": { deprecated: "2023-03-31", blockUpdate: "2023-04-30" },
  "python3.6": { deprecated: "2022-07-18", blockUpdate: "2022-08-29" },
  "dotnetcore2.1": { deprecated: "2022-01-05", blockUpdate: "2022-04-13" },
  "nodejs10.x": { deprecated: "2021-07-30", blockUpdate: "2022-02-14" },
  "ruby2.5": { deprecated: "2021-07-30", blockUpdate: "2022-03-31" },
  "python2.7": { deprecated: "2021-07-15", blockUpdate: "2022-05-30" },
  // Scheduled (still supported today)
  "python3.10": { deprecated: "2026-10-31", blockUpdate: "2027-03-03" },
  dotnet8: { deprecated: "2026-11-10", blockUpdate: "2027-03-03" },
  "ruby3.3": { deprecated: "2027-03-31", blockUpdate: "2027-05-31" },
  "nodejs22.x": { deprecated: "2027-04-30", blockUpdate: "2027-07-01" },
  "python3.11": { deprecated: "2027-06-30", blockUpdate: "2027-08-31" },
  java17: { deprecated: "2027-06-30", blockUpdate: "2027-08-31" },
  java11: { deprecated: "2027-06-30", blockUpdate: "2027-08-31" },
  "java8.al2": { deprecated: "2027-06-30", blockUpdate: "2027-08-31" },
};

function arg(name: string): string | undefined {
  const i = process.argv.indexOf(name);
  return i === -1 ? undefined : process.argv[i + 1];
}

async function enabledRegions(): Promise<string[]> {
  const res = await new EC2Client({}).send(new DescribeRegionsCommand({}));
  return (res.Regions ?? []).map((r) => r.RegionName ?? "").filter(Boolean).sort();
}

type Row = { Region: string; Function: string; Version: string; Runtime: string; Status: string; Deprecated: string; "Updates blocked": string };

async function main(): Promise<void> {
  const days = Number(arg("--days") ?? 180);
  const allVersions = process.argv.includes("--all-versions");
  const regions = arg("--regions")?.split(",") ?? (await enabledRegions());
  const horizon = Date.now() + days * 86_400_000;

  const rows: Row[] = [];
  let checked = 0;
  let images = 0;
  for (const region of regions) {
    const lambda = new LambdaClient({ region });
    const fns: FunctionConfiguration[] = [];
    // FunctionVersion "ALL" also returns published versions, which keep the runtime they were published with.
    try {
      for await (const page of paginateListFunctions({ client: lambda }, allVersions ? { FunctionVersion: "ALL" } : {})) {
        fns.push(...(page.Functions ?? []));
      }
    } catch (err) {
      console.warn(`${region}: skipped (${(err as Error).name})`); // e.g. an SCP denies this region
      continue;
    }
    for (const fn of fns) {
      checked++;
      if (fn.PackageType === "Image" || !fn.Runtime) {
        images++; // container images have no Runtime field: the base image decides
        continue;
      }
      const s = SCHEDULE[fn.Runtime];
      if (!s) continue; // supported runtime with no deprecation inside the table
      const deprecatedAt = new Date(`${s.deprecated}T00:00:00Z`).getTime();
      if (deprecatedAt > horizon) continue;
      rows.push({
        Region: region,
        Function: fn.FunctionName ?? "",
        Version: fn.Version ?? "$LATEST",
        Runtime: fn.Runtime,
        Status: deprecatedAt <= Date.now() ? "DEPRECATED" : `in ${Math.ceil((deprecatedAt - Date.now()) / 86_400_000)} days`,
        Deprecated: s.deprecated,
        "Updates blocked": s.blockUpdate ?? "",
      });
    }
  }

  rows.sort((a, b) => a.Deprecated.localeCompare(b.Deprecated) || a.Function.localeCompare(b.Function));
  if (rows.length > 0) console.table(rows);
  const deprecated = rows.filter((r) => r.Status === "DEPRECATED").length;
  console.log(`${checked} functions checked in ${regions.length} regions${allVersions ? " (all versions)" : " ($LATEST only)"}.`);
  console.log(`${deprecated} on a deprecated runtime, ${rows.length - deprecated} deprecating within ${days} days.`);
  if (images > 0) console.log(`${images} container-image functions skipped: check their base images separately.`);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

Runtimes that aren’t in SCHEDULE, such as nodejs24.x or python3.13, aren’t reported, because their deprecation dates are more than a year away. When AWS announces a new date, add a line to the table.

How do you run it?

Terminal

npm install @aws-sdk/client-lambda @aws-sdk/client-ec2
npm install --save-dev tsx typescript

# Every enabled region, $LATEST only, 180-day warning window
AWS_PROFILE=readonly AWS_REGION=us-east-1 npx tsx find-deprecated-lambda-runtimes.ts

# Two regions, include published versions, warn a year ahead
AWS_PROFILE=readonly AWS_REGION=us-east-1 npx tsx find-deprecated-lambda-runtimes.ts --regions us-east-1,eu-west-1 --all-versions --days 365

Sample output

Output

┌─────────┬─────────────┬──────────────────────┬───────────┬──────────────┬───────────────┬──────────────┬─────────────────┐
│ (index) │ Region      │ Function             │ Version   │ Runtime      │ Status        │ Deprecated   │ Updates blocked │
├─────────┼─────────────┼──────────────────────┼───────────┼──────────────┼───────────────┼──────────────┼─────────────────┤
│ 0       │ 'eu-west-1' │ 'legacy-thumbnailer' │ '$LATEST' │ 'nodejs16.x' │ 'DEPRECATED'  │ '2024-06-12' │ '2027-03-03'    │
│ 1       │ 'us-east-1' │ 'nightly-report'     │ '$LATEST' │ 'nodejs18.x' │ 'DEPRECATED'  │ '2025-09-01' │ '2027-03-03'    │
│ 2       │ 'us-east-1' │ 'stripe-webhook'     │ '$LATEST' │ 'python3.9'  │ 'DEPRECATED'  │ '2025-12-15' │ '2027-03-03'    │
│ 3       │ 'us-east-1' │ 'orders-api'         │ '$LATEST' │ 'nodejs20.x' │ 'DEPRECATED'  │ '2026-04-30' │ '2027-03-03'    │
│ 4       │ 'us-east-1' │ 'etl-loader'         │ '$LATEST' │ 'python3.10' │ 'in 34 days'  │ '2026-10-31' │ '2027-03-03'    │
│ 5       │ 'eu-west-1' │ 'invoice-pdf'        │ '$LATEST' │ 'dotnet8'    │ 'in 44 days'  │ '2026-11-10' │ '2027-03-03'    │
└─────────┴─────────────┴──────────────────────┴───────────┴──────────────┴───────────────┴──────────────┴─────────────────┘
87 functions checked in 17 regions ($LATEST only).
4 on a deprecated runtime, 2 deprecating within 180 days.
3 container-image functions skipped: check their base images separately.

Function names are illustrative. Sort the upgrade list by the Updates blocked column: after that date, a deprecated function can’t take a hotfix until its runtime is changed.

How do you upgrade a function once you’ve found it?

Changing the runtime is a configuration update (UpdateFunctionConfiguration with a new Runtime), but test the code first, because major language versions aren’t guaranteed to be backward compatible. Two traps come up often with Node.js:

After the upgrade, deploy and call the function once with the script to invoke a Lambda function with AWS SDK v3 in TypeScript, then watch its error rate. The guide on how to investigate Lambda errors with CloudWatch shows where the first failures will surface. While you’re redeploying, check the memory setting too; the script to find Lambda functions with too much memory compares each function’s peak usage with it.

Troubleshooting

  • UnrecognizedClientException or AccessDenied in one region. The region is enabled for the account but denied by a service control policy. The script prints it as skipped and moves on; pass --regions to leave it out entirely.
  • A function you expected is missing. It may be a container image (counted in the last line) or on a runtime more than --days away from deprecation. Raise --days to widen the window.
  • Lambda@Edge functions. They live in us-east-1 and are listed there with the other functions in that region.
  • The dates look wrong. AWS sometimes moves deprecation and block dates. Compare the table with the current Lambda runtimes page and update it.

Ask ChatWithCloud instead

You can also ask ChatWithCloud “Which Lambda functions use a Node.js or Python runtime that’s deprecated?” It writes AWS SDK for JavaScript v2 code, runs it on your machine with your AWS profile and answers in plain English, as in the guide to ask AI about Lambda errors in your AWS account. One profile and region per session, so ask about one region at a time. ChatWithCloud runs generated code without a confirmation step, so connect ChatWithCloud to AWS with a read-only profile for audits like this, and see what ChatWithCloud sends and stores before you start.

Frequently asked questions

How do I list Lambda functions by runtime in the AWS CLI?

Run aws lambda list-functions --query "Functions[?Runtime=='nodejs18.x'].FunctionName" in each region. Add --function-version ALL to include published versions.

Do Lambda functions stop working when the runtime is deprecated?

No. You can keep invoking them, but AWS may stop patching the runtime, they lose technical support, and after the block-update date you can’t change their code or configuration without moving to a supported runtime.

How do I check container-image Lambda functions?

ListFunctions doesn’t return a runtime for them. Check the base image in each function’s Dockerfile, and rebuild from a supported AWS base image.

Does Trusted Advisor show deprecated Lambda runtimes?

Yes. The AWS Lambda Functions Using Deprecated Runtimes check lists affected $LATEST and published versions, which makes it a useful cross-check for the script.

Related guides

Ask your AWS account in plain English

Your first 15 runs are free, with no OpenAI key needed.

npx chatwithcloud