Skip to content

AWS Deployment

This guide details how to deploy the PII Eraser AWS CloudFormation reference implementation. The reference implementation supports both ECS Fargate (serverless) and EC2 (high performance) launch types and features a load balancer, autoscaler and can either create it's own VPC or deploy into an existing VPC.

Stack Overview

This reference implementation deploys PII Eraser into a secure, high-availability architecture designed for production workloads. It leverages Amazon ECS (Elastic Container Service) to orchestrate the application, ensuring it can scale automatically based on traffic demand.

The architecture is designed with a "security-first" approach:

  • Network Isolation: The application containers are deployed into private subnets across two Availability Zones (AZs). They are not directly accessible from the public internet.
  • Traffic Management: An Internal Application Load Balancer (ALB) handles incoming traffic, distributing requests across healthy instances.
  • Auto Scaling: The stack includes an Application Auto Scaling policy that monitors CPU utilization. It automatically adds containers during high-load events and removes them during idle periods to save costs.
  • Outbound Access: A NAT Gateway is provisioned in the public subnets to allow for the container image to be pulled without allowing inbound connections.

Removing the NAT Gateway for Airgapped/Private Link Deployments

This reference implementation creates a NAT Gateway to pull the container image. If your environment is strictly airgapped and you mirror the image to a private internal ECR, you can remove the NAT Gateway and Route Table entries from vpc.yaml and rely on AWS PrivateLink (VPC Endpoints) for ECR and CloudWatch Logs.

Reference Implementation Components

The deployment is modularized into several scripts and CloudFormation templates:

File Description
deploy.sh Start Here. An interactive CLI script that packages the templates, and deploys the stack to your AWS account.
update.sh An interactive CLI script to update certain parameters such as config.yaml.
cleanup.sh CLI script to delete the stack.
create_network_for_testing.sh A CLI script that creates an existing VPC with the required subnets that the stack can be deployed into.
manage_service.py A Python utility to programmatically pause the service (scale to zero) or wake it up, ideal for batch processing workflows.
main.yaml The master orchestrator. It conditionally calls the nested stacks (Network, Common, and Compute) based on your inputs.
vpc.yaml Provisions the network layer. It creates the Virtual Private Cloud (VPC), Public/Private subnets, Route Tables, and the NAT Gateway.
common.yaml Shared infrastructure resources, including the ECS Cluster, IAM Roles (permissions), Security Groups, and the Load Balancer.
fargate.yaml Defines the service for Serverless execution. Used when you want AWS to manage the underlying infrastructure.
ec2.yaml Defines the service for EC2 execution. Used when you need specific high-performance instances (e.g., c8a or c8i with AVX-512 VNNI or AMX support) for maximum throughput.

Prerequisites

  1. AWS CLI installed and configured with appropriate permissions.
  2. Permissions: Your IAM user/role needs permissions to create:
    • VPC resources (Subnets, NAT Gateways, VPC Endpoints)
    • ECS Clusters and Services
    • Application Load Balancers
    • IAM Roles
  3. Deployment Scripts: Clone the Github Repository containing the deploy.sh and update.sh scripts.
  4. [Optional] Existing VPC: If you wish to deploy into an existing VPC, the VPC needs to have at least two subnets.
    • The subnets might need NAT Gateway access depending on how you pull the container.
    • If running strictly airgapped, ensure VPC Endpoints for ECR, S3, and CloudWatch Logs are present.

Deployment Steps

1. Prepare config.yaml

If you wish to use a custom configuration (e.g., specific allow_list items), please put your config.yaml file into the reference implementation directory. The deploy.sh and update.sh scripts automatically handle the injection of the config.yaml file into the container by Base64 encoding the file and passing it as a parameter to the CloudFormation stack, which in turn passes it to the containers via the CONFIG_B64 environment variable.

Security Best Practice

For production environments, the reference implemention can be modified to read the configuration from the AWS Systems Manager (SSM) Parameter Store rather than the CONFIG_B64 environment variable.

2. Prepare Stack Parameters

Open deploy.sh and main.yaml and edit the default values for stack parameters like REGION as desired.

3. Run Deployment Script

Run the deploy.sh script to create the stack. The script will ask you for various inputs such as the minimum number of containers the autoscaler should run.

It is advisable to use the script to create a CloudFormation Change Set. This allows you to review exactly what resources will be created before creating the stack. By default, it creates a secure VPC with private subnets for the application and public subnets for the Load Balancer.

4. Testing the Stack

Since PII Eraser is deployed into private subnets (for security), it is not directly accessible from the public internet by default.

To verify connectivity and process test inputs:

  1. Navigate to the AWS Lambda Console.
  2. Find the function named pii-prod-tester.
  3. Click the orange Test button (The default "Event JSON" is pre-configured for a basic check).
  4. Success: You will see a green box. Expand the "Details" to see the JSON response showing the transformed text.

To test specific PII Eraser features (like Redaction or Masking), you need to modify the Event JSON in the Lambda console.

  1. In the Test tab, scroll down to the Event JSON text area.
  2. Replace the existing JSON with one of the scenarios below.
  3. Click the orange Test button to send the request to your private container.

Example A: Simple Masking (Default)

This replaces PII with the specified mask character (default is #)

{
  "text": ["Call me at +49 586 249 509 or email [email protected]"],
  "operator": "mask"
}

Example B: Redact Specific Entity Types

This example redacts only the specified entity types, leaving the others untouched.

{
  "text": ["Meeting with Magda at 10 AM in Frankfurt."],
  "operator": "redact",
  "entity_types": ["NAME"]
}

Updating the Stack

Use update.sh to easily update the following parameters:

  • Container image URI
  • config.yaml
  • Autoscaler minimum and maximum container instance values

Just run update.sh and it'll prompt for updated parameters. If not entered, the previous values will be used. The script also encodes the config.yaml file in the directory and include it in the update. This isn't optional, to make sure that the deployed file always matches the local file. update.sh then triggers a rolling update to ensure service uptime.

./update.sh

[Optional] Cost Savings: Scale to Zero

PII Eraser supports a "Scale to Zero" model. This is ideal for batch processing workflows where you only pay for the infrastructure while processing data.

We provide a Python utility, manage_service.py, to programmatically control the stack. The script can set the desired number of instances and also pauses the autoscaler when stopped.

Option 1: Manual Control (CLI)

You can manually wake up or sleep the service from your terminal:

# Check current status
python3 manage_service.py status

# WAKE UP: Start 1 container and wait for it to be ready
python3 manage_service.py start --count 1

# SLEEP: Scale to 0 (Stop all billing)
python3 manage_service.py stop

Option 2: Automated Batch Processing (Python)

If you are running a Python script to process a batch of documents, you can import the service manager to automatically handle the infrastructure lifecycle. In particular, the service manager allows you to scale to a certain number of instances, allowing the autoscaler lag to be avoided.

from manage_service import PIIEraserService

# Initialize the manager
eraser = PIIEraserService(stack_name="pii-eraser-stack", region="eu-west-1")

try:
    # 1. Wake up: Scale to desired instance count
    print("🚀 Waking up PII Eraser...")
    eraser.scale(3)

    # 2. Run your batch processing
    process_sensitive_data()

finally:
    # 3. Sleep: Scale to 0 to stop billing
    print("💤 Scaling to zero...")
    eraser.scale(0)