diff --git a/all-examples/net/migration/Makefile b/all-examples/net/migration/Makefile new file mode 100644 index 00000000..5274c61a --- /dev/null +++ b/all-examples/net/migration/Makefile @@ -0,0 +1,50 @@ +# Makefile for S3 Encryption Client .NET migration Example + +# Default target +.PHONY: all install clean run help + +# Default arguments for running the example +# Override these when calling make run +BUCKET_NAME ?= testwiththisbucket +KMS_KEY_ID ?= arn:aws:kms:us-west-2:992382771485:key/ad1d7ff3-79f3-40f4-b31d-7be4d9c8b3ca + +all: install + +# Install dependencies using .NET modules +install: + @echo "[NET migration] Installing .NET dependencies..." + dotnet restore + @echo "[NET migration] Dependencies installed successfully!" + +# Clean .NET artifacts +clean: + @echo "[NET migration] Cleaning .NET artifacts..." + dotnet clean + @echo "[NET migration] Clean completed!" + +# Run the example with default arguments +run: install + @echo "[NET migration] Running S3 Encryption Client .NET Migration example..." + @export MigrationExample_S3_BUCKET=$(BUCKET_NAME) && export MigrationExample_KMS_KEY_ARN=$(KMS_KEY_ID) && dotnet test + +# Run with custom arguments +# Usage: make run-custom BUCKET_NAME=my-bucket KMS_KEY_ID=my-kms-key +run-custom: install + @export MigrationExample_S3_BUCKET=$(BUCKET_NAME) && export MigrationExample_KMS_KEY_ARN=$(KMS_KEY_ID) && dotnet test + +# Show help +help: + @echo "S3 Encryption Client .NET migration Example Makefile" + @echo "" + @echo "Available targets:" + @echo " install - Install .NET dependencies using .NET modules" + @echo " run - Install dependencies and run the example with default parameters" + @echo " run-custom - Install dependencies and run with custom parameters" + @echo " clean - Remove .NET artifacts" + @echo " help - Show this help message" + @echo "" + @echo "" + @echo "Prerequisites:" + @echo " - Supported .NET framework installed on the system. See https://www.nuget.org/packages/Amazon.Extensions.S3.Encryption for supported one." + @echo " - AWS credentials configured (AWS CLI, environment variables, or IAM role)" + @echo " - Valid S3 bucket and KMS key with appropriate permissions" \ No newline at end of file diff --git a/all-examples/net/migration/MigrationStep0.cs b/all-examples/net/migration/MigrationStep0.cs new file mode 100644 index 00000000..b9aaf660 --- /dev/null +++ b/all-examples/net/migration/MigrationStep0.cs @@ -0,0 +1,70 @@ +using Amazon.Extensions.S3.Encryption; +using Amazon.Extensions.S3.Encryption.Primitives; +using Amazon.S3.Model; + +namespace migration +{ + // Migration Step 0: This example demonstrates use of the S3 Encryption Client for .NET v2 + // and is the starting state for migrating your data to the v4 client. + // + // This example's purpose is to model behavior of an existing v2 client. + // Subsequent migration steps will demonstrate code changes needed to use the v4 client. + // + // This example configures a v2 client to: + // - Write objects using non-key committing encryption algorithms + // - Read objects encrypted with either key committing algorithms or with non-key committing algorithms + // + // In this configuration, the client can read objects encrypted + // with non-key committing algorithms (written by this v2 client or an in-progress v4 migration), + // as well as objects encrypted by a migrated v4 client + // that is configured to write objects encrypted with key committing algorithms. + // You should ensure you are using the latest version of the v2 client + // that can read objects encrypted with key committing algorithms before proceeding with migration. + public static class MigrationStep0 + { + public static async Task MigrationExampleStep0(string kmsKeyId, string bucket, string putObjectKey, string getObjectKey, string contentToPut) + { + Console.WriteLine("\n[Migration Step 0] Starting Step 0 Migration step. Inputs received: \n" + + "kms key ID: " + kmsKeyId + "\n" + + "bucket: " + bucket + "\n" + + "put object key: " + putObjectKey + "\n" + + "get object key: " + getObjectKey + "\n" + + "content to put: " + contentToPut + "\n" + ); + + var encryptionContext = new Dictionary(); + var encryptionMaterial = new EncryptionMaterialsV2(kmsKeyId, KmsType.KmsContext, encryptionContext); +#pragma warning disable 0618 + var configuration = new AmazonS3CryptoConfigurationV2(SecurityProfile.V2); +#pragma warning enable 0618 + var encryptionClient = new AmazonS3EncryptionClientV2(configuration, encryptionMaterial); + + await encryptionClient.PutObjectAsync( + new PutObjectRequest + { + BucketName = bucket, + Key = putObjectKey, + ContentBody = contentToPut + } + ); + + var response = await encryptionClient.GetObjectAsync( + new GetObjectRequest + { + BucketName = bucket, + Key = getObjectKey + } + ); + + using var reader = new StreamReader(response.ResponseStream); + var returnedContent = await reader.ReadToEndAsync(); + + if (returnedContent != contentToPut) + { + throw new Exception("Content received from getObject call does not match content put to S3."); + } + + Console.WriteLine($"[Migration Step 0] success: encryption with {putObjectKey} and decryption with {getObjectKey} completed successfully!"); + } + } +} \ No newline at end of file diff --git a/all-examples/net/migration/MigrationStep0Test.cs b/all-examples/net/migration/MigrationStep0Test.cs new file mode 100644 index 00000000..5ca0882f --- /dev/null +++ b/all-examples/net/migration/MigrationStep0Test.cs @@ -0,0 +1,51 @@ +using Xunit; + +namespace migration +{ + public class MigrationStep0Test + { + [Trait("Category", "Test-Migration-Step0")] + [Fact] + public async Task TestMigrationStep0() + { + const string content = "test-content"; + var kmsKeyId = TestUtils.TEST_KMS_KEY_ID; + var bucket = TestUtils.TEST_S3_BUCKET; + var baseKey = $"migration-test-{Guid.NewGuid()}"; + var objectKeys = new[] { $"{baseKey}-0", $"{baseKey}-1", $"{baseKey}-2", $"{baseKey}-3" }; + + // In cross-step compatibility tests, sometimes only the get operation is needed, so we provide a + // dummy key to skip the put operation while maintaining the example's structure. + // Examples are intentionally kept simple and unmodified for customer readability. + var dummyObjectKey = $"{baseKey}-dummy"; + + // Successfully round trip step 0 + await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, objectKeys[0], objectKeys[0], content); + + // Given: Step 1 round trip has succeeded with put/get object key = 1 + await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, objectKeys[1], objectKeys[1], content); + + // When: Execute Step 0 with getObjectKey=1, Then: Success (can read objects from Step 1) + await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, dummyObjectKey, objectKeys[1], content); + + // Given: Step 2 round trip has succeeded with put/get object key = 2 + await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, objectKeys[2], objectKeys[2], content); + + // When: Execute Step 0 with getObjectKey=2, Then: Success (can read commited objects) + await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, dummyObjectKey, objectKeys[2], content); + + // Given: Step 3 round trip has succeeded with put/get object key = 3 + await MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, objectKeys[3], objectKeys[3], content); + + // When: Execute Step 0 with getObjectKey=3, Then: Success (can read commited objects) + await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, dummyObjectKey, objectKeys[3], content); + + // Cleanup + foreach (var key in objectKeys) + { + await TestUtils.CleanupObject(bucket, key); + } + await TestUtils.CleanupObject(bucket, dummyObjectKey); + } + } +} diff --git a/all-examples/net/migration/MigrationStep1.cs b/all-examples/net/migration/MigrationStep1.cs new file mode 100644 index 00000000..6762b3dc --- /dev/null +++ b/all-examples/net/migration/MigrationStep1.cs @@ -0,0 +1,71 @@ +using Amazon.Extensions.S3.Encryption; +using Amazon.Extensions.S3.Encryption.Primitives; +using Amazon.S3.Model; + +namespace migration +{ + // Migration Step 1: This example demonstrates how to start using the S3 Encryption Client v4. + // + // This example's purpose is to demonstrate the code changes to + // migrate from the v3 client to the v4 client while maintaining identical behavior. + // + // When starting from a v3 client modeled in "Migration Step 0", + // "Migration Step 1" should result in no behavioral changes to your application. + // + // In this example we configure a v4 client to: + // - Write objects encrypted with non-key committing algorithms + // - Read objects encrypted either with or without key committing algorithms + // + // In this configuration, the client will continue to read objects encrypted + // with non-key committing algorithms (written by a v2 client or this migration-in-progress v4 client), + // as well as objects encrypted by a migrated v4 client + // that is configured to write objects encrypted with key committing algorithms. + // + // This configuration results in identical behavior to the S3 Encryption Client v2 client + // configured to use the default FORBID_ENCRYPT_ALLOW_DECRYPT commitment policy. + public static class MigrationStep1 + { + public static async Task MigrationExampleStep1(string kmsKeyId, string bucket, string putObjectKey, string getObjectKey, string contentToPut) + { + Console.WriteLine("\n[Migration Step 1] Starting Step 1 Migration step. Inputs received: \n" + + "kms key ID: " + kmsKeyId + "\n" + + "bucket: " + bucket + "\n" + + "put object key: " + putObjectKey + "\n" + + "get object key: " + getObjectKey + "\n" + + "content to put: " + contentToPut + "\n" + ); + + var encryptionContext = new Dictionary(); + var encryptionMaterial = new EncryptionMaterialsV4(kmsKeyId, KmsType.KmsContext, encryptionContext); + var configuration = new AmazonS3CryptoConfigurationV4(SecurityProfile.V4, CommitmentPolicy.ForbidEncryptAllowDecrypt, ContentEncryptionAlgorithm.AesGcm); + var encryptionClient = new AmazonS3EncryptionClientV4(configuration, encryptionMaterial); + + await encryptionClient.PutObjectAsync( + new PutObjectRequest + { + BucketName = bucket, + Key = putObjectKey, + ContentBody = contentToPut + } + ); + + var response = await encryptionClient.GetObjectAsync( + new GetObjectRequest + { + BucketName = bucket, + Key = getObjectKey + } + ); + + using var reader = new StreamReader(response.ResponseStream); + var returnedContent = await reader.ReadToEndAsync(); + + if (returnedContent != contentToPut) + { + throw new Exception("Content received from getObject call does not match content put to S3."); + } + + Console.WriteLine($"[Migration Step 1] success: encryption with {putObjectKey} and decryption with {getObjectKey} completed successfully!"); + } + } +} \ No newline at end of file diff --git a/all-examples/net/migration/MigrationStep1Test.cs b/all-examples/net/migration/MigrationStep1Test.cs new file mode 100644 index 00000000..bca32f25 --- /dev/null +++ b/all-examples/net/migration/MigrationStep1Test.cs @@ -0,0 +1,51 @@ +using Xunit; + +namespace migration +{ + public class MigrationStep1Test + { + [Trait("Category", "Test-Migration-Step1")] + [Fact] + public async Task TestMigrationStep1() + { + const string content = "test-content"; + var kmsKeyId = TestUtils.TEST_KMS_KEY_ID; + var bucket = TestUtils.TEST_S3_BUCKET; + var baseKey = $"migration-test-{Guid.NewGuid()}"; + var objectKeys = new[] { $"{baseKey}-0", $"{baseKey}-1", $"{baseKey}-2", $"{baseKey}-3" }; + + // In cross-step compatibility tests, sometimes only the get operation is needed, so we provide a + // dummy key to skip the put operation while maintaining the example's structure. + // Examples are intentionally kept simple and unmodified for customer readability. + var dummyObjectKey = $"{baseKey}-dummy"; + + // Successfully round trip step 1 + await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, objectKeys[1], objectKeys[1], content); + + // Given: Step 0 round trip has succeeded with put/get object key = 1 + await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, objectKeys[0], objectKeys[0], content); + + // When: Execute Step 1 with getObjectKey=0, Then: Success (can read from Step 0) + await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, dummyObjectKey, objectKeys[0], content); + + // Given: Step 2 round trip has succeeded with put/get object key = 2 + await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, objectKeys[2], objectKeys[2], content); + + // When: Execute Step 1 with getObjectKey=2, Then: Success (can read from Step 2) + await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, dummyObjectKey, objectKeys[2], content); + + // Given: Step 3 round trip has succeeded with put/get object key = 3 + await MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, objectKeys[3], objectKeys[3], content); + + // When: Execute Step 1 with getObjectKey=3, Then: Success (can read from Step 3) + await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, dummyObjectKey, objectKeys[3], content); + + // Cleanup + foreach (var key in objectKeys) + { + await TestUtils.CleanupObject(bucket, key); + } + await TestUtils.CleanupObject(bucket, dummyObjectKey); + } + } +} diff --git a/all-examples/net/migration/MigrationStep2.cs b/all-examples/net/migration/MigrationStep2.cs new file mode 100644 index 00000000..1b46f749 --- /dev/null +++ b/all-examples/net/migration/MigrationStep2.cs @@ -0,0 +1,78 @@ +using Amazon.Extensions.S3.Encryption; +using Amazon.Extensions.S3.Encryption.Primitives; +using Amazon.S3.Model; + +namespace migration +{ + // Migration Step 2: This example demonstrates how to update your v4 client configuration + // to start writing objects encrypted with key committing algorithms. + // + // This example's purpose is to demonstrate the commitment policy code changes required to + // start writing objects encrypted with key committing algorithms + // and document the behavioral changes that will result from this change. + // + // When starting from a v4 client modeled in "Migration Step 1", + // "Migration Step 2" WILL result in behavioral changes to your application. + // The client will start writing objects encrypted with key committing algorithms. + // + // IMPORTANT: You MUST have updated your readers to be able to read objects encrypted with key committing algorithms + // before deploying the changes in this step. + // This means deploying the changes from either "Migration Step 0" (if readers are v2 clients) + // or "Migration Step 1" (if readers are v4 clients) to all of your readers + // before deploying the changes from to "Migration Step 2". + // + // Once you deploy this change to your writers, your readers will start seeing + // some objects encrypted with non-key committing algorithms, + // and some objects encrypted with key committing algorithms. + // Because the changes would have already been deployed to all our readers from earlier migration steps, + // we can be sure that our entire system is ready to read both types of objects. + // After deploying these changes but before proceeding to "Migration Step 3", + // you MUST take extra steps to ensure that your system is no longer reading + // objects encrypted with non-key committing algorithms + // (such as re-encrypting any existing objects using key committing algorithms). + public static class MigrationStep2 + { + public static async Task MigrationExampleStep2(string kmsKeyId, string bucket, string putObjectKey, string getObjectKey, string contentToPut) + { + Console.WriteLine("\n[Migration Step 2] Starting Step 2 Migration step. Inputs received: \n" + + "kms key ID: " + kmsKeyId + "\n" + + "bucket: " + bucket + "\n" + + "put object key: " + putObjectKey + "\n" + + "get object key: " + getObjectKey + "\n" + + "content to put: " + contentToPut + "\n" + ); + + var encryptionContext = new Dictionary(); + var encryptionMaterial = new EncryptionMaterialsV4(kmsKeyId, KmsType.KmsContext, encryptionContext); + var configuration = new AmazonS3CryptoConfigurationV4(SecurityProfile.V4, CommitmentPolicy.RequireEncryptAllowDecrypt, ContentEncryptionAlgorithm.AesGcmWithCommitment); + var encryptionClient = new AmazonS3EncryptionClientV4(configuration, encryptionMaterial); + + await encryptionClient.PutObjectAsync( + new PutObjectRequest + { + BucketName = bucket, + Key = putObjectKey, + ContentBody = contentToPut + } + ); + + var response = await encryptionClient.GetObjectAsync( + new GetObjectRequest + { + BucketName = bucket, + Key = getObjectKey + } + ); + + using var reader = new StreamReader(response.ResponseStream); + var returnedContent = await reader.ReadToEndAsync(); + + if (returnedContent != contentToPut) + { + throw new Exception("Content received from getObject call does not match content put to S3."); + } + + Console.WriteLine($"[Migration Step 2] success: encryption with {putObjectKey} and decryption with {getObjectKey} completed successfully!"); + } + } +} \ No newline at end of file diff --git a/all-examples/net/migration/MigrationStep2Test.cs b/all-examples/net/migration/MigrationStep2Test.cs new file mode 100644 index 00000000..cfc145bc --- /dev/null +++ b/all-examples/net/migration/MigrationStep2Test.cs @@ -0,0 +1,53 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace migration +{ + public class MigrationStep2Test + { + [Trait("Category", "Test-Migration-Step2")] + [Fact] + public async Task TestMigrationStep2() + { + const string content = "test-content"; + var kmsKeyId = TestUtils.TEST_KMS_KEY_ID; + var bucket = TestUtils.TEST_S3_BUCKET; + var baseKey = $"migration-test-{Guid.NewGuid()}"; + var objectKeys = new[] { $"{baseKey}-0", $"{baseKey}-1", $"{baseKey}-2", $"{baseKey}-3" }; + + // In cross-step compatibility tests, sometimes only the get operation is needed, so we provide a + // dummy key to skip the put operation while maintaining the example's structure. + // Examples are intentionally kept simple and unmodified for customer readability. + var dummyObjectKey = $"{baseKey}-dummy"; + + // Successfully round trip step 2 + await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, objectKeys[2], objectKeys[2], content); + + // Given: Step 0 round trip has succeeded with put/get object key = 1 + await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, objectKeys[0], objectKeys[0], content); + + // When: Execute Step 2 with getObjectKey=0, Then: Success (can read from Step 0) + await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, dummyObjectKey, objectKeys[0], content); + + // Given: Step 1 round trip has succeeded with put/get object key = 2 + await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, objectKeys[1], objectKeys[1], content); + + // When: Execute Step 2 with getObjectKey=1, Then: Success (can read from Step 1) + await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, dummyObjectKey, objectKeys[1], content); + + // Given: Step 3 round trip has succeeded with put/get object key = 3 + await MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, objectKeys[3], objectKeys[3], content); + + // When: Execute Step 2 with getObjectKey=3, Then: Success (can read from Step 3) + await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, dummyObjectKey, objectKeys[3], content); + + // Cleanup + foreach (var key in objectKeys) + { + await TestUtils.CleanupObject(bucket, key); + } + await TestUtils.CleanupObject(bucket, dummyObjectKey); + } + } +} diff --git a/all-examples/net/migration/MigrationStep3.cs b/all-examples/net/migration/MigrationStep3.cs new file mode 100644 index 00000000..4911d720 --- /dev/null +++ b/all-examples/net/migration/MigrationStep3.cs @@ -0,0 +1,75 @@ +using Amazon.Extensions.S3.Encryption; +using Amazon.Extensions.S3.Encryption.Primitives; +using Amazon.S3.Model; + +namespace migration +{ + public static class MigrationStep3 + { + // Migration Step 3: This example demonstrates how to update your v4 client configuration + // to stop reading objects encrypted with non-key committing algorithms. + // + // This example's purpose is to demonstrate the commitment policy code changes required to + // stop reading objects encrypted with non-key committing algorithms + // and document the behavioral changes that will result from this change. + // + // When starting from a v4 client modeled in "Migration Step 2", + // "Migration Step 3" WILL result in behavioral changes to your application. + // The client will no longer be able to read objects encrypted with non-key committing algorithms. + // Before deploying these changes, you MUST have taken some extra steps + // to ensure that your system is no longer reading such objects, + // such as re-encrypting them with key committing algorithms. + // + // IMPORTANT: Before deploying the changes in this step, your system should not be reading + // any objects encrypted with non-key committing algorithms. + // The changes in this step will cause such read attempts to fail. + // This means the changes from "Migration Step 2" should have already been deployed to all of your readers + // before you deploy the changes from "Migration Step 3". + // + // Once you complete Step 3, you can be sure that all items being read by your system + // have been encrypted using key committing algorithms. + + public static async Task MigrationExampleStep3(string kmsKeyId, string bucket, string putObjectKey, string getObjectKey, string contentToPut) + { + Console.WriteLine("\n[Migration Step 3] Starting Step 3 Migration step. Inputs received: \n" + + "kms key ID: " + kmsKeyId + "\n" + + "bucket: " + bucket + "\n" + + "put object key: " + putObjectKey + "\n" + + "get object key: " + getObjectKey + "\n" + + "content to put: " + contentToPut + "\n" + ); + + var encryptionContext = new Dictionary(); + var encryptionMaterial = new EncryptionMaterialsV4(kmsKeyId, KmsType.KmsContext, encryptionContext); + var configuration = new AmazonS3CryptoConfigurationV4(SecurityProfile.V4, CommitmentPolicy.RequireEncryptRequireDecrypt, ContentEncryptionAlgorithm.AesGcmWithCommitment); + var encryptionClient = new AmazonS3EncryptionClientV4(configuration, encryptionMaterial); + + await encryptionClient.PutObjectAsync( + new PutObjectRequest + { + BucketName = bucket, + Key = putObjectKey, + ContentBody = contentToPut + } + ); + + var response = await encryptionClient.GetObjectAsync( + new GetObjectRequest + { + BucketName = bucket, + Key = getObjectKey + } + ); + + using var reader = new StreamReader(response.ResponseStream); + var returnedContent = await reader.ReadToEndAsync(); + + if (returnedContent != contentToPut) + { + throw new Exception("Content received from getObject call does not match content put to S3."); + } + + Console.WriteLine($"[Migration Step 3] success: encryption with {putObjectKey} and decryption with {getObjectKey} completed successfully!"); + } + } +} \ No newline at end of file diff --git a/all-examples/net/migration/MigrationStep3Test.cs b/all-examples/net/migration/MigrationStep3Test.cs new file mode 100644 index 00000000..319fba01 --- /dev/null +++ b/all-examples/net/migration/MigrationStep3Test.cs @@ -0,0 +1,61 @@ +using Xunit; + +namespace migration +{ + public class MigrationStep3Test + { + [Trait("Category", "Test-Migration-Step3")] + [Fact] + public async Task TestMigrationStep3() + { + const string content = "test-content"; + var kmsKeyId = TestUtils.TEST_KMS_KEY_ID; + var bucket = TestUtils.TEST_S3_BUCKET; + var baseKey = $"migration-test-{Guid.NewGuid()}"; + var objectKeys = new[] { $"{baseKey}-0", $"{baseKey}-1", $"{baseKey}-2", $"{baseKey}-3" }; + var commitmentPolicyError= + "The requested object is encrypted with non key committing algorithm " + + "but commitment policy is set to RequireEncryptRequireDecrypt. This commitment policy does not allow " + + "decryption of object encrypted with non key committing algorithm. " + + "Retry with RequireEncryptAllowDecrypt to encrypt with key committing algorithm and " + + "allow decryption for object encrypted with non key committing algorithm."; + + // In cross-step compatibility tests, sometimes only the get operation is needed, so we provide a + // dummy key to skip the put operation while maintaining the example's structure. + // Examples are intentionally kept simple and unmodified for customer readability. + var dummyObjectKey = $"{baseKey}-dummy"; + + // Successfully round trip step 3 + await MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, objectKeys[3], objectKeys[3], content); + + // Given: Step 0 round trip has succeeded with put/get object key = 1 + await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, objectKeys[0], objectKeys[0], content); + + // When: Execute Step 3 with getObjectKey=0, Then: should error out + var exception = await Assert.ThrowsAsync(() => + MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, dummyObjectKey, objectKeys[0], content)); + Assert.Contains(commitmentPolicyError, exception.Message); + + // Given: Step 1 round trip has succeeded with put/get object key = 2 + await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, objectKeys[1], objectKeys[1], content); + + // When: Execute Step 3 with getObjectKey=1, Then: should error out + exception = await Assert.ThrowsAsync(() => + MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, dummyObjectKey, objectKeys[1], content)); + Assert.Contains(commitmentPolicyError, exception.Message); + + // Given: Step 2 round trip has succeeded with put/get object key = 3 + await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, objectKeys[2], objectKeys[2], content); + + // When: Execute Step 3 with getObjectKey=2, Then: Success (can read from Step 2) + await MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, dummyObjectKey, objectKeys[2], content); + + // Cleanup + foreach (var key in objectKeys) + { + await TestUtils.CleanupObject(bucket, key); + } + await TestUtils.CleanupObject(bucket, dummyObjectKey); + } + } +} diff --git a/all-examples/net/migration/TestUtils.cs b/all-examples/net/migration/TestUtils.cs new file mode 100644 index 00000000..f2a4f45a --- /dev/null +++ b/all-examples/net/migration/TestUtils.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; +using Amazon.S3; +using Amazon.S3.Model; + +namespace migration +{ + public static class TestUtils + { + public static readonly string TEST_KMS_KEY_ID = Environment.GetEnvironmentVariable("MigrationExample_KMS_KEY_ARN") + ?? "arn:aws:kms:us-west-2:370957321024:alias/S3EC-Test-Server-Github-KMS-Key"; + + public static readonly string TEST_S3_BUCKET = Environment.GetEnvironmentVariable("MigrationExample_S3_BUCKET") + ?? "s3ec-test-server-github-bucket"; + + public static async Task CleanupObject(string bucket, string key) + { + var s3Client = new AmazonS3Client(); + await s3Client.DeleteObjectAsync(new DeleteObjectRequest + { + BucketName = bucket, + Key = key + }); + } + } +} diff --git a/all-examples/net/migration/migration.csproj b/all-examples/net/migration/migration.csproj new file mode 100644 index 00000000..c5d0a0c1 --- /dev/null +++ b/all-examples/net/migration/migration.csproj @@ -0,0 +1,26 @@ + + + + Exe + net8.0 + enable + enable + false + migration + + + + + + + + + + + + + + + + +