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>
6.3 KiB
6.3 KiB
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 applyfor 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 ismanaged 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 toscope 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 aconvenience 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 asinput; 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. |