From 4e2f5cf5187a8d81f548b7f8817e60dbfe7849c8 Mon Sep 17 00:00:00 2001 From: Agustin Celentano <12614595+agustincelentano@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:11:16 -0300 Subject: [PATCH 1/3] feat(iam)!: extract build workflow user into build-user module, add s3-assets Extract the shared build workflow IAM identity (user, access key, group) out of the ecr module into a new infrastructure/aws/iam/build-user module, and add a new infrastructure/aws/iam/s3-assets module that grants S3 publish permissions (s3:PutObject/GetObject on the assets bucket) to the build workflow group. The ecr module now only manages ECR permissions and attaches its policy to the group received via the new build_workflow_group_name input. BREAKING CHANGE: infrastructure/aws/iam/ecr no longer creates the build workflow user, access key or group, and no longer outputs build_workflow_access_key_id / build_workflow_access_key_secret. Consumers must instantiate the new build-user module, pass its group_name to ecr (new required input build_workflow_group_name) and to s3-assets, take the build credentials from build-user outputs, and run a tofu state mv to preserve the existing user and access key (see infrastructure/aws/iam/build-user/README.md). The IAM group is renamed from ecr-managers to asset-publishers (recreated; does not rotate the user's keys). --- infrastructure/aws/iam/build-user/README.md | 101 ++++++++++++++++++ infrastructure/aws/iam/build-user/main.tf | 16 +++ infrastructure/aws/iam/build-user/outputs.tf | 15 +++ .../aws/iam/build-user/variables.tf | 4 + infrastructure/aws/iam/ecr/README.md | 46 ++++---- infrastructure/aws/iam/ecr/main.tf | 19 +--- infrastructure/aws/iam/ecr/outputs.tf | 11 -- infrastructure/aws/iam/ecr/variables.tf | 5 + infrastructure/aws/iam/s3-assets/README.md | 62 +++++++++++ infrastructure/aws/iam/s3-assets/main.tf | 22 ++++ infrastructure/aws/iam/s3-assets/variables.tf | 14 +++ 11 files changed, 266 insertions(+), 49 deletions(-) create mode 100644 infrastructure/aws/iam/build-user/README.md create mode 100644 infrastructure/aws/iam/build-user/main.tf create mode 100644 infrastructure/aws/iam/build-user/outputs.tf create mode 100644 infrastructure/aws/iam/build-user/variables.tf create mode 100644 infrastructure/aws/iam/s3-assets/README.md create mode 100644 infrastructure/aws/iam/s3-assets/main.tf create mode 100644 infrastructure/aws/iam/s3-assets/variables.tf diff --git a/infrastructure/aws/iam/build-user/README.md b/infrastructure/aws/iam/build-user/README.md new file mode 100644 index 00000000..28889c02 --- /dev/null +++ b/infrastructure/aws/iam/build-user/README.md @@ -0,0 +1,101 @@ +# Module: build-user + +## Description + +Provisions the shared CI/CD build workflow IAM identity (user, access key, and group) used to publish application assets to any asset repository (ECR, S3, etc.) + +## Architecture + +The module creates a single `aws_iam_user` with an `aws_iam_access_key` for CI/CD build workflows, plus an `aws_iam_group` named `asset-publishers` and the `aws_iam_user_group_membership` that adds the user to it. The group is the attachment point for per-destination permission modules: `infrastructure/aws/iam/ecr` attaches its ECR policy to this group, and `infrastructure/aws/iam/s3-assets` attaches its S3 policy to the same group. The build user therefore accumulates the permissions of every enabled destination through a single group, which matches how the platform CLI publishes assets (one credential set used for all asset types). + +## Features + +- Creates a single namespaced `aws_iam_user` and `aws_iam_access_key` for CI/CD build workflow authentication +- Creates a destination-agnostic `aws_iam_group` (`asset-publishers`) that permission modules attach their policies to +- Adds the build user to the group via `aws_iam_user_group_membership` +- Exposes `group_name` so asset-repository modules (`ecr`, `s3-assets`) can grant permissions without recreating the identity + +## Basic Usage + +```hcl +module "build_user" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/build-user?ref=v5.0.0" + + cluster_name = "your-cluster-name" +} +``` + +The `group_name` output is consumed by the asset-repository permission modules: + +```hcl +module "ecr" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/ecr?ref=v5.0.0" + + cluster_name = "your-cluster-name" + build_workflow_group_name = module.build_user.group_name +} + +module "s3_assets" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/s3-assets?ref=v5.0.0" + + cluster_name = "your-cluster-name" + build_workflow_group_name = module.build_user.group_name + assets_bucket = "your-assets-bucket" +} +``` + +## Migration from < v5.0.0 (build user previously created by `iam/ecr`) + +Before v5.0.0 the build workflow user, its access key and the group lived inside the `iam/ecr` +module. This module extracts them. To migrate **without rotating the access keys** (which would +break CI), move the user and its access key in state — the group is renamed +(`ecr-managers` → `asset-publishers`) and is recreated, which does not affect the user's credentials: + +```bash +tofu state mv 'module.ecr.aws_iam_user.nullplatform_build_workflow_user' \ + 'module.build_user.aws_iam_user.nullplatform_build_workflow_user' + +tofu state mv 'module.ecr.aws_iam_access_key.nullplatform_build_workflow_user_key' \ + 'module.build_user.aws_iam_access_key.nullplatform_build_workflow_user_key' +``` + +After the moves, a `tofu plan` should show **no changes** to the user and access key (only their +state address moved), the group + membership recreated as `asset-publishers`, and the ECR policy +re-attached to the new group. + +> **Security note:** the build credentials are read by the platform on each CI run (they are not +> stored as per-repository secrets), so rotating them periodically is a good practice and this +> module makes it easy — regenerate the access key and let the platform re-read the new value. + + + + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_access_key.nullplatform_build_workflow_user_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource | +| [aws_iam_group.asset_publishers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group) | resource | +| [aws_iam_user.nullplatform_build_workflow_user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource | +| [aws_iam_user_group_membership.asset_publishers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_group_membership) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cluster\_name](#input\_cluster\_name) | Name of the cluster, used to namespace IAM resource names | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [build\_workflow\_access\_key\_id](#output\_build\_workflow\_access\_key\_id) | Access key ID for the CI/CD build workflow IAM user | +| [build\_workflow\_access\_key\_secret](#output\_build\_workflow\_access\_key\_secret) | Secret access key for the CI/CD build workflow IAM user | +| [group\_name](#output\_group\_name) | Name of the IAM group that asset-repository permission modules (ecr, s3-assets) attach their policies to. The build workflow user is a member of this group. | + diff --git a/infrastructure/aws/iam/build-user/main.tf b/infrastructure/aws/iam/build-user/main.tf new file mode 100644 index 00000000..b5f834fb --- /dev/null +++ b/infrastructure/aws/iam/build-user/main.tf @@ -0,0 +1,16 @@ +resource "aws_iam_user" "nullplatform_build_workflow_user" { + name = "nullplatform-${var.cluster_name}-build-workflow-user" +} + +resource "aws_iam_access_key" "nullplatform_build_workflow_user_key" { + user = aws_iam_user.nullplatform_build_workflow_user.name +} + +resource "aws_iam_group" "asset_publishers" { + name = "nullplatform-${var.cluster_name}-asset-publishers" +} + +resource "aws_iam_user_group_membership" "asset_publishers" { + user = aws_iam_user.nullplatform_build_workflow_user.name + groups = [aws_iam_group.asset_publishers.name] +} diff --git a/infrastructure/aws/iam/build-user/outputs.tf b/infrastructure/aws/iam/build-user/outputs.tf new file mode 100644 index 00000000..cf4dacf1 --- /dev/null +++ b/infrastructure/aws/iam/build-user/outputs.tf @@ -0,0 +1,15 @@ +output "build_workflow_access_key_id" { + description = "Access key ID for the CI/CD build workflow IAM user" + value = aws_iam_access_key.nullplatform_build_workflow_user_key.id +} + +output "build_workflow_access_key_secret" { + description = "Secret access key for the CI/CD build workflow IAM user" + value = aws_iam_access_key.nullplatform_build_workflow_user_key.secret + sensitive = true +} + +output "group_name" { + description = "Name of the IAM group that asset-repository permission modules (ecr, s3-assets) attach their policies to. The build workflow user is a member of this group." + value = aws_iam_group.asset_publishers.name +} diff --git a/infrastructure/aws/iam/build-user/variables.tf b/infrastructure/aws/iam/build-user/variables.tf new file mode 100644 index 00000000..c16e2ed1 --- /dev/null +++ b/infrastructure/aws/iam/build-user/variables.tf @@ -0,0 +1,4 @@ +variable "cluster_name" { + description = "Name of the cluster, used to namespace IAM resource names" + type = string +} diff --git a/infrastructure/aws/iam/ecr/README.md b/infrastructure/aws/iam/ecr/README.md index 2eefcf61..6e7ef625 100644 --- a/infrastructure/aws/iam/ecr/README.md +++ b/infrastructure/aws/iam/ecr/README.md @@ -2,31 +2,41 @@ ## Description -Provisions IAM resources for ECR image management and optional cross-account ECR pull access within a named cluster namespace +Provisions IAM resources for ECR image management and optional cross-account ECR pull access within a named cluster namespace. The build workflow identity (user, access key, group) lives in the `build-user` module; this module only grants ECR permissions to that group. ## Architecture -The module creates two aws_iam_role resources (an application role with a configurable assume-role principal and an optional cross-account pull role), an aws_iam_policy for ECR management actions, and an aws_iam_user with an aws_iam_access_key for CI/CD build workflows. The ECR manager policy is attached to both the application role via aws_iam_role_policy_attachment and to an aws_iam_group via aws_iam_group_policy_attachment, with the build workflow user added to that group through aws_iam_user_group_membership. When enable_cross_account_pull is true, a separate aws_iam_role and aws_iam_policy scoped to read-only ECR actions are created and linked, with pull_account_ids driving the Principal trust statements. +The module creates an aws_iam_role (application role with a configurable assume-role principal) and an aws_iam_policy for ECR management actions. The ECR manager policy is attached to the application role via aws_iam_role_policy_attachment and to the shared build-workflow group via aws_iam_group_policy_attachment. The group itself is created by the `build-user` module and passed in through `build_workflow_group_name`. When enable_cross_account_pull is true, a separate aws_iam_role and aws_iam_policy scoped to read-only ECR actions are created and linked, with pull_account_ids driving the Principal trust statements. ## Features - Creates a namespaced aws_iam_role for application image pulling with a configurable assume-role principal - Creates an aws_iam_policy granting full ECR repository lifecycle permissions including push, pull, and repository management -- Creates an aws_iam_user and aws_iam_access_key for CI/CD build workflow authentication to ECR -- Creates an aws_iam_group and attaches the ECR manager policy for group-based permission management +- Attaches the ECR manager policy to the shared build-workflow group (created by the build-user module) for group-based permission management - Optionally creates a cross-account aws_iam_role and read-only ECR pull policy for external AWS accounts - Outputs a ready-to-use ECR repository policy JSON for cross-account pull access configuration ## Basic Usage ```hcl -module "ecr" { - source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/ecr?ref=v4.5.2" +module "build_user" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/build-user?ref=v5.0.0" cluster_name = "your-cluster-name" } + +module "ecr" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/ecr?ref=v5.0.0" + + cluster_name = "your-cluster-name" + build_workflow_group_name = module.build_user.group_name +} ``` +> **Migration from < v5.0.0:** the build workflow user, access key and group were previously +> created by this module. They now live in `build-user`. See that module's README for the +> `tofu state mv` steps to migrate without rotating the access keys. + ## Using Outputs ```hcl @@ -50,14 +60,10 @@ resource "example_resource" "this" { | Name | Type | |------|------| -| [aws_iam_access_key.nullplatform_build_workflow_user_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource | -| [aws_iam_group.nullplatform_ecr_managers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group) | resource | | [aws_iam_group_policy_attachment.ecr_manager_policy_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | | [aws_iam_policy.nullplatform_ecr_manager_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.nullplatform_application_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.ecr_manager_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_user.nullplatform_build_workflow_user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource | -| [aws_iam_user_group_membership.build_workflow_ecr_managers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_group_membership) | resource | | [terraform_data.validations](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource | ## Inputs @@ -65,6 +71,7 @@ resource "example_resource" "this" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [application\_manager\_assume\_role](#input\_application\_manager\_assume\_role) | ARN of the IAM role assumed by the application manager | `string` | `"arn:aws:iam::283477532906:role/application_manager"` | no | +| [build\_workflow\_group\_name](#input\_build\_workflow\_group\_name) | Name of the IAM group (from the build-user module) to which the ECR manager policy is attached. The build workflow user is a member of this group. | `string` | n/a | yes | | [cluster\_name](#input\_cluster\_name) | Name of the cluster, used to namespace IAM resource names | `string` | n/a | yes | | [enable\_cross\_account\_pull](#input\_enable\_cross\_account\_pull) | Enable cross-account ECR pull access by creating an IAM role that external accounts can assume | `bool` | `false` | no | | [pull\_account\_ids](#input\_pull\_account\_ids) | AWS account IDs allowed to assume the cross-account ECR pull role. Required when enable\_cross\_account\_pull is true. | `list(string)` | `[]` | no | @@ -74,21 +81,18 @@ resource "example_resource" "this" { | Name | Description | |------|-------------| | [application\_role\_arn](#output\_application\_role\_arn) | ARN of the IAM role used by applications to pull ECR images | -| [build\_workflow\_access\_key\_id](#output\_build\_workflow\_access\_key\_id) | Access key ID for the CI/CD build workflow IAM user | -| [build\_workflow\_access\_key\_secret](#output\_build\_workflow\_access\_key\_secret) | Secret access key for the CI/CD build workflow IAM user | | [ecr\_repository\_policy](#output\_ecr\_repository\_policy) | ECR repository policy JSON granting pull access to the configured cross-account IDs. Empty string when enable\_cross\_account\_pull is false. | diff --git a/infrastructure/aws/iam/ecr/main.tf b/infrastructure/aws/iam/ecr/main.tf index acab5d2f..b95f87ea 100644 --- a/infrastructure/aws/iam/ecr/main.tf +++ b/infrastructure/aws/iam/ecr/main.tf @@ -44,30 +44,13 @@ resource "aws_iam_policy" "nullplatform_ecr_manager_policy" { }) } -resource "aws_iam_user" "nullplatform_build_workflow_user" { - name = "nullplatform-${var.cluster_name}-build-workflow-user" -} - -resource "aws_iam_access_key" "nullplatform_build_workflow_user_key" { - user = aws_iam_user.nullplatform_build_workflow_user.name -} - resource "aws_iam_role_policy_attachment" "ecr_manager_policy" { role = aws_iam_role.nullplatform_application_role.name policy_arn = aws_iam_policy.nullplatform_ecr_manager_policy.arn } -resource "aws_iam_group" "nullplatform_ecr_managers" { - name = "nullplatform-${var.cluster_name}-ecr-managers" -} - resource "aws_iam_group_policy_attachment" "ecr_manager_policy_group" { - group = aws_iam_group.nullplatform_ecr_managers.name + group = var.build_workflow_group_name policy_arn = aws_iam_policy.nullplatform_ecr_manager_policy.arn } -resource "aws_iam_user_group_membership" "build_workflow_ecr_managers" { - user = aws_iam_user.nullplatform_build_workflow_user.name - groups = [aws_iam_group.nullplatform_ecr_managers.name] -} - diff --git a/infrastructure/aws/iam/ecr/outputs.tf b/infrastructure/aws/iam/ecr/outputs.tf index c6274d95..8be19eb7 100644 --- a/infrastructure/aws/iam/ecr/outputs.tf +++ b/infrastructure/aws/iam/ecr/outputs.tf @@ -3,17 +3,6 @@ output "application_role_arn" { value = aws_iam_role.nullplatform_application_role.arn } -output "build_workflow_access_key_id" { - description = "Access key ID for the CI/CD build workflow IAM user" - value = aws_iam_access_key.nullplatform_build_workflow_user_key.id -} - -output "build_workflow_access_key_secret" { - description = "Secret access key for the CI/CD build workflow IAM user" - value = aws_iam_access_key.nullplatform_build_workflow_user_key.secret - sensitive = true -} - output "ecr_repository_policy" { description = "ECR repository policy JSON granting pull access to the configured cross-account IDs. Empty string when enable_cross_account_pull is false." value = var.enable_cross_account_pull ? jsonencode({ diff --git a/infrastructure/aws/iam/ecr/variables.tf b/infrastructure/aws/iam/ecr/variables.tf index 88ff1cd9..96f596ad 100644 --- a/infrastructure/aws/iam/ecr/variables.tf +++ b/infrastructure/aws/iam/ecr/variables.tf @@ -3,6 +3,11 @@ variable "cluster_name" { type = string } +variable "build_workflow_group_name" { + description = "Name of the IAM group (from the build-user module) to which the ECR manager policy is attached. The build workflow user is a member of this group." + type = string +} + variable "application_manager_assume_role" { description = "ARN of the IAM role assumed by the application manager" type = string diff --git a/infrastructure/aws/iam/s3-assets/README.md b/infrastructure/aws/iam/s3-assets/README.md new file mode 100644 index 00000000..f24ced38 --- /dev/null +++ b/infrastructure/aws/iam/s3-assets/README.md @@ -0,0 +1,62 @@ +# Module: s3-assets + +## Description + +Grants the shared build workflow group permission to publish build assets (e.g. Lambda deployment zips) to an existing S3 assets bucket + +## Architecture + +The module creates an `aws_iam_policy` allowing `s3:PutObject` and `s3:GetObject` on the objects of a given assets bucket (`arn:aws:s3:::/*`) and attaches it to the shared build-workflow group via `aws_iam_group_policy_attachment`. The group is created by the `build-user` module and passed in through `build_workflow_group_name`, so the build workflow user accumulates S3 publishing permissions alongside ECR (and any other destination) through that single group. The bucket itself is managed elsewhere and only referenced by name. + +## Features + +- Creates a namespaced `aws_iam_policy` scoped to `s3:PutObject`/`s3:GetObject` on the assets bucket objects +- Attaches the policy to the shared build-workflow group (created by the build-user module) +- Keeps the bucket out of scope: it is referenced by name, not created or managed here + +## Basic Usage + +```hcl +module "build_user" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/build-user?ref=v5.0.0" + + cluster_name = "your-cluster-name" +} + +module "s3_assets" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/s3-assets?ref=v5.0.0" + + cluster_name = "your-cluster-name" + build_workflow_group_name = module.build_user.group_name + assets_bucket = "your-assets-bucket" +} +``` + + + + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_group_policy_attachment.s3_assets_policy_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | +| [aws_iam_policy.nullplatform_s3_assets_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [assets\_bucket](#input\_assets\_bucket) | Name of the S3 bucket where build assets (e.g. Lambda zips) are published. The bucket is managed elsewhere; this module only grants the build workflow group permission to write to it. | `string` | n/a | yes | +| [build\_workflow\_group\_name](#input\_build\_workflow\_group\_name) | Name of the IAM group (from the build-user module) to which the S3 assets policy is attached. The build workflow user is a member of this group. | `string` | n/a | yes | +| [cluster\_name](#input\_cluster\_name) | Name of the cluster, used to namespace IAM resource names | `string` | n/a | yes | + +## Outputs + +No outputs. + diff --git a/infrastructure/aws/iam/s3-assets/main.tf b/infrastructure/aws/iam/s3-assets/main.tf new file mode 100644 index 00000000..ac1227c3 --- /dev/null +++ b/infrastructure/aws/iam/s3-assets/main.tf @@ -0,0 +1,22 @@ +resource "aws_iam_policy" "nullplatform_s3_assets_policy" { + name = "nullplatform-${var.cluster_name}-s3-assets-policy" + description = "Policy for publishing build assets (e.g. Lambda zips) to the assets S3 bucket" + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow" + Action = [ + "s3:PutObject", + "s3:GetObject" + ] + Resource = "arn:aws:s3:::${var.assets_bucket}/*" + } + ] + }) +} + +resource "aws_iam_group_policy_attachment" "s3_assets_policy_group" { + group = var.build_workflow_group_name + policy_arn = aws_iam_policy.nullplatform_s3_assets_policy.arn +} diff --git a/infrastructure/aws/iam/s3-assets/variables.tf b/infrastructure/aws/iam/s3-assets/variables.tf new file mode 100644 index 00000000..e2b27f17 --- /dev/null +++ b/infrastructure/aws/iam/s3-assets/variables.tf @@ -0,0 +1,14 @@ +variable "cluster_name" { + description = "Name of the cluster, used to namespace IAM resource names" + type = string +} + +variable "build_workflow_group_name" { + description = "Name of the IAM group (from the build-user module) to which the S3 assets policy is attached. The build workflow user is a member of this group." + type = string +} + +variable "assets_bucket" { + description = "Name of the S3 bucket where build assets (e.g. Lambda zips) are published. The bucket is managed elsewhere; this module only grants the build workflow group permission to write to it." + type = string +} From 370903357ac260d4b3d241118e23651c5df91452 Mon Sep 17 00:00:00 2001 From: Agustin Celentano <12614595+agustincelentano@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:21:58 -0300 Subject: [PATCH 2/3] feat(asset/s3): add S3 asset repository module Add nullplatform/asset/s3, which registers an AWS S3 bucket as a nullplatform asset repository by creating a nullplatform_provider_config of type "s3-configuration" (platform-global spec in the assets-repository category) with the target bucket.name. The platform exposes this bucket as aws.s3_assets_bucket, which the backend uses to build the S3 upload URL for Lambda/bundle assets. Unlike asset/ecr, this provider config carries no build credentials: S3 assets are published with the shared build workflow credentials (BUILD_AWS_*), so the build workflow user must be granted S3 permissions via infrastructure/aws/iam/s3-assets. --- nullplatform/asset/s3/README.md | 67 ++++++++++++++++++++++++++++++ nullplatform/asset/s3/main.tf | 11 +++++ nullplatform/asset/s3/providers.tf | 8 ++++ nullplatform/asset/s3/variables.tf | 15 +++++++ 4 files changed, 101 insertions(+) create mode 100644 nullplatform/asset/s3/README.md create mode 100644 nullplatform/asset/s3/main.tf create mode 100644 nullplatform/asset/s3/providers.tf create mode 100644 nullplatform/asset/s3/variables.tf diff --git a/nullplatform/asset/s3/README.md b/nullplatform/asset/s3/README.md new file mode 100644 index 00000000..3807f916 --- /dev/null +++ b/nullplatform/asset/s3/README.md @@ -0,0 +1,67 @@ +# Module: s3 + +## Description + +Configures an AWS S3 asset repository in nullplatform, registering the bucket where Lambda and bundle assets are published + +## Architecture + +The module creates a `nullplatform_provider_config` resource of type `s3-configuration` (a platform-global provider specification in the `assets-repository` category) whose attributes carry the target `bucket.name`. The platform maps this bucket to the `aws.s3_assets_bucket` NRN configuration (via the specification's `runtime_configuration` storage strategy), which the backend reads when generating the S3 upload URL for Lambda/bundle assets. Unlike the `ecr` asset module, this provider config does **not** carry build credentials: the CI publishes S3 assets with the shared build workflow credentials (`BUILD_AWS_*`), so the build workflow user must be granted S3 permissions separately via `infrastructure/aws/iam/s3-assets`. + +## Features + +- Registers an AWS S3 bucket as a nullplatform asset repository (`s3-configuration` provider config) +- Supplies the `bucket.name` that the platform exposes as `aws.s3_assets_bucket` +- Optionally segments the provider config by `dimensions` (e.g. region, environment) +- Does not manage the bucket or credentials: the bucket is referenced by name and S3 publish permissions are granted by `infrastructure/aws/iam/s3-assets` + +## Basic Usage + +```hcl +module "asset_s3" { + source = "git::https://github.com/nullplatform/tofu-modules.git//nullplatform/asset/s3?ref=v5.0.0" + + nrn = var.nrn + bucket_name = "your-assets-bucket" +} +``` + +Grant the build workflow user permission to write to that bucket with the companion IAM module: + +```hcl +module "s3_assets" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/s3-assets?ref=v5.0.0" + + cluster_name = "your-cluster-name" + build_workflow_group_name = module.build_user.group_name + assets_bucket = "your-assets-bucket" +} +``` + + + + +## Providers + +| Name | Version | +|------|---------| +| [nullplatform](#provider\_nullplatform) | ~> 0.0.88 | + +## Resources + +| Name | Type | +|------|------| +| [nullplatform_provider_config.s3](https://registry.terraform.io/providers/nullplatform/nullplatform/latest/docs/resources/provider_config) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [bucket\_name](#input\_bucket\_name) | Name of the existing S3 bucket used as the asset repository, where Lambda/bundle assets are published. Maps to the platform's aws.s3\_assets\_bucket configuration. | `string` | n/a | yes | +| [dimensions](#input\_dimensions) | Dimensions to segment the nullplatform provider config (e.g. by region, environment) | `map(string)` | `{}` | no | +| [nrn](#input\_nrn) | The nullplatform resource name (NRN) | `string` | n/a | yes | + +## Outputs + +No outputs. + diff --git a/nullplatform/asset/s3/main.tf b/nullplatform/asset/s3/main.tf new file mode 100644 index 00000000..9818d911 --- /dev/null +++ b/nullplatform/asset/s3/main.tf @@ -0,0 +1,11 @@ +resource "nullplatform_provider_config" "s3" { + provider = nullplatform + nrn = var.nrn + type = "s3-configuration" + dimensions = var.dimensions + attributes = jsonencode({ + bucket = { + name = var.bucket_name + } + }) +} diff --git a/nullplatform/asset/s3/providers.tf b/nullplatform/asset/s3/providers.tf new file mode 100644 index 00000000..1ad0cd5e --- /dev/null +++ b/nullplatform/asset/s3/providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + nullplatform = { + source = "nullplatform/nullplatform" + version = "~> 0.0.88" + } + } +} diff --git a/nullplatform/asset/s3/variables.tf b/nullplatform/asset/s3/variables.tf new file mode 100644 index 00000000..527285b5 --- /dev/null +++ b/nullplatform/asset/s3/variables.tf @@ -0,0 +1,15 @@ +variable "nrn" { + description = "The nullplatform resource name (NRN)" + type = string +} + +variable "dimensions" { + description = "Dimensions to segment the nullplatform provider config (e.g. by region, environment)" + type = map(string) + default = {} +} + +variable "bucket_name" { + description = "Name of the existing S3 bucket used as the asset repository, where Lambda/bundle assets are published. Maps to the platform's aws.s3_assets_bucket configuration." + type = string +} From c61a5fa549123ec3f3cff4430474aa629bedd13a Mon Sep 17 00:00:00 2001 From: Agustin Celentano <12614595+agustincelentano@users.noreply.github.com> Date: Thu, 25 Jun 2026 11:33:33 -0300 Subject: [PATCH 3/3] refactor(iam): rename build-user module to ci-build-workflow-user Rename infrastructure/aws/iam/build-user to ci-build-workflow-user for clarity: the name now states the purpose explicitly (the CI build-workflow identity that pipelines use to publish assets). The aws_iam_user's name is unchanged (nullplatform--build-workflow-user), so existing users are not recreated. Updates the ecr and s3-assets module docs/inputs that reference it. --- .../README.md | 0 .../main.tf | 0 .../outputs.tf | 0 .../variables.tf | 0 infrastructure/aws/iam/ecr/README.md | 24 +++++++++---------- infrastructure/aws/iam/ecr/variables.tf | 2 +- infrastructure/aws/iam/s3-assets/README.md | 12 +++++----- infrastructure/aws/iam/s3-assets/variables.tf | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) rename infrastructure/aws/iam/{build-user => ci-build-workflow-user}/README.md (100%) rename infrastructure/aws/iam/{build-user => ci-build-workflow-user}/main.tf (100%) rename infrastructure/aws/iam/{build-user => ci-build-workflow-user}/outputs.tf (100%) rename infrastructure/aws/iam/{build-user => ci-build-workflow-user}/variables.tf (100%) diff --git a/infrastructure/aws/iam/build-user/README.md b/infrastructure/aws/iam/ci-build-workflow-user/README.md similarity index 100% rename from infrastructure/aws/iam/build-user/README.md rename to infrastructure/aws/iam/ci-build-workflow-user/README.md diff --git a/infrastructure/aws/iam/build-user/main.tf b/infrastructure/aws/iam/ci-build-workflow-user/main.tf similarity index 100% rename from infrastructure/aws/iam/build-user/main.tf rename to infrastructure/aws/iam/ci-build-workflow-user/main.tf diff --git a/infrastructure/aws/iam/build-user/outputs.tf b/infrastructure/aws/iam/ci-build-workflow-user/outputs.tf similarity index 100% rename from infrastructure/aws/iam/build-user/outputs.tf rename to infrastructure/aws/iam/ci-build-workflow-user/outputs.tf diff --git a/infrastructure/aws/iam/build-user/variables.tf b/infrastructure/aws/iam/ci-build-workflow-user/variables.tf similarity index 100% rename from infrastructure/aws/iam/build-user/variables.tf rename to infrastructure/aws/iam/ci-build-workflow-user/variables.tf diff --git a/infrastructure/aws/iam/ecr/README.md b/infrastructure/aws/iam/ecr/README.md index 6e7ef625..2c63d9f7 100644 --- a/infrastructure/aws/iam/ecr/README.md +++ b/infrastructure/aws/iam/ecr/README.md @@ -2,25 +2,25 @@ ## Description -Provisions IAM resources for ECR image management and optional cross-account ECR pull access within a named cluster namespace. The build workflow identity (user, access key, group) lives in the `build-user` module; this module only grants ECR permissions to that group. +Provisions IAM resources for ECR image management and optional cross-account ECR pull access within a named cluster namespace. The build workflow identity (user, access key, group) lives in the `ci-build-workflow-user` module; this module only grants ECR permissions to that group. ## Architecture -The module creates an aws_iam_role (application role with a configurable assume-role principal) and an aws_iam_policy for ECR management actions. The ECR manager policy is attached to the application role via aws_iam_role_policy_attachment and to the shared build-workflow group via aws_iam_group_policy_attachment. The group itself is created by the `build-user` module and passed in through `build_workflow_group_name`. When enable_cross_account_pull is true, a separate aws_iam_role and aws_iam_policy scoped to read-only ECR actions are created and linked, with pull_account_ids driving the Principal trust statements. +The module creates an aws_iam_role (application role with a configurable assume-role principal) and an aws_iam_policy for ECR management actions. The ECR manager policy is attached to the application role via aws_iam_role_policy_attachment and to the shared build-workflow group via aws_iam_group_policy_attachment. The group itself is created by the `ci-build-workflow-user` module and passed in through `build_workflow_group_name`. When enable_cross_account_pull is true, a separate aws_iam_role and aws_iam_policy scoped to read-only ECR actions are created and linked, with pull_account_ids driving the Principal trust statements. ## Features - Creates a namespaced aws_iam_role for application image pulling with a configurable assume-role principal - Creates an aws_iam_policy granting full ECR repository lifecycle permissions including push, pull, and repository management -- Attaches the ECR manager policy to the shared build-workflow group (created by the build-user module) for group-based permission management +- Attaches the ECR manager policy to the shared build-workflow group (created by the ci-build-workflow-user module) for group-based permission management - Optionally creates a cross-account aws_iam_role and read-only ECR pull policy for external AWS accounts - Outputs a ready-to-use ECR repository policy JSON for cross-account pull access configuration ## Basic Usage ```hcl -module "build_user" { - source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/build-user?ref=v5.0.0" +module "ci_build_workflow_user" { + source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/ci-build-workflow-user?ref=v5.0.0" cluster_name = "your-cluster-name" } @@ -29,12 +29,12 @@ module "ecr" { source = "git::https://github.com/nullplatform/tofu-modules.git//infrastructure/aws/iam/ecr?ref=v5.0.0" cluster_name = "your-cluster-name" - build_workflow_group_name = module.build_user.group_name + build_workflow_group_name = module.ci_build_workflow_user.group_name } ``` > **Migration from < v5.0.0:** the build workflow user, access key and group were previously -> created by this module. They now live in `build-user`. See that module's README for the +> created by this module. They now live in `ci-build-workflow-user`. See that module's README for the > `tofu state mv` steps to migrate without rotating the access keys. ## Using Outputs @@ -71,7 +71,7 @@ resource "example_resource" "this" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [application\_manager\_assume\_role](#input\_application\_manager\_assume\_role) | ARN of the IAM role assumed by the application manager | `string` | `"arn:aws:iam::283477532906:role/application_manager"` | no | -| [build\_workflow\_group\_name](#input\_build\_workflow\_group\_name) | Name of the IAM group (from the build-user module) to which the ECR manager policy is attached. The build workflow user is a member of this group. | `string` | n/a | yes | +| [build\_workflow\_group\_name](#input\_build\_workflow\_group\_name) | Name of the IAM group (from the ci-build-workflow-user module) to which the ECR manager policy is attached. The build workflow user is a member of this group. | `string` | n/a | yes | | [cluster\_name](#input\_cluster\_name) | Name of the cluster, used to namespace IAM resource names | `string` | n/a | yes | | [enable\_cross\_account\_pull](#input\_enable\_cross\_account\_pull) | Enable cross-account ECR pull access by creating an IAM role that external accounts can assume | `bool` | `false` | no | | [pull\_account\_ids](#input\_pull\_account\_ids) | AWS account IDs allowed to assume the cross-account ECR pull role. Required when enable\_cross\_account\_pull is true. | `list(string)` | `[]` | no | @@ -87,12 +87,12 @@ resource "example_resource" "this" {