Work with Amazon S3 object lock features using an AWS SDK - Amazon Simple Storage Service

Work with Amazon S3 object lock features using an AWS SDK

The following code examples show how to work with S3 object lock features.

.NET
AWS SDK for .NET
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

Run an interactive scenario demonstrating Amazon S3 object lock features.

using Amazon.S3; using Amazon.S3.Model; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; using Microsoft.Extensions.Logging.Debug; namespace S3ObjectLockScenario; public static class S3ObjectLockWorkflow { /* Before running this .NET code example, set up your development environment, including your credentials. This .NET example performs the following tasks: 1. Create test Amazon Simple Storage Service (S3) buckets with different lock policies. 2. Upload sample objects to each bucket. 3. Set some Legal Hold and Retention Periods on objects and buckets. 4. Investigate lock policies by viewing settings or attempting to delete or overwrite objects. 5. Clean up objects and buckets. */ public static S3ActionsWrapper _s3ActionsWrapper = null!; public static IConfiguration _configuration = null!; private static string _resourcePrefix = null!; private static string noLockBucketName = null!; private static string lockEnabledBucketName = null!; private static string retentionAfterCreationBucketName = null!; private static List<string> bucketNames = new List<string>(); private static List<string> fileNames = new List<string>(); public static async Task Main(string[] args) { // Set up dependency injection for the Amazon service. using var host = Host.CreateDefaultBuilder(args) .ConfigureLogging(logging => logging.AddFilter("System", LogLevel.Debug) .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information) .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace)) .ConfigureServices((_, services) => services.AddAWSService<IAmazonS3>() .AddTransient<S3ActionsWrapper>() ) .Build(); _configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("settings.json") // Load settings from .json file. .AddJsonFile("settings.local.json", true) // Optionally, load local settings. .Build(); ConfigurationSetup(); ServicesSetup(host); try { Console.WriteLine(new string('-', 80)); Console.WriteLine("Welcome to the Amazon Simple Storage Service (S3) Object Locking Workflow Scenario."); Console.WriteLine(new string('-', 80)); await Setup(true); await DemoActionChoices(); Console.WriteLine(new string('-', 80)); Console.WriteLine("Cleaning up resources."); Console.WriteLine(new string('-', 80)); await Cleanup(true); Console.WriteLine(new string('-', 80)); Console.WriteLine("Amazon S3 Object Locking Workflow is complete."); Console.WriteLine(new string('-', 80)); } catch (Exception ex) { Console.WriteLine(new string('-', 80)); Console.WriteLine($"There was a problem: {ex.Message}"); await Cleanup(true); Console.WriteLine(new string('-', 80)); } } /// <summary> /// Populate the services for use within the console application. /// </summary> /// <param name="host">The services host.</param> private static void ServicesSetup(IHost host) { _s3ActionsWrapper = host.Services.GetRequiredService<S3ActionsWrapper>(); } /// <summary> /// Any setup operations needed. /// </summary> public static void ConfigurationSetup() { _resourcePrefix = _configuration["resourcePrefix"] ?? "dotnet-example"; noLockBucketName = _resourcePrefix + "-no-lock"; lockEnabledBucketName = _resourcePrefix + "-lock-enabled"; retentionAfterCreationBucketName = _resourcePrefix + "-retention-after-creation"; bucketNames.Add(noLockBucketName); bucketNames.Add(lockEnabledBucketName); bucketNames.Add(retentionAfterCreationBucketName); } // <summary> /// Deploy necessary resources for the scenario. /// </summary> /// <param name="interactive">True to run as interactive.</param> /// <returns>True if successful.</returns> public static async Task<bool> Setup(bool interactive) { Console.WriteLine( "\nFor this workflow, we will use the AWS SDK for .NET to create several S3\n" + "buckets and files to demonstrate working with S3 locking features.\n"); Console.WriteLine(new string('-', 80)); Console.WriteLine("Press Enter when you are ready to start."); if (interactive) Console.ReadLine(); Console.WriteLine("\nS3 buckets can be created either with or without object lock enabled."); await _s3ActionsWrapper.CreateBucketWithObjectLock(noLockBucketName, false); await _s3ActionsWrapper.CreateBucketWithObjectLock(lockEnabledBucketName, true); await _s3ActionsWrapper.CreateBucketWithObjectLock(retentionAfterCreationBucketName, false); Console.WriteLine("Press Enter to continue."); if (interactive) Console.ReadLine(); Console.WriteLine("\nA bucket can be configured to use object locking with a default retention period."); await _s3ActionsWrapper.ModifyBucketDefaultRetention(retentionAfterCreationBucketName, true, ObjectLockRetentionMode.Governance, DateTime.UtcNow.AddDays(1)); Console.WriteLine("Press Enter to continue."); if (interactive) Console.ReadLine(); Console.WriteLine("\nObject lock policies can also be added to existing buckets."); await _s3ActionsWrapper.EnableObjectLockOnBucket(lockEnabledBucketName); Console.WriteLine("Press Enter to continue."); if (interactive) Console.ReadLine(); // Upload some files to the buckets. Console.WriteLine("\nNow let's add some test files:"); var fileName = _configuration["exampleFileName"] ?? "exampleFile.txt"; int fileCount = 2; // Create the file if it does not already exist. if (!File.Exists(fileName)) { await using StreamWriter sw = File.CreateText(fileName); await sw.WriteLineAsync( "This is a sample file for uploading to a bucket."); } foreach (var bucketName in bucketNames) { for (int i = 0; i < fileCount; i++) { var numberedFileName = Path.GetFileNameWithoutExtension(fileName) + i + Path.GetExtension(fileName); fileNames.Add(numberedFileName); await _s3ActionsWrapper.UploadFileAsync(bucketName, numberedFileName, fileName); } } Console.WriteLine("Press Enter to continue."); if (interactive) Console.ReadLine(); if (!interactive) return true; Console.WriteLine("\nNow we can set some object lock policies on individual files:"); foreach (var bucketName in bucketNames) { for (int i = 0; i < fileNames.Count; i++) { // No modifications to the objects in the first bucket. if (bucketName != bucketNames[0]) { var exampleFileName = fileNames[i]; switch (i) { case 0: { var question = $"\nWould you like to add a legal hold to {exampleFileName} in {bucketName}? (y/n)"; if (GetYesNoResponse(question)) { // Set a legal hold. await _s3ActionsWrapper.ModifyObjectLegalHold(bucketName, exampleFileName, ObjectLockLegalHoldStatus.On); } break; } case 1: { var question = $"\nWould you like to add a 1 day Governance retention period to {exampleFileName} in {bucketName}? (y/n)" + "\nReminder: Only a user with the s3:BypassGovernanceRetention permission will be able to delete this file or its bucket until the retention period has expired."; if (GetYesNoResponse(question)) { // Set a Governance mode retention period for 1 day. await _s3ActionsWrapper.ModifyObjectRetentionPeriod( bucketName, exampleFileName, ObjectLockRetentionMode.Governance, DateTime.UtcNow.AddDays(1)); } break; } } } } } Console.WriteLine(new string('-', 80)); return true; } // <summary> /// List all of the current buckets and objects. /// </summary> /// <param name="interactive">True to run as interactive.</param> /// <returns>The list of buckets and objects.</returns> public static async Task<List<S3ObjectVersion>> ListBucketsAndObjects(bool interactive) { var allObjects = new List<S3ObjectVersion>(); foreach (var bucketName in bucketNames) { var objectsInBucket = await _s3ActionsWrapper.ListBucketObjectsAndVersions(bucketName); foreach (var objectKey in objectsInBucket.Versions) { allObjects.Add(objectKey); } } if (interactive) { Console.WriteLine("\nCurrent buckets and objects:\n"); int i = 0; foreach (var bucketObject in allObjects) { i++; Console.WriteLine( $"{i}: {bucketObject.Key} \n\tBucket: {bucketObject.BucketName}\n\tVersion: {bucketObject.VersionId}"); } } return allObjects; } /// <summary> /// Present the user with the demo action choices. /// </summary> /// <returns>Async task.</returns> public static async Task<bool> DemoActionChoices() { var choices = new string[]{ "List all files in buckets.", "Attempt to delete a file.", "Attempt to delete a file with retention period bypass.", "Attempt to overwrite a file.", "View the object and bucket retention settings for a file.", "View the legal hold settings for a file.", "Finish the workflow."}; var choice = 0; // Keep asking the user until they choose to move on. while (choice != 6) { Console.WriteLine(new string('-', 80)); choice = GetChoiceResponse( "\nExplore the S3 locking features by selecting one of the following choices:" , choices); Console.WriteLine(new string('-', 80)); switch (choice) { case 0: { await ListBucketsAndObjects(true); break; } case 1: { Console.WriteLine("\nEnter the number of the object to delete:"); var allFiles = await ListBucketsAndObjects(true); var fileChoice = GetChoiceResponse(null, allFiles.Select(f => f.Key).ToArray()); await _s3ActionsWrapper.DeleteObjectFromBucket(allFiles[fileChoice].BucketName, allFiles[fileChoice].Key, false, allFiles[fileChoice].VersionId); break; } case 2: { Console.WriteLine("\nEnter the number of the object to delete:"); var allFiles = await ListBucketsAndObjects(true); var fileChoice = GetChoiceResponse(null, allFiles.Select(f => f.Key).ToArray()); await _s3ActionsWrapper.DeleteObjectFromBucket(allFiles[fileChoice].BucketName, allFiles[fileChoice].Key, true, allFiles[fileChoice].VersionId); break; } case 3: { var allFiles = await ListBucketsAndObjects(true); Console.WriteLine("\nEnter the number of the object to overwrite:"); var fileChoice = GetChoiceResponse(null, allFiles.Select(f => f.Key).ToArray()); // Create the file if it does not already exist. if (!File.Exists(allFiles[fileChoice].Key)) { await using StreamWriter sw = File.CreateText(allFiles[fileChoice].Key); await sw.WriteLineAsync( "This is a sample file for uploading to a bucket."); } await _s3ActionsWrapper.UploadFileAsync(allFiles[fileChoice].BucketName, allFiles[fileChoice].Key, allFiles[fileChoice].Key); break; } case 4: { var allFiles = await ListBucketsAndObjects(true); Console.WriteLine("\nEnter the number of the object and bucket to view:"); var fileChoice = GetChoiceResponse(null, allFiles.Select(f => f.Key).ToArray()); await _s3ActionsWrapper.GetObjectRetention(allFiles[fileChoice].BucketName, allFiles[fileChoice].Key); await _s3ActionsWrapper.GetBucketObjectLockConfiguration(allFiles[fileChoice].BucketName); break; } case 5: { var allFiles = await ListBucketsAndObjects(true); Console.WriteLine("\nEnter the number of the object to view:"); var fileChoice = GetChoiceResponse(null, allFiles.Select(f => f.Key).ToArray()); await _s3ActionsWrapper.GetObjectLegalHold(allFiles[fileChoice].BucketName, allFiles[fileChoice].Key); break; } } } return true; } // <summary> /// Clean up the resources from the scenario. /// </summary> /// <param name="interactive">True to run as interactive.</param> /// <returns>True if successful.</returns> public static async Task<bool> Cleanup(bool interactive) { Console.WriteLine(new string('-', 80)); if (!interactive || GetYesNoResponse("Do you want to clean up all files and buckets? (y/n) ")) { // Remove all locks and delete all buckets and objects. var allFiles = await ListBucketsAndObjects(false); foreach (var fileInfo in allFiles) { // Check for a legal hold. var legalHold = await _s3ActionsWrapper.GetObjectLegalHold(fileInfo.BucketName, fileInfo.Key); if (legalHold?.Status?.Value == ObjectLockLegalHoldStatus.On) { await _s3ActionsWrapper.ModifyObjectLegalHold(fileInfo.BucketName, fileInfo.Key, ObjectLockLegalHoldStatus.Off); } // Check for a retention period. var retention = await _s3ActionsWrapper.GetObjectRetention(fileInfo.BucketName, fileInfo.Key); var hasRetentionPeriod = retention?.Mode == ObjectLockRetentionMode.Governance && retention.RetainUntilDate > DateTime.UtcNow.Date; await _s3ActionsWrapper.DeleteObjectFromBucket(fileInfo.BucketName, fileInfo.Key, hasRetentionPeriod, fileInfo.VersionId); } foreach (var bucketName in bucketNames) { await _s3ActionsWrapper.DeleteBucketByName(bucketName); } } else { Console.WriteLine( "Ok, we'll leave the resources intact.\n" + "Don't forget to delete them when you're done with them or you might incur unexpected charges." ); } Console.WriteLine(new string('-', 80)); return true; } /// <summary> /// Helper method to get a yes or no response from the user. /// </summary> /// <param name="question">The question string to print on the console.</param> /// <returns>True if the user responds with a yes.</returns> private static bool GetYesNoResponse(string question) { Console.WriteLine(question); var ynResponse = Console.ReadLine(); var response = ynResponse != null && ynResponse.Equals("y", StringComparison.InvariantCultureIgnoreCase); return response; } /// <summary> /// Helper method to get a choice response from the user. /// </summary> /// <param name="question">The question string to print on the console.</param> /// <param name="choices">The choices to print on the console.</param> /// <returns>The index of the selected choice</returns> private static int GetChoiceResponse(string? question, string[] choices) { if (question != null) { Console.WriteLine(question); for (int i = 0; i < choices.Length; i++) { Console.WriteLine($"\t{i + 1}. {choices[i]}"); } } var choiceNumber = 0; while (choiceNumber < 1 || choiceNumber > choices.Length) { var choice = Console.ReadLine(); Int32.TryParse(choice, out choiceNumber); } return choiceNumber - 1; } }

A wrapper class for S3 functions.

using System.Net; using Amazon.S3; using Amazon.S3.Model; using Microsoft.Extensions.Configuration; namespace S3ObjectLockScenario; /// <summary> /// Encapsulate the Amazon S3 operations. /// </summary> public class S3ActionsWrapper { private readonly IAmazonS3 _amazonS3; /// <summary> /// Constructor for the S3ActionsWrapper. /// </summary> /// <param name="amazonS3">The injected S3 client.</param> public S3ActionsWrapper(IAmazonS3 amazonS3, IConfiguration configuration) { _amazonS3 = amazonS3; } /// <summary> /// Create a new Amazon S3 bucket with object lock actions. /// </summary> /// <param name="bucketName">The name of the bucket to create.</param> /// <param name="enableObjectLock">True to enable object lock on the bucket.</param> /// <returns>True if successful.</returns> public async Task<bool> CreateBucketWithObjectLock(string bucketName, bool enableObjectLock) { Console.WriteLine($"\tCreating bucket {bucketName} with object lock {enableObjectLock}."); try { var request = new PutBucketRequest { BucketName = bucketName, UseClientRegion = true, ObjectLockEnabledForBucket = enableObjectLock, }; var response = await _amazonS3.PutBucketAsync(request); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } catch (AmazonS3Exception ex) { Console.WriteLine($"Error creating bucket: '{ex.Message}'"); return false; } } /// <summary> /// Enable object lock on an existing bucket. /// </summary> /// <param name="bucketName">The name of the bucket to modify.</param> /// <returns>True if successful.</returns> public async Task<bool> EnableObjectLockOnBucket(string bucketName) { try { // First, enable Versioning on the bucket. await _amazonS3.PutBucketVersioningAsync(new PutBucketVersioningRequest() { BucketName = bucketName, VersioningConfig = new S3BucketVersioningConfig() { EnableMfaDelete = false, Status = VersionStatus.Enabled } }); var request = new PutObjectLockConfigurationRequest() { BucketName = bucketName, ObjectLockConfiguration = new ObjectLockConfiguration() { ObjectLockEnabled = new ObjectLockEnabled("Enabled"), }, }; var response = await _amazonS3.PutObjectLockConfigurationAsync(request); Console.WriteLine($"\tAdded an object lock policy to bucket {bucketName}."); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } catch (AmazonS3Exception ex) { Console.WriteLine($"Error modifying object lock: '{ex.Message}'"); return false; } } /// <summary> /// Set or modify a retention period on an object in an S3 bucket. /// </summary> /// <param name="bucketName">The bucket of the object.</param> /// <param name="objectKey">The key of the object.</param> /// <param name="retention">The retention mode.</param> /// <param name="retainUntilDate">The date retention expires.</param> /// <returns>True if successful.</returns> public async Task<bool> ModifyObjectRetentionPeriod(string bucketName, string objectKey, ObjectLockRetentionMode retention, DateTime retainUntilDate) { try { var request = new PutObjectRetentionRequest() { BucketName = bucketName, Key = objectKey, Retention = new ObjectLockRetention() { Mode = retention, RetainUntilDate = retainUntilDate } }; var response = await _amazonS3.PutObjectRetentionAsync(request); Console.WriteLine($"\tSet retention for {objectKey} in {bucketName} until {retainUntilDate:d}."); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tError modifying retention period: '{ex.Message}'"); return false; } } /// <summary> /// Set or modify a retention period on an S3 bucket. /// </summary> /// <param name="bucketName">The bucket to modify.</param> /// <param name="retention">The retention mode.</param> /// <param name="retainUntilDate">The date for retention until.</param> /// <returns>True if successful.</returns> public async Task<bool> ModifyBucketDefaultRetention(string bucketName, bool enableObjectLock, ObjectLockRetentionMode retention, DateTime retainUntilDate) { var enabledString = enableObjectLock ? "Enabled" : "Disabled"; var timeDifference = retainUntilDate.Subtract(DateTime.Now); try { // First, enable Versioning on the bucket. await _amazonS3.PutBucketVersioningAsync(new PutBucketVersioningRequest() { BucketName = bucketName, VersioningConfig = new S3BucketVersioningConfig() { EnableMfaDelete = false, Status = VersionStatus.Enabled } }); var request = new PutObjectLockConfigurationRequest() { BucketName = bucketName, ObjectLockConfiguration = new ObjectLockConfiguration() { ObjectLockEnabled = new ObjectLockEnabled(enabledString), Rule = new ObjectLockRule() { DefaultRetention = new DefaultRetention() { Mode = retention, Days = timeDifference.Days // Can be specified in days or years but not both. } } } }; var response = await _amazonS3.PutObjectLockConfigurationAsync(request); Console.WriteLine($"\tAdded a default retention to bucket {bucketName}."); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tError modifying object lock: '{ex.Message}'"); return false; } } /// <summary> /// Get the retention period for an S3 object. /// </summary> /// <param name="bucketName">The bucket of the object.</param> /// <param name="objectKey">The object key.</param> /// <returns>The object retention details.</returns> public async Task<ObjectLockRetention> GetObjectRetention(string bucketName, string objectKey) { try { var request = new GetObjectRetentionRequest() { BucketName = bucketName, Key = objectKey }; var response = await _amazonS3.GetObjectRetentionAsync(request); Console.WriteLine($"\tObject retention for {objectKey} in {bucketName}: " + $"\n\t{response.Retention.Mode} until {response.Retention.RetainUntilDate:d}."); return response.Retention; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tUnable to fetch object lock retention: '{ex.Message}'"); return new ObjectLockRetention(); } } /// <summary> /// Set or modify a legal hold on an object in an S3 bucket. /// </summary> /// <param name="bucketName">The bucket of the object.</param> /// <param name="objectKey">The key of the object.</param> /// <param name="holdStatus">The On or Off status for the legal hold.</param> /// <returns>True if successful.</returns> public async Task<bool> ModifyObjectLegalHold(string bucketName, string objectKey, ObjectLockLegalHoldStatus holdStatus) { try { var request = new PutObjectLegalHoldRequest() { BucketName = bucketName, Key = objectKey, LegalHold = new ObjectLockLegalHold() { Status = holdStatus } }; var response = await _amazonS3.PutObjectLegalHoldAsync(request); Console.WriteLine($"\tModified legal hold for {objectKey} in {bucketName}."); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tError modifying legal hold: '{ex.Message}'"); return false; } } /// <summary> /// Get the legal hold details for an S3 object. /// </summary> /// <param name="bucketName">The bucket of the object.</param> /// <param name="objectKey">The object key.</param> /// <returns>The object legal hold details.</returns> public async Task<ObjectLockLegalHold> GetObjectLegalHold(string bucketName, string objectKey) { try { var request = new GetObjectLegalHoldRequest() { BucketName = bucketName, Key = objectKey }; var response = await _amazonS3.GetObjectLegalHoldAsync(request); Console.WriteLine($"\tObject legal hold for {objectKey} in {bucketName}: " + $"\n\tStatus: {response.LegalHold.Status}"); return response.LegalHold; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tUnable to fetch legal hold: '{ex.Message}'"); return new ObjectLockLegalHold(); } } /// <summary> /// Get the object lock configuration details for an S3 bucket. /// </summary> /// <param name="bucketName">The bucket to get details.</param> /// <returns>The bucket's object lock configuration details.</returns> public async Task<ObjectLockConfiguration> GetBucketObjectLockConfiguration(string bucketName) { try { var request = new GetObjectLockConfigurationRequest() { BucketName = bucketName }; var response = await _amazonS3.GetObjectLockConfigurationAsync(request); Console.WriteLine($"\tBucket object lock config for {bucketName} in {bucketName}: " + $"\n\tEnabled: {response.ObjectLockConfiguration.ObjectLockEnabled}" + $"\n\tRule: {response.ObjectLockConfiguration.Rule?.DefaultRetention}"); return response.ObjectLockConfiguration; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tUnable to fetch object lock config: '{ex.Message}'"); return new ObjectLockConfiguration(); } } /// <summary> /// Upload a file from the local computer to an Amazon S3 bucket. /// </summary> /// <param name="bucketName">The Amazon S3 bucket to use.</param> /// <param name="objectName">The object to upload.</param> /// <param name="filePath">The path, including file name, of the object to upload.</param> /// <returns>True if success.<returns> public async Task<bool> UploadFileAsync(string bucketName, string objectName, string filePath) { var request = new PutObjectRequest { BucketName = bucketName, Key = objectName, FilePath = filePath, ChecksumAlgorithm = ChecksumAlgorithm.SHA256 }; var response = await _amazonS3.PutObjectAsync(request); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"\tSuccessfully uploaded {objectName} to {bucketName}."); return true; } else { Console.WriteLine($"\tCould not upload {objectName} to {bucketName}."); return false; } } /// <summary> /// List bucket objects and versions. /// </summary> /// <param name="bucketName">The Amazon S3 bucket to use.</param> /// <returns>The list of objects and versions.</returns> public async Task<ListVersionsResponse> ListBucketObjectsAndVersions(string bucketName) { var request = new ListVersionsRequest() { BucketName = bucketName }; var response = await _amazonS3.ListVersionsAsync(request); return response; } /// <summary> /// Delete an object from a specific bucket. /// </summary> /// <param name="bucketName">The Amazon S3 bucket to use.</param> /// <param name="objectKey">The key of the object to delete.</param> /// <param name="hasRetention">True if the object has retention settings.</param> /// <param name="versionId">Optional versionId.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteObjectFromBucket(string bucketName, string objectKey, bool hasRetention, string? versionId = null) { try { var request = new DeleteObjectRequest() { BucketName = bucketName, Key = objectKey, VersionId = versionId, }; if (hasRetention) { // Set the BypassGovernanceRetention header // if the file has retention settings. request.BypassGovernanceRetention = true; } await _amazonS3.DeleteObjectAsync(request); Console.WriteLine( $"Deleted {objectKey} in {bucketName}."); return true; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tUnable to delete object {objectKey} in bucket {bucketName}: " + ex.Message); return false; } } /// <summary> /// Delete a specific bucket. /// </summary> /// <param name="bucketName">The Amazon S3 bucket to use.</param> /// <param name="objectKey">The key of the object to delete.</param> /// <param name="versionId">Optional versionId.</param> /// <returns>True if successful.</returns> public async Task<bool> DeleteBucketByName(string bucketName) { try { var request = new DeleteBucketRequest() { BucketName = bucketName, }; var response = await _amazonS3.DeleteBucketAsync(request); Console.WriteLine($"\tDelete for {bucketName} complete."); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonS3Exception ex) { Console.WriteLine($"\tUnable to delete bucket {bucketName}: " + ex.Message); return false; } } }
Java
SDK for Java 2.x
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

Run an interactive scenario demonstrating Amazon S3 object lock features.

import software.amazon.awssdk.services.s3.model.ObjectLockLegalHold; import software.amazon.awssdk.services.s3.model.ObjectLockRetention; import java.io.BufferedWriter; import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.stream.Collectors; /* Before running this Java V2 code example, set up your development environment, including your credentials. For more information, see the following documentation topic: https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html This Java example performs the following tasks: 1. Create test Amazon Simple Storage Service (S3) buckets with different lock policies. 2. Upload sample objects to each bucket. 3. Set some Legal Hold and Retention Periods on objects and buckets. 4. Investigate lock policies by viewing settings or attempting to delete or overwrite objects. 5. Clean up objects and buckets. */ public class S3ObjectLockWorkflow { public static final String DASHES = new String(new char[80]).replace("\0", "-"); static String bucketName; static S3LockActions s3LockActions; private static final List<String> bucketNames = new ArrayList<>(); private static final List<String> fileNames = new ArrayList<>(); public static void main(String[] args) { // Get the current date and time to ensure bucket name is unique. LocalDateTime currentTime = LocalDateTime.now(); // Format the date and time as a string. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); String timeStamp = currentTime.format(formatter); s3LockActions = new S3LockActions(); bucketName = "bucket"+timeStamp; Scanner scanner = new Scanner(System.in); System.out.println(DASHES); System.out.println("Welcome to the Amazon Simple Storage Service (S3) Object Locking Workflow Scenario."); System.out.println("Press Enter to continue..."); scanner.nextLine(); configurationSetup(); System.out.println(DASHES); System.out.println(DASHES); setup(); System.out.println("Setup is complete. Press Enter to continue..."); scanner.nextLine(); System.out.println(DASHES); System.out.println(DASHES); System.out.println("Lets present the user with choices."); System.out.println("Press Enter to continue..."); scanner.nextLine(); demoActionChoices() ; System.out.println(DASHES); System.out.println(DASHES); System.out.println("Would you like to clean up the resources? (y/n)"); String delAns = scanner.nextLine().trim(); if (delAns.equalsIgnoreCase("y")) { cleanup(); System.out.println("Clean up is complete."); } System.out.println("Press Enter to continue..."); scanner.nextLine(); System.out.println(DASHES); System.out.println(DASHES); System.out.println("Amazon S3 Object Locking Workflow is complete."); System.out.println(DASHES); } // Present the user with the demo action choices. public static void demoActionChoices() { String[] choices = { "List all files in buckets.", "Attempt to delete a file.", "Attempt to delete a file with retention period bypass.", "Attempt to overwrite a file.", "View the object and bucket retention settings for a file.", "View the legal hold settings for a file.", "Finish the workflow." }; int choice = 0; while (true) { System.out.println(DASHES); choice = getChoiceResponse("Explore the S3 locking features by selecting one of the following choices:", choices); System.out.println(DASHES); System.out.println("You selected "+choices[choice]); switch (choice) { case 0 -> { s3LockActions.listBucketsAndObjects(bucketNames, true); } case 1 -> { System.out.println("Enter the number of the object to delete:"); List<S3InfoObject> allFiles = s3LockActions.listBucketsAndObjects(bucketNames, true); List<String> fileKeys = allFiles.stream().map(f -> f.getKeyName()).collect(Collectors.toList()); String[] fileKeysArray = fileKeys.toArray(new String[0]); int fileChoice = getChoiceResponse(null, fileKeysArray); String objectKey = fileKeys.get(fileChoice); String bucketName = allFiles.get(fileChoice).getBucketName(); String version = allFiles.get(fileChoice).getVersion(); s3LockActions.deleteObjectFromBucket(bucketName, objectKey, false, version); } case 2 -> { System.out.println("Enter the number of the object to delete:"); List<S3InfoObject> allFiles = s3LockActions.listBucketsAndObjects(bucketNames, true); List<String> fileKeys = allFiles.stream().map(f -> f.getKeyName()).collect(Collectors.toList()); String[] fileKeysArray = fileKeys.toArray(new String[0]); int fileChoice = getChoiceResponse(null, fileKeysArray); String objectKey = fileKeys.get(fileChoice); String bucketName = allFiles.get(fileChoice).getBucketName(); String version = allFiles.get(fileChoice).getVersion(); s3LockActions.deleteObjectFromBucket(bucketName, objectKey, true, version); } case 3 -> { System.out.println("Enter the number of the object to overwrite:"); List<S3InfoObject> allFiles = s3LockActions.listBucketsAndObjects(bucketNames, true); List<String> fileKeys = allFiles.stream().map(f -> f.getKeyName()).collect(Collectors.toList()); String[] fileKeysArray = fileKeys.toArray(new String[0]); int fileChoice = getChoiceResponse(null, fileKeysArray); String objectKey = fileKeys.get(fileChoice); String bucketName = allFiles.get(fileChoice).getBucketName(); // Attempt to overwrite the file. try (BufferedWriter writer = new BufferedWriter(new java.io.FileWriter(objectKey))) { writer.write("This is a modified text."); } catch (IOException e) { e.printStackTrace(); } s3LockActions.uploadFile(bucketName, objectKey, objectKey); } case 4 -> { System.out.println("Enter the number of the object to overwrite:"); List<S3InfoObject> allFiles = s3LockActions.listBucketsAndObjects(bucketNames, true); List<String> fileKeys = allFiles.stream().map(f -> f.getKeyName()).collect(Collectors.toList()); String[] fileKeysArray = fileKeys.toArray(new String[0]); int fileChoice = getChoiceResponse(null, fileKeysArray); String objectKey = fileKeys.get(fileChoice); String bucketName = allFiles.get(fileChoice).getBucketName(); s3LockActions.getObjectRetention(bucketName, objectKey); } case 5 -> { System.out.println("Enter the number of the object to view:"); List<S3InfoObject> allFiles = s3LockActions.listBucketsAndObjects(bucketNames, true); List<String> fileKeys = allFiles.stream().map(f -> f.getKeyName()).collect(Collectors.toList()); String[] fileKeysArray = fileKeys.toArray(new String[0]); int fileChoice = getChoiceResponse(null, fileKeysArray); String objectKey = fileKeys.get(fileChoice); String bucketName = allFiles.get(fileChoice).getBucketName(); s3LockActions.getObjectLegalHold(bucketName, objectKey); s3LockActions.getBucketObjectLockConfiguration(bucketName); } case 6 -> { System.out.println("Exiting the workflow..."); return; } default -> { System.out.println("Invalid choice. Please select again."); } } } } // Clean up the resources from the scenario. private static void cleanup() { List<S3InfoObject> allFiles = s3LockActions.listBucketsAndObjects(bucketNames, false); for (S3InfoObject fileInfo : allFiles) { String bucketName = fileInfo.getBucketName(); String key = fileInfo.getKeyName(); String version = fileInfo.getVersion(); if (bucketName.contains("lock-enabled") || (bucketName.contains("retention-after-creation"))) { ObjectLockLegalHold legalHold = s3LockActions.getObjectLegalHold(bucketName, key); if (legalHold != null) { String holdStatus = legalHold.status().name(); System.out.println(holdStatus); if (holdStatus.compareTo("ON") == 0) { s3LockActions.modifyObjectLegalHold(bucketName, key, false); } } // Check for a retention period. ObjectLockRetention retention = s3LockActions.getObjectRetention(bucketName, key); boolean hasRetentionPeriod ; hasRetentionPeriod = retention != null; s3LockActions.deleteObjectFromBucket(bucketName, key,hasRetentionPeriod, version); } else { System.out.println(bucketName +" objects do not have a legal lock"); s3LockActions.deleteObjectFromBucket(bucketName, key,false, version); } } // Delete the buckets. System.out.println("Delete "+bucketName); for (String bucket : bucketNames){ s3LockActions.deleteBucketByName(bucket); } } private static void setup() { Scanner scanner = new Scanner(System.in); System.out.println(""" For this workflow, we will use the AWS SDK for Java to create several S3 buckets and files to demonstrate working with S3 locking features. """); System.out.println("S3 buckets can be created either with or without object lock enabled."); System.out.println("Press Enter to continue..."); scanner.nextLine(); // Create three S3 buckets. s3LockActions.createBucketWithLockOptions(false, bucketNames.get(0)); s3LockActions.createBucketWithLockOptions(true, bucketNames.get(1)); s3LockActions.createBucketWithLockOptions(false, bucketNames.get(2)); System.out.println("Press Enter to continue."); scanner.nextLine(); System.out.println("Bucket "+bucketNames.get(2) +" will be configured to use object locking with a default retention period."); s3LockActions.modifyBucketDefaultRetention(bucketNames.get(2)); System.out.println("Press Enter to continue."); scanner.nextLine(); System.out.println("Object lock policies can also be added to existing buckets. For this example, we will use "+bucketNames.get(1)); s3LockActions.enableObjectLockOnBucket(bucketNames.get(1)); System.out.println("Press Enter to continue."); scanner.nextLine(); // Upload some files to the buckets. System.out.println("Now let's add some test files:"); String fileName = "exampleFile.txt"; int fileCount = 2; try (BufferedWriter writer = new BufferedWriter(new java.io.FileWriter(fileName))) { writer.write("This is a sample file for uploading to a bucket."); } catch (IOException e) { e.printStackTrace(); } for (String bucketName : bucketNames){ for (int i = 0; i < fileCount; i++) { // Get the file name without extension. String fileNameWithoutExtension = java.nio.file.Paths.get(fileName).getFileName().toString(); int extensionIndex = fileNameWithoutExtension.lastIndexOf('.'); if (extensionIndex > 0) { fileNameWithoutExtension = fileNameWithoutExtension.substring(0, extensionIndex); } // Create the numbered file names. String numberedFileName = fileNameWithoutExtension + i + getFileExtension(fileName); fileNames.add(numberedFileName); s3LockActions.uploadFile(bucketName, numberedFileName, fileName); } } String question = null; System.out.print("Press Enter to continue..."); scanner.nextLine(); System.out.println("Now we can set some object lock policies on individual files:"); for (String bucketName : bucketNames) { for (int i = 0; i < fileNames.size(); i++){ // No modifications to the objects in the first bucket. if (!bucketName.equals(bucketNames.get(0))) { String exampleFileName = fileNames.get(i); switch (i) { case 0 -> { question = "Would you like to add a legal hold to " + exampleFileName + " in " + bucketName + " (y/n)?"; System.out.println(question); String ans = scanner.nextLine().trim(); if (ans.equalsIgnoreCase("y")) { System.out.println("**** You have selected to put a legal hold " + exampleFileName); // Set a legal hold. s3LockActions.modifyObjectLegalHold(bucketName, exampleFileName, true); } } case 1 -> { """ Would you like to add a 1 day Governance retention period to %s in %s (y/n)? Reminder: Only a user with the s3:BypassGovernanceRetention permission will be able to delete this file or its bucket until the retention period has expired. """.formatted(exampleFileName, bucketName); System.out.println(question); String ans2 = scanner.nextLine().trim(); if (ans2.equalsIgnoreCase("y")) { s3LockActions.modifyObjectRetentionPeriod(bucketName, exampleFileName); } } } } } } } // Get file extension. private static String getFileExtension(String fileName) { int dotIndex = fileName.lastIndexOf('.'); if (dotIndex > 0) { return fileName.substring(dotIndex); } return ""; } public static void configurationSetup() { String noLockBucketName = bucketName + "-no-lock"; String lockEnabledBucketName = bucketName + "-lock-enabled"; String retentionAfterCreationBucketName = bucketName + "-retention-after-creation"; bucketNames.add(noLockBucketName); bucketNames.add(lockEnabledBucketName); bucketNames.add(retentionAfterCreationBucketName); } public static int getChoiceResponse(String question, String[] choices) { Scanner scanner = new Scanner(System.in); if (question != null) { System.out.println(question); for (int i = 0; i < choices.length; i++) { System.out.println("\t" + (i + 1) + ". " + choices[i]); } } int choiceNumber = 0; while (choiceNumber < 1 || choiceNumber > choices.length) { String choice = scanner.nextLine(); try { choiceNumber = Integer.parseInt(choice); } catch (NumberFormatException e) { System.out.println("Invalid choice. Please enter a valid number."); } } return choiceNumber - 1; } }

A wrapper class for S3 functions.

import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.BucketVersioningStatus; import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; import software.amazon.awssdk.services.s3.model.CreateBucketRequest; import software.amazon.awssdk.services.s3.model.DefaultRetention; import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectLegalHoldRequest; import software.amazon.awssdk.services.s3.model.GetObjectLegalHoldResponse; import software.amazon.awssdk.services.s3.model.GetObjectLockConfigurationRequest; import software.amazon.awssdk.services.s3.model.GetObjectLockConfigurationResponse; import software.amazon.awssdk.services.s3.model.GetObjectRetentionRequest; import software.amazon.awssdk.services.s3.model.GetObjectRetentionResponse; import software.amazon.awssdk.services.s3.model.HeadBucketRequest; import software.amazon.awssdk.services.s3.model.ListObjectVersionsRequest; import software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse; import software.amazon.awssdk.services.s3.model.MFADelete; import software.amazon.awssdk.services.s3.model.ObjectLockConfiguration; import software.amazon.awssdk.services.s3.model.ObjectLockEnabled; import software.amazon.awssdk.services.s3.model.ObjectLockLegalHold; import software.amazon.awssdk.services.s3.model.ObjectLockLegalHoldStatus; import software.amazon.awssdk.services.s3.model.ObjectLockRetention; import software.amazon.awssdk.services.s3.model.ObjectLockRetentionMode; import software.amazon.awssdk.services.s3.model.ObjectLockRule; import software.amazon.awssdk.services.s3.model.PutBucketVersioningRequest; import software.amazon.awssdk.services.s3.model.PutObjectLegalHoldRequest; import software.amazon.awssdk.services.s3.model.PutObjectLockConfigurationRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectResponse; import software.amazon.awssdk.services.s3.model.PutObjectRetentionRequest; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.services.s3.model.VersioningConfiguration; import software.amazon.awssdk.services.s3.waiters.S3Waiter; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; // Contains application logic for the Amazon S3 operations used in this workflow. public class S3LockActions { private static S3Client getClient() { return S3Client.builder() .region(Region.US_EAST_1) .build(); } // Set or modify a retention period on an object in an S3 bucket. public void modifyObjectRetentionPeriod(String bucketName, String objectKey) { // Calculate the instant one day from now. Instant futureInstant = Instant.now().plus(1, ChronoUnit.DAYS); // Convert the Instant to a ZonedDateTime object with a specific time zone. ZonedDateTime zonedDateTime = futureInstant.atZone(ZoneId.systemDefault()); // Define a formatter for human-readable output. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // Format the ZonedDateTime object to a human-readable date string. String humanReadableDate = formatter.format(zonedDateTime); // Print the formatted date string. System.out.println("Formatted Date: " + humanReadableDate); ObjectLockRetention retention = ObjectLockRetention.builder() .mode(ObjectLockRetentionMode.GOVERNANCE) .retainUntilDate(futureInstant) .build(); PutObjectRetentionRequest retentionRequest = PutObjectRetentionRequest.builder() .bucket(bucketName) .key(objectKey) .retention(retention) .build(); getClient().putObjectRetention(retentionRequest); System.out.println("Set retention for "+objectKey +" in " +bucketName +" until "+ humanReadableDate +"."); } // Get the legal hold details for an S3 object. public ObjectLockLegalHold getObjectLegalHold(String bucketName, String objectKey) { try { GetObjectLegalHoldRequest legalHoldRequest = GetObjectLegalHoldRequest.builder() .bucket(bucketName) .key(objectKey) .build(); GetObjectLegalHoldResponse response = getClient().getObjectLegalHold(legalHoldRequest); System.out.println("Object legal hold for " + objectKey + " in " + bucketName + ":\n\tStatus: " + response.legalHold().status()); return response.legalHold(); } catch (S3Exception ex) { System.out.println("\tUnable to fetch legal hold: '" + ex.getMessage() + "'"); } return null; } // Create a new Amazon S3 bucket with object lock options. public void createBucketWithLockOptions(boolean enableObjectLock, String bucketName) { S3Waiter s3Waiter = getClient().waiter(); CreateBucketRequest bucketRequest = CreateBucketRequest.builder() .bucket(bucketName) .objectLockEnabledForBucket(enableObjectLock) .build(); getClient().createBucket(bucketRequest); HeadBucketRequest bucketRequestWait = HeadBucketRequest.builder() .bucket(bucketName) .build(); // Wait until the bucket is created and print out the response. s3Waiter.waitUntilBucketExists(bucketRequestWait); System.out.println(bucketName + " is ready"); } public List<S3InfoObject> listBucketsAndObjects(List<String> bucketNames, Boolean interactive) { AtomicInteger counter = new AtomicInteger(0); // Initialize counter. return bucketNames.stream() .flatMap(bucketName -> listBucketObjectsAndVersions(bucketName).versions().stream() .map(version -> { S3InfoObject s3InfoObject = new S3InfoObject(); s3InfoObject.setBucketName(bucketName); s3InfoObject.setVersion(version.versionId()); s3InfoObject.setKeyName(version.key()); return s3InfoObject; })) .peek(s3InfoObject -> { int i = counter.incrementAndGet(); // Increment and get the updated value. if (interactive) { System.out.println(i + ": "+ s3InfoObject.getKeyName()); System.out.printf("%5s Bucket name: %s\n", "", s3InfoObject.getBucketName()); System.out.printf("%5s Version: %s\n", "", s3InfoObject.getVersion()); } }) .collect(Collectors.toList()); } public ListObjectVersionsResponse listBucketObjectsAndVersions(String bucketName) { ListObjectVersionsRequest versionsRequest = ListObjectVersionsRequest.builder() .bucket(bucketName) .build(); return getClient().listObjectVersions(versionsRequest); } // Set or modify a retention period on an S3 bucket. public void modifyBucketDefaultRetention(String bucketName) { VersioningConfiguration versioningConfiguration = VersioningConfiguration.builder() .mfaDelete(MFADelete.DISABLED) .status(BucketVersioningStatus.ENABLED) .build(); PutBucketVersioningRequest versioningRequest = PutBucketVersioningRequest.builder() .bucket(bucketName) .versioningConfiguration(versioningConfiguration) .build(); getClient().putBucketVersioning(versioningRequest); DefaultRetention rention = DefaultRetention.builder() .days(1) .mode(ObjectLockRetentionMode.GOVERNANCE) .build(); ObjectLockRule lockRule = ObjectLockRule.builder() .defaultRetention(rention) .build(); ObjectLockConfiguration objectLockConfiguration = ObjectLockConfiguration.builder() .objectLockEnabled(ObjectLockEnabled.ENABLED) .rule(lockRule) .build(); PutObjectLockConfigurationRequest putObjectLockConfigurationRequest = PutObjectLockConfigurationRequest.builder() .bucket(bucketName) .objectLockConfiguration(objectLockConfiguration) .build(); getClient().putObjectLockConfiguration(putObjectLockConfigurationRequest) ; System.out.println("Added a default retention to bucket "+bucketName +"."); } // Enable object lock on an existing bucket. public void enableObjectLockOnBucket(String bucketName) { try { VersioningConfiguration versioningConfiguration = VersioningConfiguration.builder() .status(BucketVersioningStatus.ENABLED) .build(); PutBucketVersioningRequest putBucketVersioningRequest = PutBucketVersioningRequest.builder() .bucket(bucketName) .versioningConfiguration(versioningConfiguration) .build(); // Enable versioning on the bucket. getClient().putBucketVersioning(putBucketVersioningRequest); PutObjectLockConfigurationRequest request = PutObjectLockConfigurationRequest.builder() .bucket(bucketName) .objectLockConfiguration(ObjectLockConfiguration.builder() .objectLockEnabled(ObjectLockEnabled.ENABLED) .build()) .build(); getClient().putObjectLockConfiguration(request); System.out.println("Successfully enabled object lock on "+bucketName); } catch (S3Exception ex) { System.out.println("Error modifying object lock: '" + ex.getMessage() + "'"); } } public void uploadFile(String bucketName, String objectName, String filePath) { Path file = Paths.get(filePath); PutObjectRequest request = PutObjectRequest.builder() .bucket(bucketName) .key(objectName) .checksumAlgorithm(ChecksumAlgorithm.SHA256) .build(); PutObjectResponse response = getClient().putObject(request, file); if (response != null) { System.out.println("\tSuccessfully uploaded " + objectName + " to " + bucketName + "."); } else { System.out.println("\tCould not upload " + objectName + " to " + bucketName + "."); } } // Set or modify a legal hold on an object in an S3 bucket. public void modifyObjectLegalHold(String bucketName, String objectKey, boolean legalHoldOn) { ObjectLockLegalHold legalHold ; if (legalHoldOn) { legalHold = ObjectLockLegalHold.builder() .status(ObjectLockLegalHoldStatus.ON) .build(); } else { legalHold = ObjectLockLegalHold.builder() .status(ObjectLockLegalHoldStatus.OFF) .build(); } PutObjectLegalHoldRequest legalHoldRequest = PutObjectLegalHoldRequest.builder() .bucket(bucketName) .key(objectKey) .legalHold(legalHold) .build(); getClient().putObjectLegalHold(legalHoldRequest) ; System.out.println("Modified legal hold for "+ objectKey +" in "+bucketName +"."); } // Delete an object from a specific bucket. public void deleteObjectFromBucket(String bucketName, String objectKey, boolean hasRetention, String versionId) { try { DeleteObjectRequest objectRequest; if (hasRetention) { objectRequest = DeleteObjectRequest.builder() .bucket(bucketName) .key(objectKey) .versionId(versionId) .bypassGovernanceRetention(true) .build(); } else { objectRequest = DeleteObjectRequest.builder() .bucket(bucketName) .key(objectKey) .versionId(versionId) .build(); } getClient().deleteObject(objectRequest) ; System.out.println("The object was successfully deleted"); } catch (S3Exception e) { System.err.println(e.awsErrorDetails().errorMessage()); } } // Get the retention period for an S3 object. public ObjectLockRetention getObjectRetention(String bucketName, String key){ try { GetObjectRetentionRequest retentionRequest = GetObjectRetentionRequest.builder() .bucket(bucketName) .key(key) .build(); GetObjectRetentionResponse response = getClient().getObjectRetention(retentionRequest); System.out.println("tObject retention for "+key +" in "+ bucketName +": " + response.retention().mode() +" until "+ response.retention().retainUntilDate() +"."); return response.retention(); } catch (S3Exception e) { System.err.println(e.awsErrorDetails().errorMessage()); return null; } } public void deleteBucketByName(String bucketName) { try { DeleteBucketRequest request = DeleteBucketRequest.builder() .bucket(bucketName) .build(); getClient().deleteBucket(request); System.out.println(bucketName +" was deleted."); } catch (S3Exception e) { System.err.println(e.awsErrorDetails().errorMessage()); } } // Get the object lock configuration details for an S3 bucket. public void getBucketObjectLockConfiguration(String bucketName) { GetObjectLockConfigurationRequest objectLockConfigurationRequest = GetObjectLockConfigurationRequest.builder() .bucket(bucketName) .build(); GetObjectLockConfigurationResponse response = getClient().getObjectLockConfiguration(objectLockConfigurationRequest); System.out.println("Bucket object lock config for "+bucketName +": "); System.out.println("\tEnabled: "+response.objectLockConfiguration().objectLockEnabled()); System.out.println("\tRule: "+ response.objectLockConfiguration().rule().defaultRetention()); } }
JavaScript
SDK for JavaScript (v3)
Note

There's more on GitHub. Find the complete example and learn how to set up and run in the AWS Code Examples Repository.

index.js - Entrypoint for the workflow. This orchestrates all of the steps. Visit GitHub to see the implementation details for Scenario, ScenarioInput, ScenarioOutput, and ScenarioAction.

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import * as Scenarios from "@aws-doc-sdk-examples/lib/scenario/index.js"; import { exitOnFalse, loadState, saveState, } from "@aws-doc-sdk-examples/lib/scenario/steps-common.js"; import { welcome, welcomeContinue } from "./welcome.steps.js"; import { confirmCreateBuckets, confirmPopulateBuckets, confirmSetLegalHoldFileEnabled, confirmSetLegalHoldFileRetention, confirmSetRetentionPeriodFileEnabled, confirmSetRetentionPeriodFileRetention, confirmUpdateLockPolicy, confirmUpdateRetention, createBuckets, createBucketsAction, populateBuckets, populateBucketsAction, setLegalHoldFileEnabledAction, setLegalHoldFileRetentionAction, setRetentionPeriodFileEnabledAction, setRetentionPeriodFileRetentionAction, updateLockPolicy, updateLockPolicyAction, updateRetention, updateRetentionAction, } from "./setup.steps.js"; /** * @param {Scenarios} scenarios * @param {Record<string, any>} initialState */ export const getWorkflowStages = (scenarios, initialState = {}) => { const client = new S3Client({}); return { deploy: new scenarios.Scenario( "S3 Object Locking - Deploy", [ welcome(scenarios), welcomeContinue(scenarios), exitOnFalse(scenarios, "welcomeContinue"), createBuckets(scenarios), confirmCreateBuckets(scenarios), exitOnFalse(scenarios, "confirmCreateBuckets"), createBucketsAction(scenarios, client), updateRetention(scenarios), confirmUpdateRetention(scenarios), exitOnFalse(scenarios, "confirmUpdateRetention"), updateRetentionAction(scenarios, client), populateBuckets(scenarios), confirmPopulateBuckets(scenarios), exitOnFalse(scenarios, "confirmPopulateBuckets"), populateBucketsAction(scenarios, client), updateLockPolicy(scenarios), confirmUpdateLockPolicy(scenarios), exitOnFalse(scenarios, "confirmUpdateLockPolicy"), updateLockPolicyAction(scenarios, client), confirmSetLegalHoldFileEnabled(scenarios), setLegalHoldFileEnabledAction(scenarios, client), confirmSetRetentionPeriodFileEnabled(scenarios), setRetentionPeriodFileEnabledAction(scenarios, client), confirmSetLegalHoldFileRetention(scenarios), setLegalHoldFileRetentionAction(scenarios, client), confirmSetRetentionPeriodFileRetention(scenarios), setRetentionPeriodFileRetentionAction(scenarios, client), saveState, ], initialState, ), demo: new scenarios.Scenario( "S3 Object Locking - Demo", [loadState, replAction(scenarios, client)], initialState, ), clean: new scenarios.Scenario( "S3 Object Locking - Destroy", [ loadState, confirmCleanup(scenarios), exitOnFalse(scenarios, "confirmCleanup"), cleanupAction(scenarios, client), ], initialState, ), }; }; // Call function if run directly import { fileURLToPath } from "url"; import { S3Client } from "@aws-sdk/client-s3"; import { cleanupAction, confirmCleanup } from "./clean.steps.js"; import { replAction } from "./repl.steps.js"; if (process.argv[1] === fileURLToPath(import.meta.url)) { const objectLockingScenarios = getWorkflowStages(Scenarios); Scenarios.parseScenarioArgs(objectLockingScenarios); }

welcome.steps.js - Output welcome messages to the console.

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 /** * @typedef {import("@aws-doc-sdk-examples/lib/scenario/index.js")} Scenarios */ /** * @param {Scenarios} scenarios */ const welcome = (scenarios) => new scenarios.ScenarioOutput( "welcome", `Welcome to the Amazon Simple Storage Service (S3) Object Locking Workflow Scenario. For this workflow, we will use the AWS SDK for JavaScript to create several S3 buckets and files to demonstrate working with S3 locking features.`, { header: true }, ); /** * @param {Scenarios} scenarios */ const welcomeContinue = (scenarios) => new scenarios.ScenarioInput( "welcomeContinue", "Press Enter when you are ready to start.", { type: "confirm" }, ); export { welcome, welcomeContinue };

setup.steps.js - Deploy buckets, objects, and file settings.

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { BucketVersioningStatus, ChecksumAlgorithm, CreateBucketCommand, MFADeleteStatus, PutBucketVersioningCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutObjectLegalHoldCommand, PutObjectRetentionCommand, ObjectLockLegalHoldStatus, ObjectLockRetentionMode, } from "@aws-sdk/client-s3"; /** * @typedef {import("@aws-doc-sdk-examples/lib/scenario/index.js")} Scenarios */ /** * @typedef {import("@aws-sdk/client-s3").S3Client} S3Client */ const bucketPrefix = "js-object-locking"; /** * @param {Scenarios} scenarios * @param {S3Client} client */ const createBuckets = (scenarios) => new scenarios.ScenarioOutput( "createBuckets", `The following buckets will be created: ${bucketPrefix}-no-lock with object lock False. ${bucketPrefix}-lock-enabled with object lock True. ${bucketPrefix}-retention-after-creation with object lock False.`, { preformatted: true }, ); /** * @param {Scenarios} scenarios */ const confirmCreateBuckets = (scenarios) => new scenarios.ScenarioInput("confirmCreateBuckets", "Create the buckets?", { type: "confirm", }); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const createBucketsAction = (scenarios, client) => new scenarios.ScenarioAction("createBucketsAction", async (state) => { const noLockBucketName = `${bucketPrefix}-no-lock`; const lockEnabledBucketName = `${bucketPrefix}-lock-enabled`; const retentionBucketName = `${bucketPrefix}-retention-after-creation`; await client.send(new CreateBucketCommand({ Bucket: noLockBucketName })); await client.send( new CreateBucketCommand({ Bucket: lockEnabledBucketName, ObjectLockEnabledForBucket: true, }), ); await client.send(new CreateBucketCommand({ Bucket: retentionBucketName })); state.noLockBucketName = noLockBucketName; state.lockEnabledBucketName = lockEnabledBucketName; state.retentionBucketName = retentionBucketName; }); /** * @param {Scenarios} scenarios */ const populateBuckets = (scenarios) => new scenarios.ScenarioOutput( "populateBuckets", `The following test files will be created: file0.txt in ${bucketPrefix}-no-lock. file1.txt in ${bucketPrefix}-no-lock. file0.txt in ${bucketPrefix}-lock-enabled. file1.txt in ${bucketPrefix}-lock-enabled. file0.txt in ${bucketPrefix}-retention-after-creation. file1.txt in ${bucketPrefix}-retention-after-creation.`, { preformatted: true }, ); /** * @param {Scenarios} scenarios */ const confirmPopulateBuckets = (scenarios) => new scenarios.ScenarioInput( "confirmPopulateBuckets", "Populate the buckets?", { type: "confirm" }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const populateBucketsAction = (scenarios, client) => new scenarios.ScenarioAction("populateBucketsAction", async (state) => { await client.send( new PutObjectCommand({ Bucket: state.noLockBucketName, Key: "file0.txt", Body: "Content", ChecksumAlgorithm: ChecksumAlgorithm.SHA256, }), ); await client.send( new PutObjectCommand({ Bucket: state.noLockBucketName, Key: "file1.txt", Body: "Content", ChecksumAlgorithm: ChecksumAlgorithm.SHA256, }), ); await client.send( new PutObjectCommand({ Bucket: state.lockEnabledBucketName, Key: "file0.txt", Body: "Content", ChecksumAlgorithm: ChecksumAlgorithm.SHA256, }), ); await client.send( new PutObjectCommand({ Bucket: state.lockEnabledBucketName, Key: "file1.txt", Body: "Content", ChecksumAlgorithm: ChecksumAlgorithm.SHA256, }), ); await client.send( new PutObjectCommand({ Bucket: state.retentionBucketName, Key: "file0.txt", Body: "Content", ChecksumAlgorithm: ChecksumAlgorithm.SHA256, }), ); await client.send( new PutObjectCommand({ Bucket: state.retentionBucketName, Key: "file1.txt", Body: "Content", ChecksumAlgorithm: ChecksumAlgorithm.SHA256, }), ); }); /** * @param {Scenarios} scenarios */ const updateRetention = (scenarios) => new scenarios.ScenarioOutput( "updateRetention", `A bucket can be configured to use object locking with a default retention period. A default retention period will be configured for ${bucketPrefix}-retention-after-creation.`, { preformatted: true }, ); /** * @param {Scenarios} scenarios */ const confirmUpdateRetention = (scenarios) => new scenarios.ScenarioInput( "confirmUpdateRetention", "Configure default retention period?", { type: "confirm" }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const updateRetentionAction = (scenarios, client) => new scenarios.ScenarioAction("updateRetentionAction", async (state) => { await client.send( new PutBucketVersioningCommand({ Bucket: state.retentionBucketName, VersioningConfiguration: { MFADelete: MFADeleteStatus.Disabled, Status: BucketVersioningStatus.Enabled, }, }), ); await client.send( new PutObjectLockConfigurationCommand({ Bucket: state.retentionBucketName, ObjectLockConfiguration: { ObjectLockEnabled: "Enabled", Rule: { DefaultRetention: { Mode: "GOVERNANCE", Years: 1, }, }, }, }), ); }); /** * @param {Scenarios} scenarios */ const updateLockPolicy = (scenarios) => new scenarios.ScenarioOutput( "updateLockPolicy", `Object lock policies can also be added to existing buckets. An object lock policy will be added to ${bucketPrefix}-lock-enabled.`, { preformatted: true }, ); /** * @param {Scenarios} scenarios */ const confirmUpdateLockPolicy = (scenarios) => new scenarios.ScenarioInput( "confirmUpdateLockPolicy", "Add object lock policy?", { type: "confirm" }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const updateLockPolicyAction = (scenarios, client) => new scenarios.ScenarioAction("updateLockPolicyAction", async (state) => { await client.send( new PutObjectLockConfigurationCommand({ Bucket: state.lockEnabledBucketName, ObjectLockConfiguration: { ObjectLockEnabled: "Enabled", }, }), ); }); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const confirmSetLegalHoldFileEnabled = (scenarios) => new scenarios.ScenarioInput( "confirmSetLegalHoldFileEnabled", (state) => `Would you like to add a legal hold to file0.txt in ${state.lockEnabledBucketName}?`, { type: "confirm", }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const setLegalHoldFileEnabledAction = (scenarios, client) => new scenarios.ScenarioAction( "setLegalHoldFileEnabledAction", async (state) => { await client.send( new PutObjectLegalHoldCommand({ Bucket: state.lockEnabledBucketName, Key: "file0.txt", LegalHold: { Status: ObjectLockLegalHoldStatus.ON, }, }), ); console.log( `Modified legal hold for file0.txt in ${state.lockEnabledBucketName}.`, ); }, { skipWhen: (state) => !state.confirmSetLegalHoldFileEnabled }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const confirmSetRetentionPeriodFileEnabled = (scenarios) => new scenarios.ScenarioInput( "confirmSetRetentionPeriodFileEnabled", (state) => `Would you like to add a 1 day Governance retention period to file1.txt in ${state.lockEnabledBucketName}? Reminder: Only a user with the s3:BypassGovernanceRetention permission will be able to delete this file or its bucket until the retention period has expired.`, { type: "confirm", }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const setRetentionPeriodFileEnabledAction = (scenarios, client) => new scenarios.ScenarioAction( "setRetentionPeriodFileEnabledAction", async (state) => { const retentionDate = new Date(); retentionDate.setDate(retentionDate.getDate() + 1); await client.send( new PutObjectRetentionCommand({ Bucket: state.lockEnabledBucketName, Key: "file1.txt", Retention: { Mode: ObjectLockRetentionMode.GOVERNANCE, RetainUntilDate: retentionDate, }, }), ); console.log( `Set retention for file1.txt in ${state.lockEnabledBucketName} until ${retentionDate.toISOString().split("T")[0]}.`, ); }, { skipWhen: (state) => !state.confirmSetRetentionPeriodFileEnabled }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const confirmSetLegalHoldFileRetention = (scenarios) => new scenarios.ScenarioInput( "confirmSetLegalHoldFileRetention", (state) => `Would you like to add a legal hold to file0.txt in ${state.retentionBucketName}?`, { type: "confirm", }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const setLegalHoldFileRetentionAction = (scenarios, client) => new scenarios.ScenarioAction( "setLegalHoldFileRetentionAction", async (state) => { await client.send( new PutObjectLegalHoldCommand({ Bucket: state.retentionBucketName, Key: "file0.txt", LegalHold: { Status: ObjectLockLegalHoldStatus.ON, }, }), ); console.log( `Modified legal hold for file0.txt in ${state.retentionBucketName}.`, ); }, { skipWhen: (state) => !state.confirmSetLegalHoldFileRetention }, ); /** * @param {Scenarios} scenarios */ const confirmSetRetentionPeriodFileRetention = (scenarios) => new scenarios.ScenarioInput( "confirmSetRetentionPeriodFileRetention", (state) => `Would you like to add a 1 day Governance retention period to file1.txt in ${state.retentionBucketName}? Reminder: Only a user with the s3:BypassGovernanceRetention permission will be able to delete this file or its bucket until the retention period has expired.`, { type: "confirm", }, ); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const setRetentionPeriodFileRetentionAction = (scenarios, client) => new scenarios.ScenarioAction( "setRetentionPeriodFileRetentionAction", async (state) => { const retentionDate = new Date(); retentionDate.setDate(retentionDate.getDate() + 1); await client.send( new PutObjectRetentionCommand({ Bucket: state.retentionBucketName, Key: "file1.txt", Retention: { Mode: ObjectLockRetentionMode.GOVERNANCE, RetainUntilDate: retentionDate, }, BypassGovernanceRetention: true, }), ); console.log( `Set retention for file1.txt in ${state.retentionBucketName} until ${retentionDate.toISOString().split("T")[0]}.`, ); }, { skipWhen: (state) => !state.confirmSetRetentionPeriodFileRetention }, ); export { createBuckets, confirmCreateBuckets, createBucketsAction, populateBuckets, confirmPopulateBuckets, populateBucketsAction, updateRetention, confirmUpdateRetention, updateRetentionAction, updateLockPolicy, confirmUpdateLockPolicy, updateLockPolicyAction, confirmSetLegalHoldFileEnabled, setLegalHoldFileEnabledAction, confirmSetRetentionPeriodFileEnabled, setRetentionPeriodFileEnabledAction, confirmSetLegalHoldFileRetention, setLegalHoldFileRetentionAction, confirmSetRetentionPeriodFileRetention, setRetentionPeriodFileRetentionAction, };

repl.steps.js - View and delete files in the buckets.

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { ChecksumAlgorithm, DeleteObjectCommand, GetObjectLegalHoldCommand, GetObjectLockConfigurationCommand, GetObjectRetentionCommand, ListObjectVersionsCommand, PutObjectCommand, } from "@aws-sdk/client-s3"; /** * @typedef {import("@aws-doc-sdk-examples/lib/scenario/index.js")} Scenarios */ /** * @typedef {import("@aws-sdk/client-s3").S3Client} S3Client */ const choices = { EXIT: 0, LIST_ALL_FILES: 1, DELETE_FILE: 2, DELETE_FILE_WITH_RETENTION: 3, OVERWRITE_FILE: 4, VIEW_RETENTION_SETTINGS: 5, VIEW_LEGAL_HOLD_SETTINGS: 6, }; /** * @param {Scenarios} scenarios */ const replInput = (scenarios) => new scenarios.ScenarioInput( "replChoice", `Explore the S3 locking features by selecting one of the following choices`, { type: "select", choices: [ { name: "List all files in buckets", value: choices.LIST_ALL_FILES }, { name: "Attempt to delete a file.", value: choices.DELETE_FILE }, { name: "Attempt to delete a file with retention period bypass.", value: choices.DELETE_FILE_WITH_RETENTION, }, { name: "Attempt to overwrite a file.", value: choices.OVERWRITE_FILE }, { name: "View the object and bucket retention settings for a file.", value: choices.VIEW_RETENTION_SETTINGS, }, { name: "View the legal hold settings for a file.", value: choices.VIEW_LEGAL_HOLD_SETTINGS, }, { name: "Finish the workflow.", value: choices.EXIT }, ], }, ); /** * @param {S3Client} client * @param {string[]} buckets */ const getAllFiles = async (client, buckets) => { /** @type {{bucket: string, key: string, version: string}[]} */ const files = []; for (const bucket of buckets) { const objectsResponse = await client.send( new ListObjectVersionsCommand({ Bucket: bucket }), ); for (const version of objectsResponse.Versions || []) { const { Key, VersionId } = version; files.push({ bucket, key: Key, version: VersionId }); } } return files; }; /** * @param {Scenarios} scenarios * @param {S3Client} client */ const replAction = (scenarios, client) => new scenarios.ScenarioAction( "replAction", async (state) => { const files = await getAllFiles(client, [ state.noLockBucketName, state.lockEnabledBucketName, state.retentionBucketName, ]); const fileInput = new scenarios.ScenarioInput( "selectedFile", "Select a file:", { type: "select", choices: files.map((file, index) => ({ name: `${index + 1}: ${file.bucket}: ${file.key} (version: ${ file.version })`, value: index, })), }, ); const { replChoice } = state; switch (replChoice) { case choices.LIST_ALL_FILES: { const files = await getAllFiles(client, [ state.noLockBucketName, state.lockEnabledBucketName, state.retentionBucketName, ]); state.replOutput = files .map( (file) => `${file.bucket}: ${file.key} (version: ${file.version})`, ) .join("\n"); break; } case choices.DELETE_FILE: { /** @type {number} */ const fileToDelete = await fileInput.handle(state); const selectedFile = files[fileToDelete]; try { await client.send( new DeleteObjectCommand({ Bucket: selectedFile.bucket, Key: selectedFile.key, VersionId: selectedFile.version, }), ); state.replOutput = `Deleted ${selectedFile.key} in ${selectedFile.bucket}.`; } catch (err) { state.replOutput = `Unable to delete object ${selectedFile.key} in bucket ${selectedFile.bucket}: ${err.message}`; } break; } case choices.DELETE_FILE_WITH_RETENTION: { /** @type {number} */ const fileToDelete = await fileInput.handle(state); const selectedFile = files[fileToDelete]; try { await client.send( new DeleteObjectCommand({ Bucket: selectedFile.bucket, Key: selectedFile.key, VersionId: selectedFile.version, BypassGovernanceRetention: true, }), ); state.replOutput = `Deleted ${selectedFile.key} in ${selectedFile.bucket}.`; } catch (err) { state.replOutput = `Unable to delete object ${selectedFile.key} in bucket ${selectedFile.bucket}: ${err.message}`; } break; } case choices.OVERWRITE_FILE: { /** @type {number} */ const fileToOverwrite = await fileInput.handle(state); const selectedFile = files[fileToOverwrite]; try { await client.send( new PutObjectCommand({ Bucket: selectedFile.bucket, Key: selectedFile.key, Body: "New content", ChecksumAlgorithm: ChecksumAlgorithm.SHA256, }), ); state.replOutput = `Overwrote ${selectedFile.key} in ${selectedFile.bucket}.`; } catch (err) { state.replOutput = `Unable to overwrite object ${selectedFile.key} in bucket ${selectedFile.bucket}: ${err.message}`; } break; } case choices.VIEW_RETENTION_SETTINGS: { /** @type {number} */ const fileToView = await fileInput.handle(state); const selectedFile = files[fileToView]; try { const retention = await client.send( new GetObjectRetentionCommand({ Bucket: selectedFile.bucket, Key: selectedFile.key, VersionId: selectedFile.version, }), ); const bucketConfig = await client.send( new GetObjectLockConfigurationCommand({ Bucket: selectedFile.bucket, }), ); state.replOutput = `Object retention for ${selectedFile.key} in ${selectedFile.bucket}: ${retention.Retention?.Mode} until ${retention.Retention?.RetainUntilDate?.toISOString()}. Bucket object lock config for ${selectedFile.bucket} in ${selectedFile.bucket}: Enabled: ${bucketConfig.ObjectLockConfiguration?.ObjectLockEnabled} Rule: ${JSON.stringify(bucketConfig.ObjectLockConfiguration?.Rule?.DefaultRetention)}`; } catch (err) { state.replOutput = `Unable to fetch object lock retention: '${err.message}'`; } break; } case choices.VIEW_LEGAL_HOLD_SETTINGS: { /** @type {number} */ const fileToView = await fileInput.handle(state); const selectedFile = files[fileToView]; try { const legalHold = await client.send( new GetObjectLegalHoldCommand({ Bucket: selectedFile.bucket, Key: selectedFile.key, VersionId: selectedFile.version, }), ); state.replOutput = `Object legal hold for ${selectedFile.key} in ${selectedFile.bucket}: Status: ${legalHold.LegalHold?.Status}`; } catch (err) { state.replOutput = `Unable to fetch legal hold: '${err.message}'`; } break; } default: throw new Error(`Invalid replChoice: ${replChoice}`); } }, { whileConfig: { whileFn: ({ replChoice }) => replChoice !== choices.EXIT, input: replInput(scenarios), output: new scenarios.ScenarioOutput( "REPL output", (state) => state.replOutput, { preformatted: true }, ), }, }, ); export { replInput, replAction, choices };

clean.steps.js - Destroy all created resources.

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { DeleteObjectCommand, DeleteBucketCommand, ListObjectVersionsCommand, GetObjectLegalHoldCommand, GetObjectRetentionCommand, PutObjectLegalHoldCommand, } from "@aws-sdk/client-s3"; /** * @typedef {import("@aws-doc-sdk-examples/lib/scenario/index.js")} Scenarios */ /** * @typedef {import("@aws-sdk/client-s3").S3Client} S3Client */ /** * @param {Scenarios} scenarios */ const confirmCleanup = (scenarios) => new scenarios.ScenarioInput("confirmCleanup", "Clean up resources?", { type: "confirm", }); /** * @param {Scenarios} scenarios * @param {S3Client} client */ const cleanupAction = (scenarios, client) => new scenarios.ScenarioAction("cleanupAction", async (state) => { const { noLockBucketName, lockEnabledBucketName, retentionBucketName } = state; const buckets = [ noLockBucketName, lockEnabledBucketName, retentionBucketName, ]; for (const bucket of buckets) { /** @type {import("@aws-sdk/client-s3").ListObjectVersionsCommandOutput} */ let objectsResponse; try { objectsResponse = await client.send( new ListObjectVersionsCommand({ Bucket: bucket, }), ); } catch (e) { if (e instanceof Error && e.name === "NoSuchBucket") { console.log("Object's bucket has already been deleted."); continue; } else { throw e; } } for (const version of objectsResponse.Versions || []) { const { Key, VersionId } = version; try { const legalHold = await client.send( new GetObjectLegalHoldCommand({ Bucket: bucket, Key, VersionId, }), ); if (legalHold.LegalHold?.Status === "ON") { await client.send( new PutObjectLegalHoldCommand({ Bucket: bucket, Key, VersionId, LegalHold: { Status: "OFF", }, }), ); } } catch (err) { console.log( `Unable to fetch legal hold for ${Key} in ${bucket}: '${err.message}'`, ); } try { const retention = await client.send( new GetObjectRetentionCommand({ Bucket: bucket, Key, VersionId, }), ); if (retention.Retention?.Mode === "GOVERNANCE") { await client.send( new DeleteObjectCommand({ Bucket: bucket, Key, VersionId, BypassGovernanceRetention: true, }), ); } } catch (err) { console.log( `Unable to fetch object lock retention for ${Key} in ${bucket}: '${err.message}'`, ); } await client.send( new DeleteObjectCommand({ Bucket: bucket, Key, VersionId, }), ); } await client.send(new DeleteBucketCommand({ Bucket: bucket })); console.log(`Delete for ${bucket} complete.`); } }); export { confirmCleanup, cleanupAction };

For a complete list of AWS SDK developer guides and code examples, see Using this service with an AWS SDK. This topic also includes information about getting started and details about previous SDK versions.