Use case
Lambda functions often handle sensitive data (PII, credentials, financial records) that has to be protected before it's logged, stored, or passed to another service, and to meet requirements like GDPR, HIPAA, or PCI-DSS.
Today .NET customers have no built-in way to do this, so they write their own masking logic. That tends to be inconsistent from one team to the next and easy to get wrong. Two things are especially awkward to build by hand: field-level encryption that leaves the rest of the payload readable (encrypting the whole object kills your ability to query, debug, or monitor it), and envelope encryption with KMS, which means wiring up the AWS Encryption SDK, data-key caching, and key management yourself.
Where this shows up in practice:
- Multi-tenant apps that need per-tenant encryption context to keep customer data isolated.
- Logging that has to redact sensitive fields but keep the surrounding log structure intact.
- API responses that need certain fields stripped before they go back to a client.
- Workflow state or checkpoints where the PII is encrypted but the metadata stays queryable.
Powertools for Python has a mature Data Masking utility, and TypeScript added one in v2.34.0 (aws-powertools/powertools-lambda-typescript#4960 / aws-powertools/powertools-lambda-typescript#5143). Java only has it as a roadmap item so far. This asks for the .NET equivalent.
Solution/User Experience
A new AWS.Lambda.Powertools.DataMasking package with a DataMasking type and a pluggable encryption provider. The default provider uses the AWS Encryption SDK for .NET with KMS envelope encryption.
var provider = new AwsEncryptionSdkProvider(new[] { KMS_KEY_ARN });
var masker = new DataMasking(provider);
There are three operations. Erase masks fields in place and can't be undone:
var masked = masker.Erase(data, fields: new[] { "email", "address.street", "customer.ssn" });
// { "email": "*****", "address": { "street": "*****", "city": "Anytown" }, ... }
EncryptAsync can encrypt the whole payload, or just named fields so the rest stays readable:
// full payload
var encrypted = await masker.EncryptAsync(data,
encryptionContext: new() { ["tenantId"] = "acme-corp" });
// field-level: orderId, customer.name and payment.amount stay visible
var partial = await masker.EncryptAsync(data,
fields: new[] { "customer.ssn", "payment.creditCard" },
encryptionContext: new() { ["tenantId"] = "acme-corp" });
DecryptAsync works out which format it's looking at and restores the original:
var decrypted = await masker.DecryptAsync(encrypted);
Beyond the default ***** mask, custom rules would be supported: a regex with a replacement format (e.g. j****@example.com), a length-preserving mask, or a fixed replacement string.
A few things worth pinning down in the design, borrowed from the Python implementation:
- The encryption context is bound at encrypt time and validated on decrypt, so it acts as authenticated data rather than a loose label.
- The provider caches data keys (cache size and max age configurable) to cut KMS calls per invoke, which matters under Lambda concurrency.
- Field selection uses the
AWS.Lambda.Powertools.JMESPath package that already lives in this repo, so paths behave the same way as elsewhere in the toolkit.
- Logging integration:
Erase can run over an object before it's logged through AWS.Lambda.Powertools.Logging.
Logger.LogInformation("Processing order: {Order}",
masker.Erase(order, fields: new[] { "creditCard" }));
The API should stay recognizable next to Python, Java, and TypeScript: Erase / Encrypt / Decrypt plus a swappable provider.
Alternative solutions
- Do it by hand: walk the object graph and call the AWS Encryption SDK directly. Works, but it's a lot of boilerplate per use case and drifts between teams.
- Use a generic masking or encryption library. Most aren't built for Lambda, don't integrate with the AWS Encryption SDK or KMS caching, and don't do field-level encryption that preserves structure.
- Lean on AWS services: Secrets Manager is for static secrets, Macie is for discovery and classification, and raw KMS is low-level. None of them give you runtime field masking with a nice API.
Acknowledgment
Ports the Data Masking utility from Powertools for AWS Lambda (TypeScript) v2.34.0 (aws-powertools/powertools-lambda-typescript#5143, originally requested in aws-powertools/powertools-lambda-typescript#4960). The reference design is the Powertools for Python implementation.
Use case
Lambda functions often handle sensitive data (PII, credentials, financial records) that has to be protected before it's logged, stored, or passed to another service, and to meet requirements like GDPR, HIPAA, or PCI-DSS.
Today .NET customers have no built-in way to do this, so they write their own masking logic. That tends to be inconsistent from one team to the next and easy to get wrong. Two things are especially awkward to build by hand: field-level encryption that leaves the rest of the payload readable (encrypting the whole object kills your ability to query, debug, or monitor it), and envelope encryption with KMS, which means wiring up the AWS Encryption SDK, data-key caching, and key management yourself.
Where this shows up in practice:
Powertools for Python has a mature Data Masking utility, and TypeScript added one in v2.34.0 (aws-powertools/powertools-lambda-typescript#4960 / aws-powertools/powertools-lambda-typescript#5143). Java only has it as a roadmap item so far. This asks for the .NET equivalent.
Solution/User Experience
A new
AWS.Lambda.Powertools.DataMaskingpackage with aDataMaskingtype and a pluggable encryption provider. The default provider uses the AWS Encryption SDK for .NET with KMS envelope encryption.There are three operations.
Erasemasks fields in place and can't be undone:EncryptAsynccan encrypt the whole payload, or just named fields so the rest stays readable:DecryptAsyncworks out which format it's looking at and restores the original:Beyond the default
*****mask, custom rules would be supported: a regex with a replacement format (e.g.j****@example.com), a length-preserving mask, or a fixed replacement string.A few things worth pinning down in the design, borrowed from the Python implementation:
AWS.Lambda.Powertools.JMESPathpackage that already lives in this repo, so paths behave the same way as elsewhere in the toolkit.Erasecan run over an object before it's logged throughAWS.Lambda.Powertools.Logging.The API should stay recognizable next to Python, Java, and TypeScript:
Erase/Encrypt/Decryptplus a swappable provider.Alternative solutions
Acknowledgment
Ports the Data Masking utility from Powertools for AWS Lambda (TypeScript) v2.34.0 (aws-powertools/powertools-lambda-typescript#5143, originally requested in aws-powertools/powertools-lambda-typescript#4960). The reference design is the Powertools for Python implementation.