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
..

nextcloud-mcp-deployer-role

IAM role and least-privilege policy scoped to deploy the nextcloud-mcp-server Terraform module.

Use cases

  • Client account: a client creates this role in their AWS account with trusted_principal_arns = ["arn:aws:iam::<your-account-id>:root"] so you can assume it cross-account and deploy/maintain the MCP server on their behalf.
  • Your own testing: instantiated in your account with your IAM user / admin role as the trusted principal, lets you run terraform apply for the nextcloud-mcp-server module under the same permission boundary the client will use — so any "works for me, breaks for them" gap surfaces in testing rather than at the client.

What the policy grants

Always granted (the server module always creates these resources):

  • ECS, ALB, EFS, Cloud Map (servicediscovery), Bedrock describe
  • ACM cert management (scoped action set, resources * since cert ARNs aren't known at policy-write time)
  • Route53 hosted-zone reads + private-zone CRUD (Cloud Map needs the latter); record-set mutation is scoped to caller-supplied zones via route53_zone_ids
  • IAM (scoped to role/ecs/${module_name_prefix}-*), CloudWatch Logs (scoped to /ecs/${module_name_prefix}*), Secrets Manager read (scoped to ${secret_name_prefix}*), EC2 SG + describe

Conditional via inputs:

Input Effect
route53_zone_ids = [...] scopes route53:ChangeResourceRecordSets to the listed zones (otherwise falls back to *)
allow_secret_create = true adds Secrets Manager create/update/delete (scoped to secret_name_prefix)

Cross-account assume from your account

Once the client has applied this module in their account and given you the output role_arn, configure the AWS provider in your client-deployment TF project:

provider "aws" {
  assume_role {
    role_arn = "arn:aws:iam::<client-account-id>:role/clients/nextcloud-mcp-deployer"
    # external_id = "..." # optional, recommended for cross-account
  }
}

Requirements

Name Version
terraform >= 1.9
aws ~> 6.0

Providers

Name Version
aws ~> 6.0

Modules

No modules.

Resources

Name Type
aws_iam_policy.deployer resource
aws_iam_role.this resource
aws_iam_role_policy_attachment.deployer resource
aws_caller_identity.current data source
aws_iam_policy_document.deployer data source
aws_iam_policy_document.trust data source
aws_partition.current data source

Inputs

Name Description Type Default Required
allow_secret_create When true, the deployer can create/update/delete Secrets Manager
secrets matching secret_name_prefix. Set true if the secret is
managed alongside the module in the same Terraform run; leave false if
the secret is provisioned out of band (console / separate root TF) and
only the ARN is passed in.
bool false no
module_name_prefix The var.name value passed to the nextcloud-mcp-server module. Used to
scope IAM/logs/secrets ARNs. Defaults match the module default; change
only if the module is instantiated with a non-default name.
string "nextcloud-mcp-server" no
role_name Name of the deployer IAM role. string "nextcloud-mcp-deployer" no
role_path IAM path for the deployer role and its policy. string "/clients/" no
route53_zone_ids Route53 public hosted zone IDs the deployer is allowed to mutate. The
server module always creates Route53 records (ALB alias + ACM DNS-01
validation), so this should be set to the zone(s) the module's
zone_id input points at. Leaving it empty falls back to * as a
convenience but is not recommended in production — scope it.
list(string) [] no
secret_name_prefix Secrets Manager name prefix the deployer can read (and optionally
create, see allow_secret_create). The module accepts a secret ARN as
input; this prefix scopes the deployer's access to secrets matching
that name pattern.
string "nextcloud-mcp" no
trusted_principal_arns Principal ARNs allowed to assume this role. For testing in your own
account: the user/role you want to assume from. For client deployments:
typically a single root-account ARN of the deploying party (e.g.
"arn:aws:iam:::root"), with MFA or external-id
conditions added at the trust-policy level if required.
list(string) n/a yes

Outputs

Name Description
policy_arn ARN of the inline-style managed policy attached to the role.
role_arn ARN of the deployer role; pass to STS AssumeRole or use as the assume_role target in a provider block.
role_name Name of the deployer role.