Implement reversible PII de-identification round trip (T1–T7)

Build the AWS side of the HNCB demo end to end (region ap-southeast-1):

- T1 /tokenize + T2 /restore: Lambdas behind a public API Gateway (shared-secret
  auth), Presidio detection, random per-request tokens, DynamoDB vault; overlap
  resolution so a ROC ID stays TW_ROC_ID.
- T3: Presidio made private (SG-locked to the tokenize Lambda in-VPC; DynamoDB
  gateway endpoint); only /tokenize + /restore are public.
- T4: RAG Lambda registered as an MCP tool on an AgentCore Gateway (AWS_IAM/SigV4);
  agentcore_setup.sh + a SigV4 MCP invoke test.
- T5: Strands agent deployed to AgentCore Runtime; SigV4 gateway auth, apac
  inference profile, pinned deps.
- T6: advisor UI on S3+CloudFront with a Fusion-less demo orchestrator (/demo)
  chaining tokenize -> runtime -> restore.
- T7: README runbook + trace check; teardown deletes gateway/runtime/memory/ECR.

Verified live: the cloud AgentCore/Bedrock trace shows only tokens, never the
real name. Secrets stay in gitignored local.auto.tfvars.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 17:10:58 +10:00
parent 6b2a051be3
commit 78d67a2469
21 changed files with 1439 additions and 107 deletions

46
terraform/.terraform.lock.hcl generated Normal file
View File

@@ -0,0 +1,46 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/archive" {
version = "2.8.0"
constraints = "~> 2.0"
hashes = [
"h1:WB6H5ksIZiyq1lQlD/PWeh+tn4FLsbSjVnRW3+4xe2Y=",
"zh:0d14713fdc259fb377d0b899ad3c650a34194bd52194c863303ef22a65a580e2",
"zh:369b56040c7a8085d04e7e8ffac1e2b321a3170e502f788819bc34b868ec016f",
"zh:4d1a3b983ed6af5a52bfe12794674ae55cbadfa6021b37106ade68b433ad216a",
"zh:5c547549e26e083573c78a966ca68ce6d7df6bb8f3948f66a575f07da46b74ea",
"zh:6de093e62a975eb19a5e3017ce38e6e3cb639c17b79648d2000e0a8348f0e997",
"zh:7267936c2cdbc448efeb594d73e6b56a53d6a7ae14fe88cdd2a4133adc3302f0",
"zh:7482f023050ed426b4b45116e1761643bc33b1fd4ce4a6fab207ae2571f35940",
"zh:76bbd93b234e5a2927d98b511d86565700f549b570871a194c35f944b96cefb7",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:c6afc4bc1f002bac9c173007dd4da05fde788cd14c2916089f958c33fedb0dfa",
"zh:d3ba40bd806a3a08e9237dece679193c99afb2085de6b45d7f5d1f673cfcd368",
"zh:e1ad7ded53ecd6f0e5b473a3b44eae2b2e885653a56050ab583d387332be02e4",
"zh:e93e78575ce82be6084cc153c24ba8f385dc8d6880888ee66e918460c870953d",
]
}
provider "registry.terraform.io/hashicorp/aws" {
version = "5.100.0"
constraints = "~> 5.0"
hashes = [
"h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=",
"zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644",
"zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2",
"zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274",
"zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b",
"zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862",
"zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93",
"zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2",
"zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e",
"zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421",
"zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4",
"zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9",
"zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9",
"zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70",
]
}

41
terraform/agentcore.tf Normal file
View File

@@ -0,0 +1,41 @@
# ---------------------------------------------------------------------------
# AgentCore Gateway support (task T4). Terraform's AgentCore coverage lags, so the
# Gateway + target themselves are created by scripts/agentcore_setup.sh via
# `aws bedrock-agentcore-control`. What IS declarative here is the IAM role the
# Gateway assumes to invoke the RAG tool Lambda -- kept in Terraform so it is
# tagged, auditable, and torn down with the rest of the stack.
# ---------------------------------------------------------------------------
resource "aws_iam_role" "agentcore_gateway" {
name = "${local.name}-gateway-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "bedrock-agentcore.amazonaws.com" }
Action = "sts:AssumeRole"
Condition = {
StringEquals = { "aws:SourceAccount" = data.aws_caller_identity.current.account_id }
}
}]
})
tags = local.cloud_tag # the Gateway is a cloud-zone component
}
resource "aws_iam_role_policy" "agentcore_gateway" {
name = "${local.name}-gateway-policy"
role = aws_iam_role.agentcore_gateway.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
# The Gateway only needs to invoke the one RAG tool Lambda.
Effect = "Allow"
Action = ["lambda:InvokeFunction"]
Resource = [aws_lambda_function.rag.arn]
}]
})
}
data "aws_caller_identity" "current" {}
output "gateway_role_arn" { value = aws_iam_role.agentcore_gateway.arn }

View File

@@ -1,9 +1,10 @@
# ---------------------------------------------------------------------------
# Hosting for the Presidio detector only. Fusion is a shared SaaS instance and is
# NOT deployed here. Because Fusion SaaS calls the detector over the internet, the
# analyzer port is public in this demo (lock it down / add auth for anything real;
# better: fold detection behind the /tokenize endpoint so only that is public --
# see CLAUDE.md task T3).
# NOT deployed here. The detector is PRIVATE (task T3): its port 5001 is reachable
# only from the /tokenize Lambda's security group, not the internet. The only
# public surface is the API Gateway (/tokenize + /restore). The task keeps a public
# IP purely so Fargate can pull the image from ECR (there's no NAT); inbound is
# still SG-locked, so nothing is reachable from outside the VPC.
#
# For a laptop-only rehearsal you can skip this file and run Presidio via docker.
# ---------------------------------------------------------------------------
@@ -17,21 +18,42 @@ data "aws_subnets" "default" {
}
}
# Route tables of the default VPC -- needed to attach the DynamoDB gateway endpoint
# so the (now VPC-attached) tokenize Lambda can still reach the vault/customers tables.
data "aws_route_tables" "default" {
vpc_id = data.aws_vpc.default.id
}
resource "aws_ecs_cluster" "this" {
name = "${local.name}-cluster"
tags = local.onprem_tag
}
# SG the tokenize Lambda runs in. No ingress; egress open so it can reach Presidio
# (5001) and the DynamoDB endpoint. Presidio trusts this SG (below).
resource "aws_security_group" "tokenize_lambda" {
name = "${local.name}-tokenize-lambda-sg"
description = "tokenize Lambda ENIs"
vpc_id = data.aws_vpc.default.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = local.onprem_tag
}
resource "aws_security_group" "svc" {
name = "${local.name}-svc-sg"
description = "Demo services SG"
vpc_id = data.aws_vpc.default.id
ingress {
description = "Presidio analyzer (public for SaaS Fusion demo - restrict for real use)"
from_port = 5001
to_port = 5001
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Presidio analyzer -- only the tokenize Lambda may call it"
from_port = 5001
to_port = 5001
protocol = "tcp"
security_groups = [aws_security_group.tokenize_lambda.id]
}
egress {
from_port = 0
@@ -42,6 +64,15 @@ resource "aws_security_group" "svc" {
tags = local.onprem_tag
}
# Gateway endpoint so VPC-attached Lambdas reach DynamoDB without a NAT / public route.
resource "aws_vpc_endpoint" "dynamodb" {
vpc_id = data.aws_vpc.default.id
service_name = "com.amazonaws.${var.region}.dynamodb"
vpc_endpoint_type = "Gateway"
route_table_ids = data.aws_route_tables.default.ids
tags = merge(local.onprem_tag, { Name = "${local.name}-dynamodb" })
}
resource "aws_iam_role" "ecs_exec" {
name = "${local.name}-ecs-exec"
assume_role_policy = jsonencode({
@@ -64,17 +95,37 @@ resource "aws_ecs_task_definition" "presidio" {
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 512
memory = 1024
execution_role_arn = aws_iam_role.ecs_exec.arn
# zh_core_web_lg (~600MB) plus presidio/spacy needs headroom; 1GB OOMs on load.
memory = 2048
execution_role_arn = aws_iam_role.ecs_exec.arn
# Built natively on Apple Silicon (podman) -> run on Fargate Graviton.
runtime_platform {
cpu_architecture = "ARM64"
operating_system_family = "LINUX"
}
container_definitions = jsonencode([{
name = "presidio"
image = var.presidio_image_uri
essential = true
portMappings = [{ containerPort = 5001 }]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.presidio.name
"awslogs-region" = var.region
"awslogs-stream-prefix" = "presidio"
}
}
}])
tags = local.onprem_tag
}
resource "aws_cloudwatch_log_group" "presidio" {
name = "/ecs/${local.name}-presidio"
retention_in_days = 7
tags = local.onprem_tag
}
resource "aws_ecs_service" "presidio" {
name = "${local.name}-presidio"
cluster = aws_ecs_cluster.this.id

296
terraform/gateway.tf Normal file
View File

@@ -0,0 +1,296 @@
# ---------------------------------------------------------------------------
# Public HTTPS endpoints that Fusion SaaS calls: POST /tokenize (T1) and
# POST /restore (T2), both on one API Gateway HTTP API. Fusion is shared SaaS, so
# it can only reach PUBLIC HTTPS with auth -- each handler checks a shared secret
# (tokenize_api_key, sent as x-api-key / bearer). These live in the "on-prem" zone
# (tags): they own the reversible token<->PII map, which must never leave it.
# (API Gateway, not a Lambda Function URL: this org's SCP blocks unauthenticated
# Function URLs.)
# ---------------------------------------------------------------------------
data "archive_file" "tokenize" {
type = "zip"
source_dir = "${path.module}/../gateway_api/tokenize"
output_path = "${path.module}/tokenize_lambda.zip"
}
resource "aws_iam_role" "tokenize" {
name = "${local.name}-tokenize-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy" "tokenize" {
name = "${local.name}-tokenize-policy"
role = aws_iam_role.tokenize.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
# Mint into the vault; scan customers for best-effort PERSON -> customer_id.
Effect = "Allow"
Action = ["dynamodb:PutItem"]
Resource = [aws_dynamodb_table.vault.arn]
},
{
Effect = "Allow"
Action = ["dynamodb:Scan"]
Resource = [aws_dynamodb_table.customers.arn]
},
{
Effect = "Allow"
Action = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
Resource = "arn:aws:logs:*:*:*"
}
]
})
}
resource "aws_lambda_function" "tokenize" {
function_name = "${local.name}-tokenize"
role = aws_iam_role.tokenize.arn
runtime = "python3.12"
handler = "handler.lambda_handler"
filename = data.archive_file.tokenize.output_path
source_code_hash = data.archive_file.tokenize.output_base64sha256
timeout = 15
environment {
variables = {
VAULT_TABLE = aws_dynamodb_table.vault.name
CUSTOMERS_TABLE = aws_dynamodb_table.customers.name
PRESIDIO_URL = var.presidio_url
TOKENIZE_API_KEY = var.tokenize_api_key
VAULT_TTL_SECONDS = "3600"
}
}
# In-VPC so it can reach the now-private Presidio over 5001. DynamoDB is reached
# via the gateway endpoint (see ecs.tf); no NAT needed.
vpc_config {
subnet_ids = data.aws_subnets.default.ids
security_group_ids = [aws_security_group.tokenize_lambda.id]
}
tags = local.onprem_tag
}
# VPC-attached Lambdas need ENI management permissions.
resource "aws_iam_role_policy_attachment" "tokenize_vpc" {
role = aws_iam_role.tokenize.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}
# Public HTTPS front door. We use an API Gateway HTTP API rather than a Lambda
# Function URL because this org's SCP blocks unauthenticated (auth_type=NONE)
# Function URLs. The API is public; auth is the shared secret checked in-handler
# (Fusion sends it as x-api-key / bearer). T2 adds a POST /restore route here.
resource "aws_apigatewayv2_api" "gateway" {
name = "${local.name}-gateway"
protocol_type = "HTTP"
# The static UI (CloudFront origin) calls /demo cross-origin -> allow CORS.
cors_configuration {
allow_origins = ["*"]
allow_methods = ["POST", "OPTIONS"]
allow_headers = ["content-type", "x-api-key"]
}
tags = local.onprem_tag
}
resource "aws_apigatewayv2_integration" "tokenize" {
api_id = aws_apigatewayv2_api.gateway.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.tokenize.invoke_arn
payload_format_version = "2.0"
}
resource "aws_apigatewayv2_route" "tokenize" {
api_id = aws_apigatewayv2_api.gateway.id
route_key = "POST /tokenize"
target = "integrations/${aws_apigatewayv2_integration.tokenize.id}"
}
resource "aws_apigatewayv2_stage" "default" {
api_id = aws_apigatewayv2_api.gateway.id
name = "$default"
auto_deploy = true
tags = local.onprem_tag
}
resource "aws_lambda_permission" "tokenize_apigw" {
statement_id = "AllowApiGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.tokenize.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.gateway.execution_arn}/*/*"
}
########################################
# T2: /restore -- egress re-identification (vault lookup by session_id)
########################################
data "archive_file" "restore" {
type = "zip"
source_dir = "${path.module}/../gateway_api/restore"
output_path = "${path.module}/restore_lambda.zip"
}
resource "aws_iam_role" "restore" {
name = "${local.name}-restore-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy" "restore" {
name = "${local.name}-restore-policy"
role = aws_iam_role.restore.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
# Read-only: look up this session's tokens to re-attach identity.
Effect = "Allow"
Action = ["dynamodb:Scan"]
Resource = [aws_dynamodb_table.vault.arn]
},
{
Effect = "Allow"
Action = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
Resource = "arn:aws:logs:*:*:*"
}
]
})
}
resource "aws_lambda_function" "restore" {
function_name = "${local.name}-restore"
role = aws_iam_role.restore.arn
runtime = "python3.12"
handler = "handler.lambda_handler"
filename = data.archive_file.restore.output_path
source_code_hash = data.archive_file.restore.output_base64sha256
timeout = 15
environment {
variables = {
VAULT_TABLE = aws_dynamodb_table.vault.name
TOKENIZE_API_KEY = var.tokenize_api_key
}
}
tags = local.onprem_tag
}
resource "aws_apigatewayv2_integration" "restore" {
api_id = aws_apigatewayv2_api.gateway.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.restore.invoke_arn
payload_format_version = "2.0"
}
resource "aws_apigatewayv2_route" "restore" {
api_id = aws_apigatewayv2_api.gateway.id
route_key = "POST /restore"
target = "integrations/${aws_apigatewayv2_integration.restore.id}"
}
resource "aws_lambda_permission" "restore_apigw" {
statement_id = "AllowApiGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.restore.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.gateway.execution_arn}/*/*"
}
########################################
# T6: /demo -- Fusion-less orchestrator the static UI calls (tokenize->agent->restore)
########################################
data "archive_file" "orchestrator" {
type = "zip"
source_dir = "${path.module}/../gateway_api/orchestrator"
output_path = "${path.module}/orchestrator_lambda.zip"
}
resource "aws_iam_role" "orchestrator" {
name = "${local.name}-orchestrator-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy" "orchestrator" {
name = "${local.name}-orchestrator-policy"
role = aws_iam_role.orchestrator.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
# Invoke the AgentCore Runtime. Scoped to the configured runtime ARN when set.
Effect = "Allow"
Action = ["bedrock-agentcore:InvokeAgentRuntime"]
Resource = var.agent_runtime_arn != "" ? [var.agent_runtime_arn, "${var.agent_runtime_arn}/*"] : ["*"]
},
{
Effect = "Allow"
Action = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
Resource = "arn:aws:logs:*:*:*"
}
]
})
}
resource "aws_lambda_function" "orchestrator" {
function_name = "${local.name}-orchestrator"
role = aws_iam_role.orchestrator.arn
runtime = "python3.12"
handler = "handler.lambda_handler"
filename = data.archive_file.orchestrator.output_path
source_code_hash = data.archive_file.orchestrator.output_base64sha256
timeout = 120
environment {
variables = {
REGION = var.region
TOKENIZE_URL = "${trimsuffix(aws_apigatewayv2_stage.default.invoke_url, "/")}/tokenize"
RESTORE_URL = "${trimsuffix(aws_apigatewayv2_stage.default.invoke_url, "/")}/restore"
TOKENIZE_API_KEY = var.tokenize_api_key
AGENT_RUNTIME_ARN = var.agent_runtime_arn
}
}
tags = local.onprem_tag
}
resource "aws_apigatewayv2_integration" "orchestrator" {
api_id = aws_apigatewayv2_api.gateway.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.orchestrator.invoke_arn
payload_format_version = "2.0"
}
resource "aws_apigatewayv2_route" "orchestrator" {
api_id = aws_apigatewayv2_api.gateway.id
route_key = "POST /demo"
target = "integrations/${aws_apigatewayv2_integration.orchestrator.id}"
}
resource "aws_lambda_permission" "orchestrator_apigw" {
statement_id = "AllowApiGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.orchestrator.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.gateway.execution_arn}/*/*"
}
output "tokenize_url" { value = "${trimsuffix(aws_apigatewayv2_stage.default.invoke_url, "/")}/tokenize" }
output "restore_url" { value = "${trimsuffix(aws_apigatewayv2_stage.default.invoke_url, "/")}/restore" }
output "demo_url" { value = "${trimsuffix(aws_apigatewayv2_stage.default.invoke_url, "/")}/demo" }

View File

@@ -8,6 +8,14 @@ terraform {
provider "aws" {
region = var.region
# Every taggable resource created by this stack carries this tag, on top of any
# per-resource tags (e.g. the on-prem/cloud Zone tags).
default_tags {
tags = {
Owner = "conan hncb demo"
}
}
}
locals {

85
terraform/ui.tf Normal file
View File

@@ -0,0 +1,85 @@
# ---------------------------------------------------------------------------
# T6: host the advisor UI on S3 + CloudFront. The bucket is private; CloudFront
# reaches it via Origin Access Control. index.html is static and committed; the
# live endpoint is injected via a generated config.js so we never bake an
# ephemeral URL into the repo.
# ---------------------------------------------------------------------------
resource "aws_s3_bucket" "ui" {
bucket_prefix = "${local.name}-ui-"
force_destroy = true
tags = local.onprem_tag
}
resource "aws_cloudfront_origin_access_control" "ui" {
name = "${local.name}-ui-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
resource "aws_cloudfront_distribution" "ui" {
enabled = true
default_root_object = "index.html"
comment = "${local.name} advisor UI"
origin {
domain_name = aws_s3_bucket.ui.bucket_regional_domain_name
origin_id = "ui-s3"
origin_access_control_id = aws_cloudfront_origin_access_control.ui.id
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "ui-s3"
viewer_protocol_policy = "redirect-to-https"
forwarded_values {
query_string = false
cookies { forward = "none" }
}
}
restrictions {
geo_restriction { restriction_type = "none" }
}
viewer_certificate {
cloudfront_default_certificate = true
}
tags = local.cloud_tag
}
resource "aws_s3_bucket_policy" "ui" {
bucket = aws_s3_bucket.ui.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "cloudfront.amazonaws.com" }
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.ui.arn}/*"
Condition = { StringEquals = { "AWS:SourceArn" = aws_cloudfront_distribution.ui.arn } }
}]
})
}
resource "aws_s3_object" "index" {
bucket = aws_s3_bucket.ui.id
key = "index.html"
source = "${path.module}/../ui/index.html"
etag = filemd5("${path.module}/../ui/index.html")
content_type = "text/html"
}
# Injected config: the demo orchestrator endpoint the UI calls.
resource "aws_s3_object" "config" {
bucket = aws_s3_bucket.ui.id
key = "config.js"
content = "window.DEMO_ENDPOINT = \"${trimsuffix(aws_apigatewayv2_stage.default.invoke_url, "/")}/demo\";\n"
content_type = "application/javascript"
etag = md5("${trimsuffix(aws_apigatewayv2_stage.default.invoke_url, "/")}/demo")
}
output "ui_url" { value = "https://${aws_cloudfront_distribution.ui.domain_name}" }

View File

@@ -23,3 +23,30 @@ variable "presidio_image_uri" {
type = string
default = "REPLACE_ME_PRESIDIO_IMAGE_URI"
}
# Where the /tokenize Lambda reaches the Presidio detector. After deploy this is
# the Presidio Fargate task's public endpoint (http://<public-ip>:5001). Kept a
# variable so a laptop rehearsal can point at a local/ngrok detector.
variable "presidio_url" {
description = "Base URL of the Presidio detector /analyze service."
type = string
default = "http://localhost:5001"
}
# Shared secret Fusion SaaS presents to the public endpoints (bearer / x-api-key).
# NEVER commit a real value -- pass via TF_VAR_tokenize_api_key or a .tfvars file
# that is gitignored. Empty default leaves the endpoint open (dev only).
variable "tokenize_api_key" {
description = "Shared secret required on /tokenize (and /restore) requests."
type = string
default = ""
sensitive = true
}
# AgentCore Runtime ARN (created out-of-band by `agentcore launch`, see T5). The
# demo orchestrator (T6) invokes it. Empty -> orchestrator skips the agent step.
variable "agent_runtime_arn" {
description = "Bedrock AgentCore Runtime ARN the demo orchestrator invokes."
type = string
default = ""
}