terraform { required_version = ">= 1.9, < 1.10" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } variable "consumer_repo" { description = "The consumer repository (org/repo) — for the nova:contract tag." type = string default = "acdl/consumer-a" } variable "owner_id" { description = "The owning team (for the nova:owner ABAC tag)." type = string default = "team-a" } variable "account_id" { description = "The consumer's AWS account ID (where the deploy role is created)." type = string default = "000000000000" } variable "region" { description = "AWS region." type = string default = "us-east-1" } provider "aws" { region = var.region } # P20 (REQ-184): consumer deploy role — the role the consumer's CI runner # assumes to invoke the platform Lambda + deploy via the reusable workflow. # The trust policy allows the consumer's CI runner (GitHub Actions / # Gitea act_runner) to assume this role. In a real deployment, the trust # policy is scoped to the consumer's OIDC provider; for offline-proven # mode, a placeholder trust is used. resource "aws_iam_role" "consumer_deploy" { name = "nova-${replace(var.consumer_repo, "/", "-")}-deploy" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { # Placeholder: in a real deployment, this is the consumer's # OIDC provider ARN. Offline-proven mode uses a wildcard. Federated = "arn:aws:iam::${var.account_id}:oidc-provider/token.actions.githubusercontent.com" } Action = "sts:AssumeRoleWithWebIdentity" Condition = { StringEquals = { "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com" } StringLike = { "token.actions.githubusercontent.com:sub" = "repo:${var.consumer_repo}:*" } } } ] }) tags = { "nova:owner" = var.owner_id "nova:contract" = var.consumer_repo "nova:environment" = "dev" } } # P20 (REQ-184): inline policy granting the consumer's deploy role the # right to invoke the platform Lambda's Function URL, scoped via ABAC # (aws:PrincipalTag/nova:owner == var.owner_id). The platform Lambda's # resource-based policy + the consumer_invoke_policy.json template # enforce the ABAC scope at the Lambda side; this policy grants the # invoke permission on the consumer side. resource "aws_iam_role_policy" "consumer_invoke" { name = "nova-consumer-invoke" role = aws_iam_role.consumer_deploy.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "lambda:InvokeFunctionUrl", ] Resource = [ # The platform Lambda ARN (cross-account). The account_id is # the platform account, not the consumer account. For offline- # proven mode, a placeholder ARN is used. "arn:aws:lambda:${var.region}:000000000000:function:nova-contract-ingestor" ] Condition = { StringEquals = { "aws:PrincipalTag/nova:owner" = var.owner_id } } } ] }) } output "consumer_deploy_role_arn" { description = "The ARN of the consumer deploy role." value = aws_iam_role.consumer_deploy.arn } output "consumer_deploy_role_name" { description = "The name of the consumer deploy role." value = aws_iam_role.consumer_deploy.name }