Skip to content

Instantly share code, notes, and snippets.

@tobywf
Last active April 11, 2024 06:52
Show Gist options
  • Save tobywf/6eb494f4b46cef367540074512161334 to your computer and use it in GitHub Desktop.
Save tobywf/6eb494f4b46cef367540074512161334 to your computer and use it in GitHub Desktop.
A quick script to remove old AWS Lambda function versions
from __future__ import absolute_import, print_function, unicode_literals
import boto3
def clean_old_lambda_versions():
client = boto3.client('lambda')
functions = client.list_functions()['Functions']
for function in functions:
versions = client.list_versions_by_function(FunctionName=function['FunctionArn'])['Versions']
for version in versions:
if version['Version'] != function['Version']:
arn = version['FunctionArn']
print('delete_function(FunctionName={})'.format(arn))
#client.delete_function(FunctionName=arn) # uncomment me once you've checked
if __name__ == '__main__':
clean_old_lambda_versions()
@devonik
Copy link

devonik commented Jul 25, 2022

More revisions. We don't use aliases, so this keeps the most recent two by version number.

import boto3

def clean_old_lambda_versions(client):
    functions = client.list_functions()['Functions']
    for function in functions:
        arn = function['FunctionArn']
        print(arn)
        all_versions = []

        versions = client.list_versions_by_function(
            FunctionName=arn)
        # Page through all the versions
        while True:
            page_versions = [int(v['Version']) for v in versions['Versions'] if not v['Version'] == '$LATEST']
            all_versions.extend(page_versions)
            try:
                marker = versions['NextMarker']
            except:
                break
            versions = client.list_versions_by_function(
                FunctionName=arn, Marker=marker)

        # Sort and keep the last 2
        all_versions.sort()
        print('Which versions must go?')
        print(all_versions[0:-2])
        print('Which versions will live')
        print(all_versions[-2::])
        for chopBlock in all_versions[0:-2]:
            functionArn = '{}:{}'.format(arn, chopBlock)
            print('When uncommented, will run: delete_function(FunctionName={})'.format(functionArn))
            # I want to leave this commented in Git for safety so we don't run it unscrupulously
            # client.delete_function(FunctionName=functionArn)  # uncomment me once you've checked

if __name__ == '__main__':
    client = boto3.client('lambda', region_name='us-east-1')
    clean_old_lambda_versions(client)

Nice. I tried all above. And this Code were what I need thank you. Even care about the pagination. Cleanup everything except two latest version numbers

@unilynx
Copy link

unilynx commented Sep 18, 2022

And a nodejs version to delete function versions (not layers as the earlier nodejs code does)

delete-old-lambda-versions.mjs

import AWS from 'aws-sdk';

//Delete all but 'keepversions' highest numbered numeric versions
const keepversions = 1;
const lambda = new AWS.Lambda({ region: 'us-east-1' });

async function main()
{
  const listfunctions = await lambda.listFunctions().promise();

  for(let func of listfunctions.Functions)
  {
    let versions = (await lambda.listVersionsByFunction({FunctionName: func.FunctionArn}).promise()).Versions;
    versions.forEach(_ => _.versionnumber = parseInt(_.Version) ) //eliminate '$LATEST'
    versions = versions.filter(_ => !isNaN(_.versionnumber))
    //sort in reverse order
    versions = versions.sort((lhs,rhs) => rhs.versionnumber - lhs.versionnumber);

    let keep = versions.slice(0,keepversions);
    let remove = versions.slice(keepversions);
    console.log(`${func.FunctionArn}: Keeping ${JSON.stringify(keep.map(_ => _.Version))}, deleting ${JSON.stringify(remove.map(_ => _.Version))}`);

    for(let version of remove)
      await lambda.deleteFunction({ FunctionName: version.FunctionArn }).promise() //version FunctionArn embeds the version number
  }
}

main().then(() => process.exit(0)).catch(e => { console.error(e); });

@hoIIer
Copy link

hoIIer commented Oct 27, 2022

This worked for me to delete all but the live alias/most recent version:

import boto3

functions_paginator = client.get_paginator('list_functions')
version_paginator = client.get_paginator('list_versions_by_function')

for function_page in functions_paginator.paginate():
    for function in function_page['Functions']:
        aliases = client.list_aliases(FunctionName=function['FunctionArn'])
        alias_versions = [alias['FunctionVersion'] for alias in aliases['Aliases']]
        for version_page in version_paginator.paginate(FunctionName=function['FunctionArn']):
            for version in version_page['Versions']:
                arn = version['FunctionArn']
                if version['Version'] != function['Version'] and version['Version'] not in alias_versions:
                    print('  🥊 {}'.format(arn))
                    # client.delete_function(FunctionName=arn)
                else:
                    print('  💚 {}'.format(arn))

@jainuls
Copy link

jainuls commented May 18, 2023

anybody know how to do this cleanup with terraform ?

@nl-brett-stime
Copy link

@jainul01

anybody know how to do this cleanup with terraform ?

I don't think this can be done via Terraform (TF) without writing a custom provider in go-lang. Even then, it would be impractical since you'd have to first import each version into TF state before TF would destroy it.

If you're already managing the Lambdas via TF and applying the TF generates additional versions, you could consider setting the publish attribute false: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function#publish . That will have TF disable the versioning system that's built in to the Lambda service. You'd probably want to make sure you have versioning controlled through some other means before you do that, though. E.g., you could store multiple revisions in S3 and direct the TF to deploy one version or another. In that scenario, rolling back to a prior version would just mean selecting that older revision from S3 and having the TF push that as the one/active version that Lambda is aware of.

@imlanie
Copy link

imlanie commented Aug 12, 2023

More revisions. We don't use aliases, so this keeps the most recent two by version number.

import boto3

def clean_old_lambda_versions(client):
    functions = client.list_functions()['Functions']
    for function in functions:
        arn = function['FunctionArn']
        print(arn)
        all_versions = []

        versions = client.list_versions_by_function(
            FunctionName=arn)
        # Page through all the versions
        while True:
            page_versions = [int(v['Version']) for v in versions['Versions'] if not v['Version'] == '$LATEST']
            all_versions.extend(page_versions)
            try:
                marker = versions['NextMarker']
            except:
                break
            versions = client.list_versions_by_function(
                FunctionName=arn, Marker=marker)

        # Sort and keep the last 2
        all_versions.sort()
        print('Which versions must go?')
        print(all_versions[0:-2])
        print('Which versions will live')
        print(all_versions[-2::])
        for chopBlock in all_versions[0:-2]:
            functionArn = '{}:{}'.format(arn, chopBlock)
            print('When uncommented, will run: delete_function(FunctionName={})'.format(functionArn))
            # I want to leave this commented in Git for safety so we don't run it unscrupulously
            # client.delete_function(FunctionName=functionArn)  # uncomment me once you've checked

if __name__ == '__main__':
    client = boto3.client('lambda', region_name='us-east-1')
    clean_old_lambda_versions(client)

Nice. I tried all above. And this Code were what I need thank you. Even care about the pagination. Cleanup everything except two latest version numbers

More revisions. We don't use aliases, so this keeps the most recent two by version number.

import boto3

def clean_old_lambda_versions(client):
    functions = client.list_functions()['Functions']
    for function in functions:
        arn = function['FunctionArn']
        print(arn)
        all_versions = []

        versions = client.list_versions_by_function(
            FunctionName=arn)
        # Page through all the versions
        while True:
            page_versions = [int(v['Version']) for v in versions['Versions'] if not v['Version'] == '$LATEST']
            all_versions.extend(page_versions)
            try:
                marker = versions['NextMarker']
            except:
                break
            versions = client.list_versions_by_function(
                FunctionName=arn, Marker=marker)

        # Sort and keep the last 2
        all_versions.sort()
        print('Which versions must go?')
        print(all_versions[0:-2])
        print('Which versions will live')
        print(all_versions[-2::])
        for chopBlock in all_versions[0:-2]:
            functionArn = '{}:{}'.format(arn, chopBlock)
            print('When uncommented, will run: delete_function(FunctionName={})'.format(functionArn))
            # I want to leave this commented in Git for safety so we don't run it unscrupulously
            # client.delete_function(FunctionName=functionArn)  # uncomment me once you've checked

if __name__ == '__main__':
    client = boto3.client('lambda', region_name='us-east-1')
    clean_old_lambda_versions(client)

More revisions. We don't use aliases, so this keeps the most recent two by version number.

import boto3

def clean_old_lambda_versions(client):
    functions = client.list_functions()['Functions']
    for function in functions:
        arn = function['FunctionArn']
        print(arn)
        all_versions = []

        versions = client.list_versions_by_function(
            FunctionName=arn)
        # Page through all the versions
        while True:
            page_versions = [int(v['Version']) for v in versions['Versions'] if not v['Version'] == '$LATEST']
            all_versions.extend(page_versions)
            try:
                marker = versions['NextMarker']
            except:
                break
            versions = client.list_versions_by_function(
                FunctionName=arn, Marker=marker)

        # Sort and keep the last 2
        all_versions.sort()
        print('Which versions must go?')
        print(all_versions[0:-2])
        print('Which versions will live')
        print(all_versions[-2::])
        for chopBlock in all_versions[0:-2]:
            functionArn = '{}:{}'.format(arn, chopBlock)
            print('When uncommented, will run: delete_function(FunctionName={})'.format(functionArn))
            # I want to leave this commented in Git for safety so we don't run it unscrupulously
            # client.delete_function(FunctionName=functionArn)  # uncomment me once you've checked

if __name__ == '__main__':
    client = boto3.client('lambda', region_name='us-east-1')
    clean_old_lambda_versions(client)

Nice. I tried all above. And this Code were what I need thank you. Even care about the pagination. Cleanup everything except two latest version numbers

Thanks for this code. I added one extra check to filter only functions with a name like substring. I used it today to clean up one function that had 110 versions. Definitely saved a lot of time!

@JanAbb
Copy link

JanAbb commented Jan 17, 2024

I had the use case for this due to the deprecation of node versions < 18. I wrote a script to delete all old versions that were not the newest to make the AWS Trusted Advisor happy again. This is my code for people in need to delete for all lambdas all old versions but the newest:

import { LambdaClient, ListVersionsByFunctionCommand, DeleteFunctionCommand, FunctionConfiguration, ListFunctionsCommand } from '@aws-sdk/client-lambda';

const lambda = new LambdaClient({
    region: 'eu-central-1'
});

const fetchAllVersions = async (functionName: string) => {
    let marker: string | undefined;
    let versions: FunctionConfiguration[] = [];
    do {
        const listCommand = new ListVersionsByFunctionCommand({
            FunctionName: functionName,
            Marker: marker
        });
        const partOfVersions = await lambda.send(listCommand);
        versions = versions.concat(partOfVersions.Versions ?? []);
        marker = partOfVersions.NextMarker;
    } while(marker);
    

    return versions;
};

const deleteOldVersionsOfLambda = async (functionName: string) => {
    const versions = await fetchAllVersions(functionName);
    const deletionList: string[] = versions?.filter(v => v.Version !== '$LATEST').map(v => v.FunctionArn) as string[];


    for (const arn of deletionList) {
        const deletionCommand = new DeleteFunctionCommand({
            FunctionName: arn
        });

        await lambda.send(deletionCommand);
    }

    return null;
};

const getAllFunctionsAvailable = async () => {
    const listFunctionsCommand = new ListFunctionsCommand({});

    const functions = await lambda.send(listFunctionsCommand);

    return functions.Functions?.map(f => f.FunctionName).filter(f => f != null);
};

const deleteAllOldVersions = async () => {
    const allFunctionsNames = await getAllFunctionsAvailable();
    
    for (const functionName of allFunctionsNames!) {
        await deleteOldVersionsOfLambda(functionName!);
    }
};

deleteAllOldVersions();

@sw-iot-yashdholakia
Copy link

More revisions. We don't use aliases, so this keeps the most recent two by version number.

import boto3

def clean_old_lambda_versions(client):
    functions = client.list_functions()['Functions']
    for function in functions:
        arn = function['FunctionArn']
        print(arn)
        all_versions = []

        versions = client.list_versions_by_function(
            FunctionName=arn)
        # Page through all the versions
        while True:
            page_versions = [int(v['Version']) for v in versions['Versions'] if not v['Version'] == '$LATEST']
            all_versions.extend(page_versions)
            try:
                marker = versions['NextMarker']
            except:
                break
            versions = client.list_versions_by_function(
                FunctionName=arn, Marker=marker)

        # Sort and keep the last 2
        all_versions.sort()
        print('Which versions must go?')
        print(all_versions[0:-2])
        print('Which versions will live')
        print(all_versions[-2::])
        for chopBlock in all_versions[0:-2]:
            functionArn = '{}:{}'.format(arn, chopBlock)
            print('When uncommented, will run: delete_function(FunctionName={})'.format(functionArn))
            # I want to leave this commented in Git for safety so we don't run it unscrupulously
            # client.delete_function(FunctionName=functionArn)  # uncomment me once you've checked

if __name__ == '__main__':
    client = boto3.client('lambda', region_name='us-east-1')
    clean_old_lambda_versions(client)

This Helped for my 88 lambda cleanup, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment