From 48e099040ca1189b4014e46d933a7b28e6a84b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=84=9D=28Junseok=20Oh=29DevOps?= <52226147+Atom-oh@users.noreply.github.com> Date: Mon, 19 Oct 2026 18:35:11 +0000 Subject: [PATCH 1/2] feat(terraform): add default-off Jaeger and Dynatrace VPC attachment flags jaeger_vpc_enabled and dynatrace_vpc_enabled (default false, nullable = false) attach the Jaeger and Dynatrace connector Lambdas to the VPC for private endpoints, with the same agentcore_enabled && integrations_enabled prerequisite as the existing per-kind flags. No IAM statement changes; the ENI policy count only widens. tftest pins defaults-dark and selective attachment. Co-Authored-By: Claude Fable 5.1 --- terraform/foundation/ai.tf | 30 +++++++++++++--- .../foundation/tests/runtime_iam.tftest.hcl | 34 +++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/terraform/foundation/ai.tf b/terraform/foundation/ai.tf index 1908b8537..d3f94176d 100644 --- a/terraform/foundation/ai.tf +++ b/terraform/foundation/ai.tf @@ -58,6 +58,28 @@ variable "mimir_vpc_enabled" { default = false } +variable "jaeger_vpc_enabled" { + type = bool + default = false + nullable = false + description = "Attach the Jaeger query Lambda to private subnets. Requires agentcore_enabled and integrations_enabled; routing, DNS and target ingress must be operator-configured." + validation { + condition = !var.jaeger_vpc_enabled || (var.agentcore_enabled && var.integrations_enabled) + error_message = "jaeger_vpc_enabled requires agentcore_enabled and integrations_enabled." + } +} + +variable "dynatrace_vpc_enabled" { + type = bool + default = false + nullable = false + description = "Attach the Dynatrace environment API Lambda to private subnets for a private ActiveGate endpoint. Requires agentcore_enabled and integrations_enabled. Does not affect the official hosted MCP preset." + validation { + condition = !var.dynatrace_vpc_enabled || (var.agentcore_enabled && var.integrations_enabled) + error_message = "dynatrace_vpc_enabled requires agentcore_enabled and integrations_enabled." + } +} + variable "istio_vpc_enabled" { type = bool description = "Attach the istio-read Lambda to the private subnets so it can reach a PRIVATE-ONLY EKS API endpoint. Requires agentcore_enabled. Default false → no-op ($0); off = non-VPC (reaches a public/public+private cluster endpoint). PERSIST in live terraform.tfvars." @@ -509,10 +531,10 @@ resource "aws_iam_role_policy" "agent_lambda_opensearch" { }) } -# ENI perms for the opensearch-mcp Lambda ONLY when it is VPC-attached (opensearch_vpc_enabled). +# ENI permissions exist only while a managed Lambda VPC option is enabled. # Compound-gated: references agent_lambda[0], which exists only when agentcore_enabled → guard both. resource "aws_iam_role_policy" "agent_lambda_vpc_eni" { - count = var.agentcore_enabled && (var.opensearch_vpc_enabled || var.clickhouse_vpc_enabled || var.prometheus_vpc_enabled || var.loki_vpc_enabled || var.tempo_vpc_enabled || var.mimir_vpc_enabled || var.istio_vpc_enabled) ? 1 : 0 + count = var.agentcore_enabled && (var.opensearch_vpc_enabled || var.clickhouse_vpc_enabled || var.prometheus_vpc_enabled || var.loki_vpc_enabled || var.tempo_vpc_enabled || var.mimir_vpc_enabled || var.jaeger_vpc_enabled || var.dynatrace_vpc_enabled || var.istio_vpc_enabled) ? 1 : 0 name = "${var.project}-agent-lambda-vpc-eni" role = aws_iam_role.agent_lambda[0].id policy = jsonencode({ @@ -967,10 +989,10 @@ resource "aws_lambda_function" "agent" { } # Per-Lambda VPC opt-in: attach a connector to the private subnets ONLY when its _vpc_enabled - # flag is set (opensearch/clickhouse/prometheus/loki/tempo/mimir for VPC-only datasources; + # flag is set (opensearch/clickhouse/prometheus/loki/tempo/mimir/jaeger/dynatrace for private datasources; # istio-read for a private-only EKS API endpoint). Off (default) → no vpc_config → non-VPC. dynamic "vpc_config" { - for_each = ((each.key == "opensearch-mcp" && var.opensearch_vpc_enabled) || (each.key == "clickhouse-mcp" && var.clickhouse_vpc_enabled) || (each.key == "prometheus-mcp" && var.prometheus_vpc_enabled) || (each.key == "loki-mcp" && var.loki_vpc_enabled) || (each.key == "tempo-mcp" && var.tempo_vpc_enabled) || (each.key == "mimir-mcp" && var.mimir_vpc_enabled) || (each.key == "istio-read" && var.istio_vpc_enabled)) ? [1] : [] + for_each = ((each.key == "opensearch-mcp" && var.opensearch_vpc_enabled) || (each.key == "clickhouse-mcp" && var.clickhouse_vpc_enabled) || (each.key == "prometheus-mcp" && var.prometheus_vpc_enabled) || (each.key == "loki-mcp" && var.loki_vpc_enabled) || (each.key == "tempo-mcp" && var.tempo_vpc_enabled) || (each.key == "mimir-mcp" && var.mimir_vpc_enabled) || (each.key == "jaeger-mcp" && var.jaeger_vpc_enabled) || (each.key == "dynatrace-mcp" && var.dynatrace_vpc_enabled) || (each.key == "istio-read" && var.istio_vpc_enabled)) ? [1] : [] content { subnet_ids = local.private_subnet_ids security_group_ids = [aws_security_group.service.id] diff --git a/terraform/foundation/tests/runtime_iam.tftest.hcl b/terraform/foundation/tests/runtime_iam.tftest.hcl index 89d6afd56..13e18afaf 100644 --- a/terraform/foundation/tests/runtime_iam.tftest.hcl +++ b/terraform/foundation/tests/runtime_iam.tftest.hcl @@ -80,6 +80,40 @@ run "defaults_remain_dark" { condition = (length(aws_iam_role_policy.agentcore) == 0 && length(aws_iam_role_policy.official_mcp_credentials) == 0 && length(aws_iam_role_policy.steampipe_task) == 0 && length(aws_iam_role_policy.worker_lambda) == 0 && !var.inventory_host_only && var.steampipe_image_digest == null && var.worker_image_digest == null && !var.remediation_enabled && !var.diagnosis_notify_enabled && !var.integrations_write_enabled) error_message = "Core runtime and host/image overrides must remain opt-in." } + assert { + condition = !var.jaeger_vpc_enabled && !var.dynatrace_vpc_enabled && length(aws_iam_role_policy.agent_lambda_vpc_eni) == 0 + error_message = "Private external datasource networking must remain default-off." + } +} + +run "private_datasource_flags_require_applied_prerequisites" { + command = plan + variables { + jaeger_vpc_enabled = true + dynatrace_vpc_enabled = true + } + expect_failures = [var.jaeger_vpc_enabled, var.dynatrace_vpc_enabled] +} + +run "private_jaeger_and_dynatrace_attach_only_the_selected_lambdas" { + command = plan + variables { + agentcore_enabled = true + integrations_enabled = true + jaeger_vpc_enabled = true + dynatrace_vpc_enabled = true + } + assert { + condition = ( + aws_lambda_function.agent["jaeger-mcp"].vpc_config[0].subnet_ids == toset(var.existing_private_subnet_ids) && + aws_lambda_function.agent["dynatrace-mcp"].vpc_config[0].subnet_ids == toset(var.existing_private_subnet_ids) && + length(aws_lambda_function.agent["datadog-mcp"].vpc_config) == 0 && + length(aws_lambda_function.agent["prometheus-mcp"].vpc_config) == 0 && + length(aws_iam_role_policy.agent_lambda_vpc_eni) == 1 && + !var.official_mcp_enabled && !var.remediation_enabled && !var.integrations_write_enabled + ) + error_message = "Private datasource attachment must be selective and must not activate MCP/write gates." + } } run "explicit_inventory_targets_pin_metadata_permissions_and_actual_principal" { From 1a82c02988f012e15447c3c342de70740ae39cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=84=9D=28Junseok=20Oh=29DevOps?= <52226147+Atom-oh@users.noreply.github.com> Date: Tue, 20 Oct 2026 03:36:31 +0000 Subject: [PATCH 2/2] fix: address review feedback (iteration 1/5) --- terraform/foundation/ai.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/foundation/ai.tf b/terraform/foundation/ai.tf index d3f94176d..77c928a4d 100644 --- a/terraform/foundation/ai.tf +++ b/terraform/foundation/ai.tf @@ -62,7 +62,7 @@ variable "jaeger_vpc_enabled" { type = bool default = false nullable = false - description = "Attach the Jaeger query Lambda to private subnets. Requires agentcore_enabled and integrations_enabled; routing, DNS and target ingress must be operator-configured." + description = "Attach the Jaeger query Lambda to private subnets. Requires agentcore_enabled and integrations_enabled; routing, DNS and target ingress must be operator-configured. Default false → no-op ($0). PERSIST in live terraform.tfvars." validation { condition = !var.jaeger_vpc_enabled || (var.agentcore_enabled && var.integrations_enabled) error_message = "jaeger_vpc_enabled requires agentcore_enabled and integrations_enabled." @@ -73,7 +73,7 @@ variable "dynatrace_vpc_enabled" { type = bool default = false nullable = false - description = "Attach the Dynatrace environment API Lambda to private subnets for a private ActiveGate endpoint. Requires agentcore_enabled and integrations_enabled. Does not affect the official hosted MCP preset." + description = "Attach the Dynatrace environment API Lambda to private subnets for a private ActiveGate endpoint. Requires agentcore_enabled and integrations_enabled; routing, DNS and egress (including to hosted SaaS environments) must be operator-configured. Does not affect the official hosted MCP preset. Default false → no-op ($0). PERSIST in live terraform.tfvars." validation { condition = !var.dynatrace_vpc_enabled || (var.agentcore_enabled && var.integrations_enabled) error_message = "dynatrace_vpc_enabled requires agentcore_enabled and integrations_enabled."