Files
Chris CoutinhoandClaude Opus 4.7 e4c552cd19 fix(infra): address PR review feedback on tf modules
Deployer role:
- Add servicediscovery actions; module always creates Cloud Map namespace
  and service so the policy must grant CreatePrivateDnsNamespace etc.
- Make Route53 + ACM permissions unconditional. The server module always
  issues an ACM cert and writes Route53 records (no CloudFront default-cert
  path exists), so gating these on route53_zone_ids was broken. Split
  Route53 into hosted-zone management (always) plus record-set mutation
  (scoped to caller-supplied zones, falls back to *).
- Remove unused cloudfront:* statement; no CloudFront resources in module.
- Replace acm:* wildcard with explicit cert-management action set.

Server module:
- qdrant_image_tag is now nullable with default null and validated against
  use_external_qdrant, so external-qdrant callers can omit it instead of
  passing a sentinel "unused" value.
- task_role_arn and efs_id outputs marked sensitive; qdrant_dns_name returns
  null when use_external_qdrant = true.
- ALB SG now has matching IPv6 egress rule (was v4-only).
- nextcloud_url validates the https:// scheme.
- random_pet.subdomain keeper includes zone_name so a zone migration that
  preserves zone_id still triggers regeneration.
- Pin required_version >= 1.9 on both modules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:24:09 +02:00

16 KiB

Requirements

Name Version
terraform >= 1.9
aws ~> 6.0
random ~> 3.6

Providers

Name Version
aws ~> 6.0
random ~> 3.6

Modules

No modules.

Resources

Name Type
aws_acm_certificate.this resource
aws_acm_certificate_validation.this resource
aws_cloudwatch_log_group.qdrant resource
aws_cloudwatch_log_group.this resource
aws_ecs_cluster.this resource
aws_ecs_service.qdrant resource
aws_ecs_service.this resource
aws_ecs_task_definition.qdrant resource
aws_ecs_task_definition.this resource
aws_efs_access_point.data resource
aws_efs_access_point.oauth resource
aws_efs_access_point.qdrant resource
aws_efs_file_system.this resource
aws_efs_mount_target.this resource
aws_iam_role.execution resource
aws_iam_role.qdrant_task resource
aws_iam_role.task resource
aws_iam_role_policy.execution_secrets resource
aws_iam_role_policy.qdrant_task_efs resource
aws_iam_role_policy.qdrant_task_exec resource
aws_iam_role_policy.task_bedrock resource
aws_iam_role_policy.task_efs resource
aws_iam_role_policy.task_exec resource
aws_iam_role_policy_attachment.execution_managed resource
aws_lb.this resource
aws_lb_listener.http_redirect resource
aws_lb_listener.https resource
aws_lb_target_group.this resource
aws_route53_record.alias resource
aws_route53_record.cert_validation resource
aws_security_group.alb resource
aws_security_group.efs resource
aws_security_group.qdrant resource
aws_security_group.task resource
aws_service_discovery_private_dns_namespace.this resource
aws_service_discovery_service.qdrant resource
aws_vpc_security_group_egress_rule.alb_all_v4 resource
aws_vpc_security_group_egress_rule.alb_all_v6 resource
aws_vpc_security_group_egress_rule.qdrant_all_v4 resource
aws_vpc_security_group_egress_rule.qdrant_all_v6 resource
aws_vpc_security_group_egress_rule.task_all_v4 resource
aws_vpc_security_group_egress_rule.task_all_v6 resource
aws_vpc_security_group_ingress_rule.alb_http_v4 resource
aws_vpc_security_group_ingress_rule.alb_http_v6 resource
aws_vpc_security_group_ingress_rule.alb_https_v4 resource
aws_vpc_security_group_ingress_rule.alb_https_v6 resource
aws_vpc_security_group_ingress_rule.efs_from_qdrant resource
aws_vpc_security_group_ingress_rule.efs_from_task resource
aws_vpc_security_group_ingress_rule.qdrant_from_task resource
aws_vpc_security_group_ingress_rule.task_from_alb resource
random_pet.subdomain resource
aws_caller_identity.current data source
aws_iam_policy_document.ecs_tasks_trust data source
aws_iam_policy_document.execution_secrets data source
aws_iam_policy_document.qdrant_task_efs data source
aws_iam_policy_document.task_bedrock data source
aws_iam_policy_document.task_efs data source
aws_iam_policy_document.task_exec data source
aws_region.current data source

Inputs

Name Description Type Default Required
allowed_mcp_clients MCP OAuth client allowlist published as ALLOWED_MCP_CLIENTS. Each entry is id or `id redirect_uri`. Empty list keeps the upstream defaults (claude-desktop, test-mcp-client). list(string) []
allowed_mgmt_client Management API client allowlist published as ALLOWED_MGMT_CLIENT (comma-separated client IDs). Required from upstream v0.74.0+: when unset/empty the management API is fail-closed and rejects all tokens. Empty string skips publishing the env var. string "" no
bedrock_embedding_model Bedrock model ID used for semantic search embeddings string "amazon.titan-embed-text-v2:0" no
container_port Port the server listens on inside the container number 8004 no
cpu Fargate task vCPU units (1024 = 1 vCPU) number 512 no
image Container image (without tag) string "ghcr.io/cbcoutinho/nextcloud-mcp-server" no
image_tag Container image tag. Pin to a specific release; avoid :latest. string n/a yes
log_retention_days CloudWatch log retention in days number 30 no
memory Fargate task memory (MiB) number 1024 no
name Logical name prefix for resources string "nextcloud-mcp-server" no
nextcloud_url Public URL of the Nextcloud instance the MCP server pairs with (e.g., https://cloud.example.com). Used to advertise the OIDC discovery endpoint via /api/v1/status so the astrolabe Nextcloud app can discover Nextcloud's oidc_provider as the IdP instead of falling back to http://localhost. string n/a yes
private_subnet_ids Private subnet IDs (for EFS mount targets only). list(string) n/a yes
public_subnet_ids Public subnet IDs (for the ALB and the ECS task ENI). Tasks run with assign_public_ip=true since this VPC has no NAT gateway; the task SG only allows ingress from the ALB SG. list(string) n/a yes
qdrant_collection Qdrant collection name. Set to a stable value (anything other than upstream's default 'nextcloud_content') so the upstream config doesn't fall through to its hostname-based auto-naming, which churns the collection on every rolling deploy. string "nextcloud-mcp" no
qdrant_cpu Qdrant Fargate task vCPU units (1024 = 1 vCPU) number 512 no
qdrant_image Qdrant container image (without tag) string "qdrant/qdrant" no
qdrant_image_tag Qdrant container image tag (e.g., v1.15.0). Pin to a specific release; avoid :latest. Required only when use_external_qdrant = false; omit (or pass null) when use_external_qdrant = true. string null no
qdrant_memory Qdrant Fargate task memory (MiB) number 1024 no
secret_arn ARN of the Secrets Manager secret holding JSON {host, client_id, client_secret, token_encryption_key, webhook_secret} string n/a yes
use_external_qdrant When true, skip the in-AWS Qdrant ECS task and source QDRANT_URL/QDRANT_API_KEY from the Secrets Manager secret (keys: qdrant_url, qdrant_api_key). When false, run an in-AWS Qdrant Fargate task and point the MCP server at it via Cloud Map DNS. bool false no
vector_sync_processor_workers Concurrent embedding workers. Keep at 1 unless you've verified Bedrock quota headroom. number 1 no
vector_sync_scan_interval Seconds between background vector sync scans number 60 no
vpc_id VPC ID to deploy into string n/a yes
zone_id Route53 hosted zone ID for the random subdomain string n/a yes
zone_name Route53 hosted zone name (without trailing dot), e.g. astrolabeonline.com string n/a yes

Outputs

Name Description
alb_dns_name n/a
ecs_cluster_name n/a
ecs_service_name n/a
efs_id EFS file-system ID. Marked sensitive — surfacing it in CI logs invites enumeration of mount targets.
fqdn Fully-qualified domain name
log_group_name n/a
qdrant_dns_name Internal DNS name where mcp-server reaches qdrant (null when use_external_qdrant = true).
qdrant_service_name Qdrant ECS service name (null when use_external_qdrant = true)
subdomain Generated random subdomain (label only, without the zone)
task_role_arn Task role ARN. Marked sensitive — knowing the ARN is the first step to abusing it via SSRF/role-confusion.
url Public HTTPS URL of the MCP server