From c8ef30a16bbd695721b6881a9ba1d541bb813175 Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Thu, 28 May 2026 12:07:55 -0300 Subject: [PATCH 01/15] feat: add dynamic assume role support via assume_role_arn in values.yaml When assume_role_arn is set, the agent's base credentials (IRSA or static) are used only to call sts:AssumeRole; all subsequent AWS calls (CLI + Terraform) run under the target role. When empty, IRSA is used directly, preserving backward compatibility for single-account setups. Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/scripts/aws/assume_role | 25 +++++++++++++++++++ aws-s3-bucket/scripts/aws/build_context | 3 +++ .../scripts/aws/build_permissions_context | 3 +++ aws-s3-bucket/values.yaml | 6 +++++ aws-s3-bucket/workflows/aws/create.yaml | 6 +++++ aws-s3-bucket/workflows/aws/delete.yaml | 6 +++++ aws-s3-bucket/workflows/aws/link-update.yaml | 6 +++++ aws-s3-bucket/workflows/aws/link.yaml | 6 +++++ aws-s3-bucket/workflows/aws/unlink.yaml | 6 +++++ aws-s3-bucket/workflows/aws/update.yaml | 6 +++++ 10 files changed, 73 insertions(+) create mode 100644 aws-s3-bucket/scripts/aws/assume_role diff --git a/aws-s3-bucket/scripts/aws/assume_role b/aws-s3-bucket/scripts/aws/assume_role new file mode 100644 index 0000000..21f5165 --- /dev/null +++ b/aws-s3-bucket/scripts/aws/assume_role @@ -0,0 +1,25 @@ +#!/bin/bash +# Sourceable helper — do NOT execute directly. +# Reads assume_role_arn from $VALUES. If set, calls sts:AssumeRole and exports +# temporary credentials so all subsequent AWS calls (CLI + Terraform) use that +# role. If empty, exports empty strings and IRSA from the pod handles auth. + +ASSUME_ROLE_ARN=$(grep "^assume_role_arn:" "${VALUES:-/dev/null}" 2>/dev/null \ + | sed 's/^[^:]*: *//;s/^"//;s/"$//' | head -1) + +if [ -n "$ASSUME_ROLE_ARN" ]; then + echo "Assuming role: $ASSUME_ROLE_ARN" + ASSUMED_CREDS=$(aws sts assume-role \ + --role-arn "$ASSUME_ROLE_ARN" \ + --role-session-name "np-s3-${SERVICE_ID:-workflow}" \ + --output json) + export AWS_ACCESS_KEY_ID=$(echo "$ASSUMED_CREDS" | jq -r '.Credentials.AccessKeyId') + export AWS_SECRET_ACCESS_KEY=$(echo "$ASSUMED_CREDS" | jq -r '.Credentials.SecretAccessKey') + export AWS_SESSION_TOKEN=$(echo "$ASSUMED_CREDS" | jq -r '.Credentials.SessionToken') + echo "Role assumed successfully." +else + echo "No assume_role_arn configured, using pod credentials (IRSA)." + export AWS_ACCESS_KEY_ID="" + export AWS_SECRET_ACCESS_KEY="" + export AWS_SESSION_TOKEN="" +fi diff --git a/aws-s3-bucket/scripts/aws/build_context b/aws-s3-bucket/scripts/aws/build_context index 8e2ae71..c1d53c9 100755 --- a/aws-s3-bucket/scripts/aws/build_context +++ b/aws-s3-bucket/scripts/aws/build_context @@ -71,6 +71,9 @@ if [ -n "${AWS_PROFILE_VAL}" ] && [ -z "${AWS_PROFILE:-}" ]; then export AWS_PROFILE="${AWS_PROFILE_VAL}" fi +# shellcheck source=assume_role +. "$(dirname "${BASH_SOURCE[0]}")/assume_role" + # --- Resolve AWS region from nullplatform provider -------------------------- # We derive the account NRN from the service NRN by stripping everything from # :namespace= onward, then query the provider with stored_keys: [account.region]. diff --git a/aws-s3-bucket/scripts/aws/build_permissions_context b/aws-s3-bucket/scripts/aws/build_permissions_context index 96cd775..9fa5dc7 100755 --- a/aws-s3-bucket/scripts/aws/build_permissions_context +++ b/aws-s3-bucket/scripts/aws/build_permissions_context @@ -36,6 +36,9 @@ if [ -n "${AWS_PROFILE_VAL}" ] && [ -z "${AWS_PROFILE:-}" ]; then export AWS_PROFILE="${AWS_PROFILE_VAL}" fi +# shellcheck source=assume_role +. "$(dirname "${BASH_SOURCE[0]}")/assume_role" + # --- Read service outputs (set by write_service_outputs after bucket creation) - SERVICE_ID=$(echo "$CONTEXT" | jq -r '.service.id') diff --git a/aws-s3-bucket/values.yaml b/aws-s3-bucket/values.yaml index 025cea0..fbf37f5 100644 --- a/aws-s3-bucket/values.yaml +++ b/aws-s3-bucket/values.yaml @@ -10,3 +10,9 @@ # will export it so Terraform and AWS CLI use the correct credentials. # Run "aws sso login --profile " before starting np-agent locally. aws_profile: "" + +# IAM role ARN to assume before running any AWS operation. +# When set, the agent's base credentials (e.g. IRSA) are used only to call +# sts:AssumeRole; all subsequent AWS calls (CLI + Terraform) run as this role. +# Leave empty to use the agent's assigned credentials directly (e.g. IRSA). +assume_role_arn: "" diff --git a/aws-s3-bucket/workflows/aws/create.yaml b/aws-s3-bucket/workflows/aws/create.yaml index b8b3809..f09f3bc 100644 --- a/aws-s3-bucket/workflows/aws/create.yaml +++ b/aws-s3-bucket/workflows/aws/create.yaml @@ -15,6 +15,12 @@ steps: type: environment - name: TOFU_VARIABLES type: environment + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment - name: tofu type: script diff --git a/aws-s3-bucket/workflows/aws/delete.yaml b/aws-s3-bucket/workflows/aws/delete.yaml index 18d0923..219aa33 100644 --- a/aws-s3-bucket/workflows/aws/delete.yaml +++ b/aws-s3-bucket/workflows/aws/delete.yaml @@ -15,6 +15,12 @@ steps: type: environment - name: TOFU_VARIABLES type: environment + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment - name: tofu type: script diff --git a/aws-s3-bucket/workflows/aws/link-update.yaml b/aws-s3-bucket/workflows/aws/link-update.yaml index baed09e..a165cb4 100644 --- a/aws-s3-bucket/workflows/aws/link-update.yaml +++ b/aws-s3-bucket/workflows/aws/link-update.yaml @@ -34,6 +34,12 @@ steps: type: environment - name: TOFU_VARIABLES type: environment + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment - name: tofu type: script diff --git a/aws-s3-bucket/workflows/aws/link.yaml b/aws-s3-bucket/workflows/aws/link.yaml index baed09e..a165cb4 100644 --- a/aws-s3-bucket/workflows/aws/link.yaml +++ b/aws-s3-bucket/workflows/aws/link.yaml @@ -34,6 +34,12 @@ steps: type: environment - name: TOFU_VARIABLES type: environment + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment - name: tofu type: script diff --git a/aws-s3-bucket/workflows/aws/unlink.yaml b/aws-s3-bucket/workflows/aws/unlink.yaml index 8e00019..b5d0856 100644 --- a/aws-s3-bucket/workflows/aws/unlink.yaml +++ b/aws-s3-bucket/workflows/aws/unlink.yaml @@ -34,6 +34,12 @@ steps: type: environment - name: TOFU_VARIABLES type: environment + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment - name: tofu type: script diff --git a/aws-s3-bucket/workflows/aws/update.yaml b/aws-s3-bucket/workflows/aws/update.yaml index b8b3809..f09f3bc 100644 --- a/aws-s3-bucket/workflows/aws/update.yaml +++ b/aws-s3-bucket/workflows/aws/update.yaml @@ -15,6 +15,12 @@ steps: type: environment - name: TOFU_VARIABLES type: environment + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment - name: tofu type: script From 21d9b39171ab5cc3c9040e1ae114b19acc7056a2 Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Thu, 28 May 2026 12:17:22 -0300 Subject: [PATCH 02/15] docs: add assume role configuration instructions to README Co-Authored-By: Claude Sonnet 4.6 --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 918ad5a..1b60fb2 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ The service lives under [`aws-s3-bucket/`](./aws-s3-bucket) to keep the repo ope │ ├── permissions/ # OpenTofu module: IAM user + access key + scoped policy (per link) │ ├── requirements/ # OpenTofu module: IAM policies the agent role needs │ ├── workflows/aws/ # Workflow YAMLs (create/update/delete/link/link-update/unlink) -│ ├── scripts/aws/ # build_context, do_tofu, write_service_outputs, write_link_outputs, delete_tfstate_bucket +│ ├── scripts/aws/ # build_context, do_tofu, write_service_outputs, write_link_outputs, delete_tfstate_bucket, assume_role │ ├── entrypoint/ # entrypoint/service/link (agent entrypoint) -│ └── values.yaml # Static config (aws_profile for local dev) +│ └── values.yaml # Static config (aws_profile, assume_role_arn) └── README.md ``` @@ -78,6 +78,49 @@ Only credentials are exposed at the link level — bucket identity (name / ARN / | `link-update` | Link updated | In-place update of the IAM user policy (access_level or path_prefix changes). Credentials are preserved. | | `unlink` | Application unlinked | Destroys the IAM user and access key | +## Agent AWS Authentication + +The agent authenticates to AWS using one of two mechanisms, configured via `values.yaml`: + +### IRSA (default) + +Leave `assume_role_arn` empty. The agent pod's IRSA role is used directly for all AWS calls (CLI + Terraform). This is the right choice when the IRSA role already has the required S3 and IAM permissions in the target account. + +```yaml +# values.yaml +assume_role_arn: "" +``` + +### Dynamic assume role + +Set `assume_role_arn` to an IAM role ARN. The agent calls `sts:AssumeRole` using its IRSA identity, then uses the resulting temporary credentials for all subsequent AWS calls in the workflow. This is useful when: + +- The S3 bucket lives in a **different AWS account** than the agent (cross-account) +- You want a **dedicated role per service type** (e.g. `np-s3-creator-role`) rather than granting all permissions to the agent's base role + +```yaml +# values.yaml +assume_role_arn: "arn:aws:iam::123456789012:role/np-s3-creator-role" +``` + +The target role must have a trust policy that allows the agent's IRSA role to assume it: + +```json +{ + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam:::role/" + }, + "Action": "sts:AssumeRole" +} +``` + +The target role needs the same IAM permissions as listed in the [AWS IAM permissions](#aws-iam-permissions-for-the-agent-role) section below. + +If `assume_role_arn` is set but the assume role call fails (wrong ARN, missing trust policy, insufficient permissions), the workflow **aborts immediately** — it does not fall back to the IRSA credentials. + +> **Note for overrides:** `values.yaml` can be overridden per-deployment via the `--overrides-path` mechanism. Set `assume_role_arn` in the overrides `values.yaml` to scope it to a specific customer or environment without modifying the base service definition. + ## Requirements ### nullplatform prerequisites From 7e1ee17971a28c9cea8055424a4677994cd179fa Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 10:27:46 -0300 Subject: [PATCH 03/15] feat(requirements): add assume role support to requirements module Add create_role and trusted_arns variables to optionally create an IAM role with a configurable trust policy, allowing other roles to assume it via sts:AssumeRole. Outputs role_arn and role_name expose the created role. Existing role_name behavior is unchanged. Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/requirements/main.tf | 39 ++++++++++++++++++++----- aws-s3-bucket/requirements/output.tf | 10 +++++++ aws-s3-bucket/requirements/variables.tf | 14 ++++++++- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/aws-s3-bucket/requirements/main.tf b/aws-s3-bucket/requirements/main.tf index c22da2a..5a1c83b 100644 --- a/aws-s3-bucket/requirements/main.tf +++ b/aws-s3-bucket/requirements/main.tf @@ -1,22 +1,47 @@ ################################################################################ -# Policy attachments (only when role_name is provided) +# IAM role (only when create_role = true) ################################################################################ +resource "aws_iam_role" "nullplatform_s3_role" { + count = var.create_role ? 1 : 0 + name = "nullplatform_${var.name}_s3_role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { AWS = var.trusted_arns } + Action = "sts:AssumeRole" + } + ] + }) +} + +################################################################################ +# Policy attachments (when create_role = true or role_name is provided) +################################################################################ + +locals { + effective_role_name = var.create_role ? aws_iam_role.nullplatform_s3_role[0].name : var.role_name + attach_policies = var.create_role || var.role_name != null +} + resource "aws_iam_role_policy_attachment" "s3" { - count = var.role_name != null ? 1 : 0 - role = var.role_name + count = local.attach_policies ? 1 : 0 + role = local.effective_role_name policy_arn = aws_iam_policy.nullplatform_s3_policy.arn } resource "aws_iam_role_policy_attachment" "s3_iam" { - count = var.role_name != null ? 1 : 0 - role = var.role_name + count = local.attach_policies ? 1 : 0 + role = local.effective_role_name policy_arn = aws_iam_policy.nullplatform_s3_iam_policy.arn } resource "aws_iam_role_policy_attachment" "s3_tfstate" { - count = var.role_name != null ? 1 : 0 - role = var.role_name + count = local.attach_policies ? 1 : 0 + role = local.effective_role_name policy_arn = aws_iam_policy.nullplatform_s3_tfstate_policy.arn } diff --git a/aws-s3-bucket/requirements/output.tf b/aws-s3-bucket/requirements/output.tf index ed628df..b9d8c8b 100644 --- a/aws-s3-bucket/requirements/output.tf +++ b/aws-s3-bucket/requirements/output.tf @@ -12,3 +12,13 @@ output "s3_tfstate_policy_arn" { description = "ARN of the tfstate bucket management policy" value = aws_iam_policy.nullplatform_s3_tfstate_policy.arn } + +output "role_arn" { + description = "ARN of the IAM role created by this module. Empty string when create_role is false." + value = var.create_role ? aws_iam_role.nullplatform_s3_role[0].arn : "" +} + +output "role_name" { + description = "Name of the IAM role created by this module. Empty string when create_role is false." + value = var.create_role ? aws_iam_role.nullplatform_s3_role[0].name : "" +} diff --git a/aws-s3-bucket/requirements/variables.tf b/aws-s3-bucket/requirements/variables.tf index 4c72b77..67eebb8 100644 --- a/aws-s3-bucket/requirements/variables.tf +++ b/aws-s3-bucket/requirements/variables.tf @@ -4,7 +4,19 @@ variable "name" { } variable "role_name" { - description = "IAM role name to attach the S3 service policies to. If set, Terraform manages the attachments and will detach them automatically on destroy." + description = "IAM role name to attach the S3 service policies to. If set, Terraform manages the attachments and will detach them automatically on destroy. Ignored when create_role is true." type = string default = null } + +variable "create_role" { + description = "When true, creates a new IAM role for the S3 service and attaches all policies to it. The role will allow the ARNs in trusted_arns to assume it via sts:AssumeRole." + type = bool + default = false +} + +variable "trusted_arns" { + description = "List of IAM principal ARNs (roles, users, accounts) allowed to assume the role created by this module. Only used when create_role is true." + type = list(string) + default = [] +} From 445c5c8c3114f8059965f1f7ea38fa85a18df107 Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 16:27:37 -0300 Subject: [PATCH 04/15] chore: set assume_role_arn for providers-test cluster simulation Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-s3-bucket/values.yaml b/aws-s3-bucket/values.yaml index fbf37f5..9c08922 100644 --- a/aws-s3-bucket/values.yaml +++ b/aws-s3-bucket/values.yaml @@ -15,4 +15,4 @@ aws_profile: "" # When set, the agent's base credentials (e.g. IRSA) are used only to call # sts:AssumeRole; all subsequent AWS calls (CLI + Terraform) run as this role. # Leave empty to use the agent's assigned credentials directly (e.g. IRSA). -assume_role_arn: "" +assume_role_arn: "arn:aws:iam::235494813897:role/nullplatform_aws-services-cluster_s3_role" From a30d3bc4cdd8f0fb44ecbe8eed36acedae80459c Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 16:30:44 -0300 Subject: [PATCH 05/15] fix: add -reconfigure to tofu init to handle stale backend state in OUTPUT_DIR Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/scripts/aws/do_tofu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-s3-bucket/scripts/aws/do_tofu b/aws-s3-bucket/scripts/aws/do_tofu index c8d1c31..f00f418 100755 --- a/aws-s3-bucket/scripts/aws/do_tofu +++ b/aws-s3-bucket/scripts/aws/do_tofu @@ -50,7 +50,7 @@ cp -r "$TOFU_MODULE_DIR"/* . echo "Running: tofu init" # shellcheck disable=SC2086 -tofu init $TOFU_INIT_VARIABLES +tofu init -reconfigure $TOFU_INIT_VARIABLES echo "Running: tofu $TOFU_ACTION" # shellcheck disable=SC2086 From 2bf770c5945210e26e282794da3d5ae3f37f5c2f Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 16:54:10 -0300 Subject: [PATCH 06/15] fix: auto-install aws CLI in build_context when not available in pod PATH Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/scripts/aws/build_context | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/aws-s3-bucket/scripts/aws/build_context b/aws-s3-bucket/scripts/aws/build_context index c1d53c9..f7b033d 100755 --- a/aws-s3-bucket/scripts/aws/build_context +++ b/aws-s3-bucket/scripts/aws/build_context @@ -9,6 +9,28 @@ set -euo pipefail # build_permissions_context in the next workflow step. # --------------------------------------------------------------------------- +# --------------------------------------------------------------------------- +# Ensure aws CLI is available. Download and cache if not pre-installed. +# --------------------------------------------------------------------------- +if ! command -v aws &>/dev/null; then + AWS_CLI_BIN_DIR="/tmp/np-aws-bin" + AWS_CLI_BIN="${AWS_CLI_BIN_DIR}/aws" + + if [ ! -f "${AWS_CLI_BIN}" ]; then + echo "aws CLI not found, installing to ${AWS_CLI_BIN_DIR}..." + mkdir -p "${AWS_CLI_BIN_DIR}" + curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip + unzip -qo /tmp/awscliv2.zip -d /tmp/awscliv2 + /tmp/awscliv2/aws/install --install-dir "${AWS_CLI_BIN_DIR}/dist" --bin-dir "${AWS_CLI_BIN_DIR}" --update + rm -rf /tmp/awscliv2.zip /tmp/awscliv2 + echo "aws CLI installed: $("${AWS_CLI_BIN}" --version)" + else + echo "Using cached aws CLI at ${AWS_CLI_BIN}" + fi + + export PATH="${AWS_CLI_BIN_DIR}:${PATH}" +fi + # --- Parse service context -------------------------------------------------- SERVICE_ID=$(echo "$CONTEXT" | jq -r '.service.id') From 5a710a39446de96e03ee657c768c1346cfda5def Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 16:56:34 -0300 Subject: [PATCH 07/15] revert: fix: auto-install aws CLI in build_context when not available in pod PATH --- aws-s3-bucket/scripts/aws/build_context | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/aws-s3-bucket/scripts/aws/build_context b/aws-s3-bucket/scripts/aws/build_context index f7b033d..c1d53c9 100755 --- a/aws-s3-bucket/scripts/aws/build_context +++ b/aws-s3-bucket/scripts/aws/build_context @@ -9,28 +9,6 @@ set -euo pipefail # build_permissions_context in the next workflow step. # --------------------------------------------------------------------------- -# --------------------------------------------------------------------------- -# Ensure aws CLI is available. Download and cache if not pre-installed. -# --------------------------------------------------------------------------- -if ! command -v aws &>/dev/null; then - AWS_CLI_BIN_DIR="/tmp/np-aws-bin" - AWS_CLI_BIN="${AWS_CLI_BIN_DIR}/aws" - - if [ ! -f "${AWS_CLI_BIN}" ]; then - echo "aws CLI not found, installing to ${AWS_CLI_BIN_DIR}..." - mkdir -p "${AWS_CLI_BIN_DIR}" - curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip - unzip -qo /tmp/awscliv2.zip -d /tmp/awscliv2 - /tmp/awscliv2/aws/install --install-dir "${AWS_CLI_BIN_DIR}/dist" --bin-dir "${AWS_CLI_BIN_DIR}" --update - rm -rf /tmp/awscliv2.zip /tmp/awscliv2 - echo "aws CLI installed: $("${AWS_CLI_BIN}" --version)" - else - echo "Using cached aws CLI at ${AWS_CLI_BIN}" - fi - - export PATH="${AWS_CLI_BIN_DIR}:${PATH}" -fi - # --- Parse service context -------------------------------------------------- SERVICE_ID=$(echo "$CONTEXT" | jq -r '.service.id') From 7588488507487b09114b95f1ed1dc666ed21d99e Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 17:04:58 -0300 Subject: [PATCH 08/15] fix: add missing S3 bucket read permissions required by AWS provider Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/requirements/main.tf | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/aws-s3-bucket/requirements/main.tf b/aws-s3-bucket/requirements/main.tf index 5a1c83b..552bc90 100644 --- a/aws-s3-bucket/requirements/main.tf +++ b/aws-s3-bucket/requirements/main.tf @@ -62,26 +62,38 @@ resource "aws_iam_policy" "nullplatform_s3_policy" { "Action" : [ "s3:CreateBucket", "s3:DeleteBucket", + "s3:HeadBucket", + "s3:ListBucket", + "s3:ListBucketVersions", + "s3:ListAllMyBuckets", "s3:GetBucketLocation", "s3:GetBucketVersioning", "s3:GetBucketEncryption", "s3:GetBucketPublicAccessBlock", "s3:GetBucketPolicy", "s3:GetBucketTagging", + "s3:GetBucketAcl", + "s3:GetBucketOwnershipControls", + "s3:GetBucketLogging", + "s3:GetBucketNotification", + "s3:GetBucketObjectLockConfiguration", + "s3:GetBucketReplication", + "s3:GetBucketRequestPayment", + "s3:GetBucketWebsite", + "s3:GetBucketCORS", + "s3:GetLifecycleConfiguration", + "s3:GetAccelerateConfiguration", "s3:PutBucketVersioning", "s3:PutBucketEncryption", "s3:PutBucketPublicAccessBlock", "s3:PutBucketPolicy", "s3:PutBucketTagging", + "s3:PutBucketOwnershipControls", "s3:DeleteBucketPolicy", - "s3:HeadBucket", - "s3:ListBucket", - "s3:ListBucketVersions", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", - "s3:DeleteObjectVersion", - "s3:ListAllMyBuckets" + "s3:DeleteObjectVersion" ], "Resource" : "*" } From f7c232dd373fa4f9361db13206e8aefdbc594d0e Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 17:08:48 -0300 Subject: [PATCH 09/15] fix: use correct IAM action name s3:GetReplicationConfiguration Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/requirements/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-s3-bucket/requirements/main.tf b/aws-s3-bucket/requirements/main.tf index 552bc90..64c0814 100644 --- a/aws-s3-bucket/requirements/main.tf +++ b/aws-s3-bucket/requirements/main.tf @@ -77,7 +77,7 @@ resource "aws_iam_policy" "nullplatform_s3_policy" { "s3:GetBucketLogging", "s3:GetBucketNotification", "s3:GetBucketObjectLockConfiguration", - "s3:GetBucketReplication", + "s3:GetReplicationConfiguration", "s3:GetBucketRequestPayment", "s3:GetBucketWebsite", "s3:GetBucketCORS", From 61f3bdaed3be336449b38a011affaf350d5c6629 Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 17:13:12 -0300 Subject: [PATCH 10/15] fix: correct IAM action names for encryption (Get/PutEncryptionConfiguration) Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/requirements/main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws-s3-bucket/requirements/main.tf b/aws-s3-bucket/requirements/main.tf index 64c0814..2bc072e 100644 --- a/aws-s3-bucket/requirements/main.tf +++ b/aws-s3-bucket/requirements/main.tf @@ -68,7 +68,7 @@ resource "aws_iam_policy" "nullplatform_s3_policy" { "s3:ListAllMyBuckets", "s3:GetBucketLocation", "s3:GetBucketVersioning", - "s3:GetBucketEncryption", + "s3:GetEncryptionConfiguration", "s3:GetBucketPublicAccessBlock", "s3:GetBucketPolicy", "s3:GetBucketTagging", @@ -84,7 +84,7 @@ resource "aws_iam_policy" "nullplatform_s3_policy" { "s3:GetLifecycleConfiguration", "s3:GetAccelerateConfiguration", "s3:PutBucketVersioning", - "s3:PutBucketEncryption", + "s3:PutEncryptionConfiguration", "s3:PutBucketPublicAccessBlock", "s3:PutBucketPolicy", "s3:PutBucketTagging", From f2673ce550d2c0bb7bba57d97f69bc8bfa36e20b Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 29 May 2026 17:24:14 -0300 Subject: [PATCH 11/15] fix(permissions): unset inherited credentials before assume_role to prevent self-assume failure Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/scripts/aws/build_permissions_context | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aws-s3-bucket/scripts/aws/build_permissions_context b/aws-s3-bucket/scripts/aws/build_permissions_context index 9fa5dc7..e18a1e0 100755 --- a/aws-s3-bucket/scripts/aws/build_permissions_context +++ b/aws-s3-bucket/scripts/aws/build_permissions_context @@ -36,6 +36,12 @@ if [ -n "${AWS_PROFILE_VAL}" ] && [ -z "${AWS_PROFILE:-}" ]; then export AWS_PROFILE="${AWS_PROFILE_VAL}" fi +# Unset any credentials inherited from build_context so assume_role uses the +# pod's IRSA identity instead of trying to re-assume an already-assumed role. +unset AWS_ACCESS_KEY_ID +unset AWS_SECRET_ACCESS_KEY +unset AWS_SESSION_TOKEN + # shellcheck source=assume_role . "$(dirname "${BASH_SOURCE[0]}")/assume_role" From 073ad4f9d308d060b770b9a7fed3e745d274f409 Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Mon, 1 Jun 2026 10:49:51 -0300 Subject: [PATCH 12/15] fix: always export link context vars and improve error visibility in build_context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed the ACTION_SOURCE guard around link variable extraction — the check was never true because ACTION_SOURCE is never set, causing LINK_ID and related outputs declared in link.yaml to be missing, which fails the workflow step. Variables now always extracted from $CONTEXT using jq defaults (empty string for non-link actions), preserving backward compatibility. Also redirected error messages from stderr to stdout so they appear in NP workflow logs, and added a DEBUG dump of the providers response to aid future diagnosis. Co-Authored-By: Claude Sonnet 4.6 --- aws-s3-bucket/scripts/aws/build_context | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/aws-s3-bucket/scripts/aws/build_context b/aws-s3-bucket/scripts/aws/build_context index c1d53c9..7a89043 100755 --- a/aws-s3-bucket/scripts/aws/build_context +++ b/aws-s3-bucket/scripts/aws/build_context @@ -81,7 +81,7 @@ fi ACCOUNT_NRN=$(echo "$CONTEXT" | jq -r '.service.nrn // .entity_nrn // ""' | sed 's/:namespace=.*$//') if [ -z "$ACCOUNT_NRN" ]; then - echo "ERROR: could not derive account NRN from .service.nrn in context" >&2 + echo "ERROR: could not derive account NRN from .service.nrn in context" exit 1 fi @@ -93,7 +93,8 @@ ACCOUNT_PROVIDER_ID=$(echo "$NP_PROVIDERS" \ | jq -r '[(.results // [])[] | select((.data_source.stored_keys // []) | contains(["account.region"]))] | first | .id // ""') if [ -z "$ACCOUNT_PROVIDER_ID" ] || [ "$ACCOUNT_PROVIDER_ID" = "null" ]; then - echo "ERROR: no account provider with account.region found for ${ACCOUNT_NRN}" >&2 + echo "ERROR: no account provider with account.region found for ${ACCOUNT_NRN}" + echo "DEBUG providers response: ${NP_PROVIDERS}" exit 1 fi @@ -101,7 +102,7 @@ ACCOUNT_PROVIDER_DATA=$(np provider read --id "$ACCOUNT_PROVIDER_ID" --format js REGION=$(echo "$ACCOUNT_PROVIDER_DATA" | jq -r '.attributes.account.region // ""') if [ -z "$REGION" ]; then - echo "ERROR: account.region not found in provider ${ACCOUNT_PROVIDER_ID}" >&2 + echo "ERROR: account.region not found in provider ${ACCOUNT_PROVIDER_ID}" exit 1 fi @@ -145,13 +146,11 @@ export TOFU_VARIABLES="-var=service_id=${SERVICE_ID} -var=region=${REGION} -var= # --- Extract link context (link workflows only) ----------------------------- # These are consumed by build_permissions_context in the next workflow step. -if [ "${ACTION_SOURCE:-}" = "link" ]; then - export LINK_ID=$(echo "$CONTEXT" | jq -r '.link.id // ""') - export LINK_NAME=$(echo "$CONTEXT" | jq -r '.link.name // ""') - export SCOPE_ID=$(echo "$CONTEXT" | jq -r '.link.scope.id // ""') - export SCOPE_NRN=$(echo "$CONTEXT" | jq -r '.link.scope.nrn // ""') +export LINK_ID=$(echo "$CONTEXT" | jq -r '.link.id // ""') +export LINK_NAME=$(echo "$CONTEXT" | jq -r '.link.name // ""') +export SCOPE_ID=$(echo "$CONTEXT" | jq -r '.link.scope.id // ""') +export SCOPE_NRN=$(echo "$CONTEXT" | jq -r '.link.scope.nrn // ""') - LINK_ATTRS=$(echo "$CONTEXT" | jq -r '(.link.attributes // {}) * (.parameters // {})') - export LINK_ACCESS_LEVEL=$(echo "$LINK_ATTRS" | jq -r '.access_level // "read-write"') - export LINK_PATH_PREFIX=$(echo "$LINK_ATTRS" | jq -r '.path_prefix // ""') -fi +LINK_ATTRS=$(echo "$CONTEXT" | jq -r '(.link.attributes // {}) * (.parameters // {})') +export LINK_ACCESS_LEVEL=$(echo "$LINK_ATTRS" | jq -r '.access_level // "read-write"') +export LINK_PATH_PREFIX=$(echo "$LINK_ATTRS" | jq -r '.path_prefix // ""') From f12d8114da35269b84e17c3daa52040bb75d7dfa Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Wed, 3 Jun 2026 17:08:07 -0300 Subject: [PATCH 13/15] chore: remove committed account ARN and local credentials, document changes Security/hygiene pass to keep the published service account-agnostic: - Delete local np-api-skill.{key,key.admin,token} files and ignore np-api-skill.*, *.key, *.token, *.pem and .claude/ so credentials can never be committed. - Clear the hardcoded testing ARN in aws-s3-bucket/values.yaml; assume_role_arn now defaults to "" (IRSA), with the account-specific ARN provided per installation via --overrides-path. - README: document credential isolation before assume role (self-assume prevention), expand the agent IAM permissions section (three policies + why the full s3:Get* read set is required), and clarify the account-agnostic note. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 9 +++++++++ README.md | 18 +++++++++++++----- aws-s3-bucket/values.yaml | 6 +++++- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index c1ae460..cc05805 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,12 @@ dist # Intellij .idea + +# Local nullplatform API credentials — never commit +np-api-skill.* +*.key +*.token +*.pem + +# Claude Code local config +.claude/ diff --git a/README.md b/README.md index 1b60fb2..de8929b 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,11 @@ The target role needs the same IAM permissions as listed in the [AWS IAM permiss If `assume_role_arn` is set but the assume role call fails (wrong ARN, missing trust policy, insufficient permissions), the workflow **aborts immediately** — it does not fall back to the IRSA credentials. -> **Note for overrides:** `values.yaml` can be overridden per-deployment via the `--overrides-path` mechanism. Set `assume_role_arn` in the overrides `values.yaml` to scope it to a specific customer or environment without modifying the base service definition. +#### Credential isolation before assume role + +Link actions (`link` / `link-update` / `unlink`) run `build_permissions_context`, which **unsets any AWS credentials inherited from earlier steps** (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN`) before sourcing `assume_role`. This is deliberate: without it, `sts:AssumeRole` would be called using *already-assumed* temporary credentials instead of the pod's IRSA identity, which fails as a self-assume (the assumed role is usually not trusted to assume itself). Clearing the environment first guarantees the assume-role call always starts from the IRSA identity, whether or not a previous step had already assumed the role. + +> **Note for overrides:** `values.yaml` ships with `assume_role_arn` empty so the published service stays account-agnostic. Provide the account-specific ARN per deployment via the `--overrides-path` mechanism — set `assume_role_arn` in the overrides `values.yaml` to scope it to a specific customer or environment without modifying the base service definition. ## Requirements @@ -129,11 +133,15 @@ If `assume_role_arn` is set but the assume role call fails (wrong ARN, missing t ### AWS IAM permissions (for the agent role) -The agent executing this service needs the IAM policies defined in [`aws-s3-bucket/requirements/main.tf`](./aws-s3-bucket/requirements/main.tf): +The agent executing this service (its IRSA role, or the `assume_role_arn` target) needs the three IAM policies defined in [`aws-s3-bucket/requirements/main.tf`](./aws-s3-bucket/requirements/main.tf). The `requirements/` module can also create the role itself (`create_role = true`) with a trust policy for `trusted_arns`. + +| Policy | Purpose | Resource scope | +|---|---|---| +| `nullplatform__s3_policy` | Create / configure / delete the user-facing S3 buckets | `*` | +| `nullplatform__s3_iam_policy` | Manage the per-link IAM users + access keys | `arn:aws:iam::*:user/np-s3-*` | +| `nullplatform__s3_tfstate_policy` | Manage the per-service OpenTofu state buckets | `arn:aws:s3:::np-service-*` (+ `/*`) | -- S3 bucket management (`s3:CreateBucket`, `s3:PutBucketVersioning`, etc.) over `*` -- IAM user management (`iam:CreateUser`, `iam:CreateAccessKey`, `iam:PutUserPolicy`, etc.) over `arn:aws:iam::*:user/np-s3-*` (or `*` for simpler scope) -- S3 tfstate management over `arn:aws:s3:::np-service-*` +**Why so many `s3:Get*` read actions?** The AWS Terraform provider refreshes the *full* configuration of every managed bucket on each `plan`/`apply` (versioning, encryption, replication, public-access block, ACL, ownership, logging, lifecycle, CORS, website, etc.). Each of those reads maps to a distinct IAM action — e.g. `s3:GetEncryptionConfiguration`, `s3:GetReplicationConfiguration`, `s3:GetBucketPublicAccessBlock`. Missing any one of them makes the provider fail the refresh even when the bucket itself is fine, so the bucket-management policy grants the complete read set alongside the `Create`/`Put`/`Delete` actions. ### Runtime dependencies diff --git a/aws-s3-bucket/values.yaml b/aws-s3-bucket/values.yaml index 9c08922..2c432b6 100644 --- a/aws-s3-bucket/values.yaml +++ b/aws-s3-bucket/values.yaml @@ -15,4 +15,8 @@ aws_profile: "" # When set, the agent's base credentials (e.g. IRSA) are used only to call # sts:AssumeRole; all subsequent AWS calls (CLI + Terraform) run as this role. # Leave empty to use the agent's assigned credentials directly (e.g. IRSA). -assume_role_arn: "arn:aws:iam::235494813897:role/nullplatform_aws-services-cluster_s3_role" +# +# Account-specific, so no real ARN is committed here. Provide it per installation +# via the --overrides-path values.yaml, e.g.: +# assume_role_arn: "arn:aws:iam:::role/" +assume_role_arn: "" From ab1ebf8fe2bd5023d4edcd678db904bf6574a1ff Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Fri, 5 Jun 2026 16:46:30 -0300 Subject: [PATCH 14/15] feat: resolve assume_role_arn from nullplatform IAM provider by selector The agent now resolves the IAM role ARN to assume from the "AWS IAM" provider (category Identity & Access Control, spec aws-iam-configuration) declared in nullplatform, matching its arns list by selector. Precedence: env var -> provider -> values.yaml -> IRSA. - assume_role_lib (new): pure arn_for_selector_from_json + provider_arn_for_selector (list->read, since provider list omits deep attributes). - assume_role: rewritten with the precedence chain above. - build_context: derive ACCOUNT_NRN and selector BEFORE sourcing assume_role (previously sourced before NRN was available); export ASSUME_ROLE_NRN/SELECTOR. - build_permissions_context: same selector resolution for link actions, after the inherited-credentials unset. - values.yaml: new assume_role_selector (default service slug); assume_role_arn is now a fallback for local testing / back-compat. - tests: bash unit tests for both lib functions, using a fake np on PATH as a test double; fixtures use placeholder account/ARN values only. - README: auth section rewritten to document the provider and precedence. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 45 ++++++++---- aws-s3-bucket/scripts/aws/assume_role | 38 ++++++++-- aws-s3-bucket/scripts/aws/assume_role_lib | 42 +++++++++++ aws-s3-bucket/scripts/aws/build_context | 21 ++++-- .../scripts/aws/build_permissions_context | 9 +++ .../scripts/aws/test/assume_role_lib_test | 44 +++++++++++ .../aws/test/assume_role_provider_test | 73 +++++++++++++++++++ aws-s3-bucket/values.yaml | 21 ++++-- 8 files changed, 262 insertions(+), 31 deletions(-) create mode 100644 aws-s3-bucket/scripts/aws/assume_role_lib create mode 100644 aws-s3-bucket/scripts/aws/test/assume_role_lib_test create mode 100644 aws-s3-bucket/scripts/aws/test/assume_role_provider_test diff --git a/README.md b/README.md index de8929b..02f87b5 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ The service lives under [`aws-s3-bucket/`](./aws-s3-bucket) to keep the repo ope │ ├── permissions/ # OpenTofu module: IAM user + access key + scoped policy (per link) │ ├── requirements/ # OpenTofu module: IAM policies the agent role needs │ ├── workflows/aws/ # Workflow YAMLs (create/update/delete/link/link-update/unlink) -│ ├── scripts/aws/ # build_context, do_tofu, write_service_outputs, write_link_outputs, delete_tfstate_bucket, assume_role +│ ├── scripts/aws/ # build_context, do_tofu, write_service_outputs, write_link_outputs, delete_tfstate_bucket, assume_role, assume_role_lib (+ test/) │ ├── entrypoint/ # entrypoint/service/link (agent entrypoint) -│ └── values.yaml # Static config (aws_profile, assume_role_arn) +│ └── values.yaml # Static config (aws_profile, assume_role_selector, assume_role_arn) └── README.md ``` @@ -80,30 +80,47 @@ Only credentials are exposed at the link level — bucket identity (name / ARN / ## Agent AWS Authentication -The agent authenticates to AWS using one of two mechanisms, configured via `values.yaml`: +The agent resolves the IAM role ARN to assume using the following **order of precedence**, then either assumes that role or falls back to IRSA: -### IRSA (default) +1. `ASSUME_ROLE_ARN` already set in the environment (explicit override). +2. The **"AWS IAM" provider** (category *Identity & Access Control*) declared in nullplatform — matched by selector. *(preferred)* +3. `assume_role_arn` in `values.yaml` (static fallback / local testing). +4. None of the above → the pod's **IRSA** identity is used directly. + +### Provider-based assume role (preferred) + +Declare an **"AWS IAM" provider** (specification `aws-iam-configuration`) at the account level in nullplatform. Its `iam_role_arns.arns` is a list of `{selector, arn}` pairs, so a single provider holds the role ARNs for every service/scope in the account: + +| selector | arn | +|---|---| +| `aws-s3-bucket` | `arn:aws:iam:::role/` | +| `lambda` | `arn:aws:iam:::role/` | -Leave `assume_role_arn` empty. The agent pod's IRSA role is used directly for all AWS calls (CLI + Terraform). This is the right choice when the IRSA role already has the required S3 and IAM permissions in the target account. +The agent looks the provider up at the account NRN, then picks the ARN whose `selector` matches `assume_role_selector` from `values.yaml` (default: the service slug, `aws-s3-bucket`). It then calls `sts:AssumeRole` with its IRSA identity and uses the resulting temporary credentials for all subsequent AWS calls (CLI + Terraform). ```yaml -# values.yaml -assume_role_arn: "" +# values.yaml — selector to match in the IAM provider (empty -> service slug) +assume_role_selector: "aws-s3-bucket" ``` -### Dynamic assume role +This is the right choice when the bucket lives in a **different account** than the agent (cross-account) or you want a **dedicated role per service type** rather than granting all permissions to the agent's base role — without committing any account-specific ARN to the repo. + +### IRSA (default) -Set `assume_role_arn` to an IAM role ARN. The agent calls `sts:AssumeRole` using its IRSA identity, then uses the resulting temporary credentials for all subsequent AWS calls in the workflow. This is useful when: +With no provider entry for the selector and `assume_role_arn` empty, the agent pod's IRSA role is used directly for all AWS calls. This is the right choice when the IRSA role already has the required S3 and IAM permissions in the target account. -- The S3 bucket lives in a **different AWS account** than the agent (cross-account) -- You want a **dedicated role per service type** (e.g. `np-s3-creator-role`) rather than granting all permissions to the agent's base role +### Static `assume_role_arn` (fallback) + +For local testing or single-account back-compat, set `assume_role_arn` in `values.yaml` directly. It is used only when the provider yields no ARN for the selector. ```yaml # values.yaml assume_role_arn: "arn:aws:iam::123456789012:role/np-s3-creator-role" ``` -The target role must have a trust policy that allows the agent's IRSA role to assume it: +### Trust policy + +However the ARN is resolved, the target role must have a trust policy that allows the agent's IRSA role to assume it: ```json { @@ -117,13 +134,13 @@ The target role must have a trust policy that allows the agent's IRSA role to as The target role needs the same IAM permissions as listed in the [AWS IAM permissions](#aws-iam-permissions-for-the-agent-role) section below. -If `assume_role_arn` is set but the assume role call fails (wrong ARN, missing trust policy, insufficient permissions), the workflow **aborts immediately** — it does not fall back to the IRSA credentials. +Once an ARN is resolved (from the provider or `assume_role_arn`), a failing `sts:AssumeRole` call (wrong ARN, missing trust policy, insufficient permissions) makes the workflow **abort immediately** — it does not fall back to the IRSA credentials. The IRSA fallback only applies when no ARN is resolved at all. #### Credential isolation before assume role Link actions (`link` / `link-update` / `unlink`) run `build_permissions_context`, which **unsets any AWS credentials inherited from earlier steps** (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN`) before sourcing `assume_role`. This is deliberate: without it, `sts:AssumeRole` would be called using *already-assumed* temporary credentials instead of the pod's IRSA identity, which fails as a self-assume (the assumed role is usually not trusted to assume itself). Clearing the environment first guarantees the assume-role call always starts from the IRSA identity, whether or not a previous step had already assumed the role. -> **Note for overrides:** `values.yaml` ships with `assume_role_arn` empty so the published service stays account-agnostic. Provide the account-specific ARN per deployment via the `--overrides-path` mechanism — set `assume_role_arn` in the overrides `values.yaml` to scope it to a specific customer or environment without modifying the base service definition. +> **Note for overrides:** `values.yaml` ships with `assume_role_arn` empty so the published service stays account-agnostic. Prefer declaring the per-account ARNs in the **"AWS IAM" provider** (account-specific data stays in nullplatform, not the repo). The static `assume_role_arn` remains available per deployment via the `--overrides-path` mechanism for local testing or environments without the provider. ## Requirements diff --git a/aws-s3-bucket/scripts/aws/assume_role b/aws-s3-bucket/scripts/aws/assume_role index 21f5165..9276e0f 100644 --- a/aws-s3-bucket/scripts/aws/assume_role +++ b/aws-s3-bucket/scripts/aws/assume_role @@ -1,12 +1,38 @@ #!/bin/bash # Sourceable helper — do NOT execute directly. -# Reads assume_role_arn from $VALUES. If set, calls sts:AssumeRole and exports -# temporary credentials so all subsequent AWS calls (CLI + Terraform) use that -# role. If empty, exports empty strings and IRSA from the pod handles auth. +# Resolves the IAM role ARN to assume, in this order of precedence: +# 1. $ASSUME_ROLE_ARN already set in the environment (explicit override). +# 2. The "AWS IAM" provider (Identity & Access Control) in nullplatform, +# matched by $ASSUME_ROLE_SELECTOR at $ASSUME_ROLE_NRN. This is the +# declarative, per-service/scope source. +# 3. assume_role_arn from $VALUES (values.yaml) — local testing / back-compat. +# 4. None of the above -> empty -> use pod credentials (IRSA) directly. +# +# When an ARN is resolved, calls sts:AssumeRole and exports temporary +# credentials so all subsequent AWS calls (CLI + Terraform) run as that role. -ASSUME_ROLE_ARN=$(grep "^assume_role_arn:" "${VALUES:-/dev/null}" 2>/dev/null \ - | sed 's/^[^:]*: *//;s/^"//;s/"$//' | head -1) +# shellcheck source=assume_role_lib +. "$(dirname "${BASH_SOURCE[0]}")/assume_role_lib" +ASSUME_ROLE_ARN="${ASSUME_ROLE_ARN:-}" + +# 2. nullplatform IAM provider, by selector. +if [ -z "$ASSUME_ROLE_ARN" ] && [ -n "${ASSUME_ROLE_SELECTOR:-}" ] && [ -n "${ASSUME_ROLE_NRN:-}" ]; then + ASSUME_ROLE_ARN=$(provider_arn_for_selector "$ASSUME_ROLE_NRN" "$ASSUME_ROLE_SELECTOR") + if [ -n "$ASSUME_ROLE_ARN" ]; then + echo "Resolved assume_role_arn from provider (selector=${ASSUME_ROLE_SELECTOR}): ${ASSUME_ROLE_ARN}" + else + echo "No ARN for selector '${ASSUME_ROLE_SELECTOR}' in IAM provider at ${ASSUME_ROLE_NRN}; trying values.yaml." + fi +fi + +# 3. Static values.yaml fallback. +if [ -z "$ASSUME_ROLE_ARN" ]; then + ASSUME_ROLE_ARN=$(grep "^assume_role_arn:" "${VALUES:-/dev/null}" 2>/dev/null \ + | sed 's/^[^:]*: *//;s/^"//;s/"$//' | head -1) +fi + +# 4. Assume the role, or fall back to IRSA. if [ -n "$ASSUME_ROLE_ARN" ]; then echo "Assuming role: $ASSUME_ROLE_ARN" ASSUMED_CREDS=$(aws sts assume-role \ @@ -18,7 +44,7 @@ if [ -n "$ASSUME_ROLE_ARN" ]; then export AWS_SESSION_TOKEN=$(echo "$ASSUMED_CREDS" | jq -r '.Credentials.SessionToken') echo "Role assumed successfully." else - echo "No assume_role_arn configured, using pod credentials (IRSA)." + echo "No assume role ARN resolved, using pod credentials (IRSA)." export AWS_ACCESS_KEY_ID="" export AWS_SECRET_ACCESS_KEY="" export AWS_SESSION_TOKEN="" diff --git a/aws-s3-bucket/scripts/aws/assume_role_lib b/aws-s3-bucket/scripts/aws/assume_role_lib new file mode 100644 index 0000000..90e8185 --- /dev/null +++ b/aws-s3-bucket/scripts/aws/assume_role_lib @@ -0,0 +1,42 @@ +#!/bin/bash +# Sourceable library of PURE helpers for assume-role resolution. +# Defines functions only — NO side effects on source, so it can be unit-tested +# (see scripts/aws/test/assume_role_lib_test) and reused by assume_role. + +# arn_for_selector_from_json +# Given the JSON returned by `np provider read --id --format json` and a +# selector string, echoes the matching IAM role ARN, or empty string if there +# is no match / the input is missing or malformed. First match wins. +arn_for_selector_from_json() { + local json="$1" selector="$2" + [ -n "$json" ] || return 0 + [ -n "$selector" ] || return 0 + printf '%s' "$json" | jq -r --arg sel "$selector" ' + [ .attributes.iam_role_arns.arns[]? + | select(.selector == $sel) + | .arn ] + | first // ""' 2>/dev/null +} + +# provider_arn_for_selector +# Looks up the "AWS IAM" provider (specification aws-iam-configuration, category +# "Identity & Access Control") at , reads it, and echoes the ARN matching +# . Empty string if no provider / no match. Requires np + jq. +# NOTE: `np provider list` does NOT return deep attributes, so we list to get the +# provider id and then `np provider read --id` to obtain the arns (same two-step +# pattern used for account.region resolution in build_context). +provider_arn_for_selector() { + local nrn="$1" selector="$2" + [ -n "$nrn" ] || return 0 + [ -n "$selector" ] || return 0 + + local pid data + pid=$(np provider list --nrn "$nrn" \ + --specification_slug aws-iam-configuration \ + --format json --limit 100 2>/dev/null \ + | jq -r '[ (.results // [])[] ] | first | .id // ""' 2>/dev/null) + [ -n "$pid" ] && [ "$pid" != "null" ] || return 0 + + data=$(np provider read --id "$pid" --format json 2>/dev/null) + arn_for_selector_from_json "$data" "$selector" +} diff --git a/aws-s3-bucket/scripts/aws/build_context b/aws-s3-bucket/scripts/aws/build_context index 7a89043..cae86f8 100755 --- a/aws-s3-bucket/scripts/aws/build_context +++ b/aws-s3-bucket/scripts/aws/build_context @@ -71,12 +71,9 @@ if [ -n "${AWS_PROFILE_VAL}" ] && [ -z "${AWS_PROFILE:-}" ]; then export AWS_PROFILE="${AWS_PROFILE_VAL}" fi -# shellcheck source=assume_role -. "$(dirname "${BASH_SOURCE[0]}")/assume_role" - -# --- Resolve AWS region from nullplatform provider -------------------------- +# --- Derive the account NRN (CONTEXT-only; needed for provider lookups) ------ # We derive the account NRN from the service NRN by stripping everything from -# :namespace= onward, then query the provider with stored_keys: [account.region]. +# :namespace= onward. ACCOUNT_NRN=$(echo "$CONTEXT" | jq -r '.service.nrn // .entity_nrn // ""' | sed 's/:namespace=.*$//') @@ -85,6 +82,20 @@ if [ -z "$ACCOUNT_NRN" ]; then exit 1 fi +# --- Assume role ------------------------------------------------------------ +# Selector for the "AWS IAM" provider (Identity & Access Control). Defaults to +# the service slug; override with assume_role_selector in values.yaml if the +# selector configured in the UI differs from the service slug. +SERVICE_SLUG=$(echo "$CONTEXT" | jq -r '.service.slug // ""') +export ASSUME_ROLE_NRN="$ACCOUNT_NRN" +export ASSUME_ROLE_SELECTOR=$(yaml_value "assume_role_selector" "$SERVICE_SLUG" "$VALUES") + +# shellcheck source=assume_role +. "$(dirname "${BASH_SOURCE[0]}")/assume_role" + +# --- Resolve AWS region from nullplatform provider -------------------------- +# Query the account providers and pick the one exposing account.region. + NP_PROVIDERS=$(np provider list --nrn "$ACCOUNT_NRN" --format json --limit 100) echo "Resolving region for account: ${ACCOUNT_NRN}" diff --git a/aws-s3-bucket/scripts/aws/build_permissions_context b/aws-s3-bucket/scripts/aws/build_permissions_context index e18a1e0..9ab42fb 100755 --- a/aws-s3-bucket/scripts/aws/build_permissions_context +++ b/aws-s3-bucket/scripts/aws/build_permissions_context @@ -41,6 +41,15 @@ fi unset AWS_ACCESS_KEY_ID unset AWS_SECRET_ACCESS_KEY unset AWS_SESSION_TOKEN +unset ASSUME_ROLE_ARN + +# Resolve the assume-role ARN from the "AWS IAM" provider. This service operates +# on S3 resources in the bucket's account, so it uses the same selector as the +# service actions (service slug, override via assume_role_selector in values.yaml). +ACCOUNT_NRN=$(echo "$CONTEXT" | jq -r '.service.nrn // .entity_nrn // ""' | sed 's/:namespace=.*$//') +SERVICE_SLUG=$(echo "$CONTEXT" | jq -r '.service.slug // ""') +export ASSUME_ROLE_NRN="$ACCOUNT_NRN" +export ASSUME_ROLE_SELECTOR=$(yaml_value "assume_role_selector" "$SERVICE_SLUG" "$VALUES") # shellcheck source=assume_role . "$(dirname "${BASH_SOURCE[0]}")/assume_role" diff --git a/aws-s3-bucket/scripts/aws/test/assume_role_lib_test b/aws-s3-bucket/scripts/aws/test/assume_role_lib_test new file mode 100644 index 0000000..327f0b2 --- /dev/null +++ b/aws-s3-bucket/scripts/aws/test/assume_role_lib_test @@ -0,0 +1,44 @@ +#!/bin/bash +# Unit tests for the pure resolution functions in assume_role_lib. +# No AWS / np calls — only the jq/selector logic that carries the real risk. +# Run: bash scripts/aws/test/assume_role_lib_test +set -u + +HERE="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../assume_role_lib +. "$HERE/../assume_role_lib" + +fail=0 +assert_eq() { + local desc="$1" expected="$2" actual="$3" + if [ "$expected" = "$actual" ]; then + echo "PASS: $desc" + else + echo "FAIL: $desc" + echo " expected: [$expected]" + echo " actual: [$actual]" + fail=1 + fi +} + +# A provider read payload as returned by `np provider read --id --format json`. +JSON='{"attributes":{"iam_role_arns":{"arns":[{"selector":"s3","arn":"arn:aws:iam::111:role/s3"},{"selector":"lambda","arn":"arn:aws:iam::111:role/lambda"}]}}}' + +assert_eq "selector s3 returns its arn" "arn:aws:iam::111:role/s3" "$(arn_for_selector_from_json "$JSON" s3)" +assert_eq "selector lambda returns its arn" "arn:aws:iam::111:role/lambda" "$(arn_for_selector_from_json "$JSON" lambda)" +assert_eq "unknown selector returns empty" "" "$(arn_for_selector_from_json "$JSON" ecs)" +assert_eq "missing arns key returns empty" "" "$(arn_for_selector_from_json '{"attributes":{}}' s3)" +assert_eq "empty input returns empty" "" "$(arn_for_selector_from_json '' s3)" +assert_eq "malformed json returns empty" "" "$(arn_for_selector_from_json 'not json' s3)" +assert_eq "empty selector returns empty" "" "$(arn_for_selector_from_json "$JSON" '')" + +# First match wins when duplicate selectors exist. +DUP='{"attributes":{"iam_role_arns":{"arns":[{"selector":"s3","arn":"first"},{"selector":"s3","arn":"second"}]}}}' +assert_eq "duplicate selector takes first" "first" "$(arn_for_selector_from_json "$DUP" s3)" + +if [ "$fail" -eq 0 ]; then + echo "--- all assertions passed ---" +else + echo "--- failures present ---" +fi +exit $fail diff --git a/aws-s3-bucket/scripts/aws/test/assume_role_provider_test b/aws-s3-bucket/scripts/aws/test/assume_role_provider_test new file mode 100644 index 0000000..f358db8 --- /dev/null +++ b/aws-s3-bucket/scripts/aws/test/assume_role_provider_test @@ -0,0 +1,73 @@ +#!/bin/bash +# Tests for provider_arn_for_selector — the np list->read orchestration. +# Uses a fake `np` on PATH as a test double for the external CLI (the only +# way to exercise the orchestration without a live nullplatform connection). +# Run: bash scripts/aws/test/assume_role_provider_test +set -u + +HERE="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=../assume_role_lib +. "$HERE/../assume_role_lib" + +fail=0 +assert_eq() { + local desc="$1" expected="$2" actual="$3" + if [ "$expected" = "$actual" ]; then + echo "PASS: $desc" + else + echo "FAIL: $desc" + echo " expected: [$expected]" + echo " actual: [$actual]" + fail=1 + fi +} + +# --- Fake np on PATH -------------------------------------------------------- +FAKE_DIR=$(mktemp -d) +cat > "$FAKE_DIR/np" <<'EOF' +#!/bin/bash +args="$*" +case "$args" in + *"provider list"*) + if [ "${FAKE_NP_MODE:-}" = "no_provider" ]; then + echo '{"results":[]}' + else + echo '{"results":[{"id":"prov-123"}]}' + fi + ;; + *"provider read"*) + echo '{"attributes":{"iam_role_arns":{"arns":[{"selector":"aws-s3-bucket","arn":"arn:aws:iam::123456789012:role/test-s3-role"}]}}}' + ;; + *) echo '{}' ;; +esac +EOF +chmod +x "$FAKE_DIR/np" +export PATH="$FAKE_DIR:$PATH" + +ARN_S3="arn:aws:iam::123456789012:role/test-s3-role" + +assert_eq "resolves arn for matching selector" "$ARN_S3" \ + "$(provider_arn_for_selector "organization=1:account=2" aws-s3-bucket)" + +export FAKE_NP_MODE=no_provider +assert_eq "no provider instance returns empty" "" \ + "$(provider_arn_for_selector "organization=1:account=2" aws-s3-bucket)" +unset FAKE_NP_MODE + +assert_eq "selector not in provider returns empty" "" \ + "$(provider_arn_for_selector "organization=1:account=2" does-not-exist)" + +assert_eq "empty nrn returns empty" "" \ + "$(provider_arn_for_selector "" aws-s3-bucket)" + +assert_eq "empty selector returns empty" "" \ + "$(provider_arn_for_selector "organization=1:account=2" "")" + +rm -rf "$FAKE_DIR" + +if [ "$fail" -eq 0 ]; then + echo "--- all assertions passed ---" +else + echo "--- failures present ---" +fi +exit $fail diff --git a/aws-s3-bucket/values.yaml b/aws-s3-bucket/values.yaml index 2c432b6..d42a884 100644 --- a/aws-s3-bucket/values.yaml +++ b/aws-s3-bucket/values.yaml @@ -11,12 +11,21 @@ # Run "aws sso login --profile " before starting np-agent locally. aws_profile: "" -# IAM role ARN to assume before running any AWS operation. -# When set, the agent's base credentials (e.g. IRSA) are used only to call -# sts:AssumeRole; all subsequent AWS calls (CLI + Terraform) run as this role. -# Leave empty to use the agent's assigned credentials directly (e.g. IRSA). +# --- Assume role ------------------------------------------------------------ +# Preferred source: the "AWS IAM" provider (category "Identity & Access Control") +# declared in nullplatform. The agent looks up that provider at the account NRN +# and picks the ARN whose `selector` matches assume_role_selector below. This +# keeps role ARNs declarative and per-service/scope, with no account-specific +# value committed here. # -# Account-specific, so no real ARN is committed here. Provide it per installation -# via the --overrides-path values.yaml, e.g.: +# selector to match in the IAM provider's arns list. Leave empty to default to +# the service slug (.service.slug from the notification context). Set explicitly +# if the selector configured in the UI differs from the service slug. +assume_role_selector: "aws-s3-bucket" + +# Static fallback used only when the IAM provider yields no ARN for the selector +# (e.g. local testing without nullplatform, or back-compat single-account setups). +# When set, the agent's base credentials (e.g. IRSA) are used only to call +# sts:AssumeRole; all subsequent AWS calls run as this role. Empty -> IRSA direct. # assume_role_arn: "arn:aws:iam:::role/" assume_role_arn: "" From e39cba4cae4d3eaaeb275309f3927e52976d52d3 Mon Sep 17 00:00:00 2001 From: David Fernandez Date: Tue, 9 Jun 2026 19:25:06 -0300 Subject: [PATCH 15/15] refactor(workflows): assume role in a dedicated first step Extract the assume-role logic out of build_context and build_permissions_context into a new standalone assume_role_step that runs as the first step of every aws workflow. The role is now assumed exactly once, up front, and the temporary credentials are exported and inherited (then re-exported) by all subsequent steps. Assuming once eliminates the double-assume that required unsetting and re-assuming credentials in build_permissions_context (the self-assume failure addressed in 5671ac6); that workaround is removed. Co-Authored-By: Claude Opus 4.8 (1M context) --- aws-s3-bucket/scripts/aws/assume_role_step | 58 +++++++++++++++++++ aws-s3-bucket/scripts/aws/build_context | 14 ++--- .../scripts/aws/build_permissions_context | 24 +++----- aws-s3-bucket/workflows/aws/create.yaml | 11 ++++ aws-s3-bucket/workflows/aws/delete.yaml | 11 ++++ aws-s3-bucket/workflows/aws/link-update.yaml | 11 ++++ aws-s3-bucket/workflows/aws/link.yaml | 11 ++++ aws-s3-bucket/workflows/aws/unlink.yaml | 11 ++++ aws-s3-bucket/workflows/aws/update.yaml | 11 ++++ 9 files changed, 135 insertions(+), 27 deletions(-) create mode 100755 aws-s3-bucket/scripts/aws/assume_role_step diff --git a/aws-s3-bucket/scripts/aws/assume_role_step b/aws-s3-bucket/scripts/aws/assume_role_step new file mode 100755 index 0000000..59f6bb9 --- /dev/null +++ b/aws-s3-bucket/scripts/aws/assume_role_step @@ -0,0 +1,58 @@ +#!/bin/bash +set -euo pipefail + +# --------------------------------------------------------------------------- +# assume_role_step — FIRST step of every aws workflow. +# +# Resolves the IAM role to assume (nullplatform "AWS IAM" provider by selector, +# or the values.yaml fallback), calls sts:AssumeRole once, and exports temporary +# AWS credentials. Every subsequent step (build_context, +# build_permissions_context, do_tofu, ...) inherits and re-exports these, so the +# whole workflow runs as that role. If no ARN resolves, falls back to the pod's +# IRSA identity (empty credentials -> default chain). +# +# Doing the assume exactly once, up front, avoids the self-assume failure that +# happened when a second step tried to re-assume on top of already-assumed +# credentials (see commit 5671ac6). +# +# Inputs (from the NP notification context / values): +# $CONTEXT — notification JSON (.service.nrn, .service.slug) +# $VALUES — path to values.yaml (aws_profile, assume_role_selector, ...) +# Outputs (captured by the workflow YAML as type: environment): +# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN +# --------------------------------------------------------------------------- + +# CRITICAL: $VALUES is a FILE PATH (set by np service workflow exec --values), +# NOT JSON. Read individual keys with yaml_value(). +yaml_value() { + local key="$1" default="$2" file="$3" + local val + val=$(grep "^${key}:" "$file" 2>/dev/null | sed 's/^[^:]*: *//;s/^"//;s/"$//' | head -1) + echo "${val:-$default}" +} + +# Named AWS profile for local testing (e.g. an SSO profile). Used only to obtain +# the base credentials that call sts:AssumeRole; once a role is assumed the +# AWS_* credentials below take precedence. +AWS_PROFILE_VAL=$(yaml_value "aws_profile" "" "$VALUES") +if [ -n "${AWS_PROFILE_VAL}" ] && [ -z "${AWS_PROFILE:-}" ]; then + export AWS_PROFILE="${AWS_PROFILE_VAL}" +fi + +# Derive the account NRN (strip everything from :namespace= onward) and the +# service slug; both feed the per-service ARN lookup in the IAM provider. +ACCOUNT_NRN=$(echo "$CONTEXT" | jq -r '.service.nrn // .entity_nrn // ""' | sed 's/:namespace=.*$//') +if [ -z "$ACCOUNT_NRN" ]; then + echo "ERROR: could not derive account NRN from .service.nrn in context" >&2 + exit 1 +fi +SERVICE_SLUG=$(echo "$CONTEXT" | jq -r '.service.slug // ""') + +# Selector for the "AWS IAM" provider (Identity & Access Control). Defaults to +# the service slug; override with assume_role_selector in values.yaml if the +# selector configured in the UI differs from the service slug. +export ASSUME_ROLE_NRN="$ACCOUNT_NRN" +export ASSUME_ROLE_SELECTOR=$(yaml_value "assume_role_selector" "$SERVICE_SLUG" "$VALUES") + +# shellcheck source=assume_role +. "$(dirname "${BASH_SOURCE[0]}")/assume_role" diff --git a/aws-s3-bucket/scripts/aws/build_context b/aws-s3-bucket/scripts/aws/build_context index cae86f8..0e6d928 100755 --- a/aws-s3-bucket/scripts/aws/build_context +++ b/aws-s3-bucket/scripts/aws/build_context @@ -82,16 +82,10 @@ if [ -z "$ACCOUNT_NRN" ]; then exit 1 fi -# --- Assume role ------------------------------------------------------------ -# Selector for the "AWS IAM" provider (Identity & Access Control). Defaults to -# the service slug; override with assume_role_selector in values.yaml if the -# selector configured in the UI differs from the service slug. -SERVICE_SLUG=$(echo "$CONTEXT" | jq -r '.service.slug // ""') -export ASSUME_ROLE_NRN="$ACCOUNT_NRN" -export ASSUME_ROLE_SELECTOR=$(yaml_value "assume_role_selector" "$SERVICE_SLUG" "$VALUES") - -# shellcheck source=assume_role -. "$(dirname "${BASH_SOURCE[0]}")/assume_role" +# --- AWS credentials -------------------------------------------------------- +# The role was already assumed by the "assume role" workflow step, which exported +# AWS_ACCESS_KEY_ID/SECRET/SESSION_TOKEN. They are inherited here and re-exported +# in this step's output block, so all AWS CLI + Terraform calls run as that role. # --- Resolve AWS region from nullplatform provider -------------------------- # Query the account providers and pick the one exposing account.region. diff --git a/aws-s3-bucket/scripts/aws/build_permissions_context b/aws-s3-bucket/scripts/aws/build_permissions_context index 9ab42fb..824a489 100755 --- a/aws-s3-bucket/scripts/aws/build_permissions_context +++ b/aws-s3-bucket/scripts/aws/build_permissions_context @@ -36,23 +36,13 @@ if [ -n "${AWS_PROFILE_VAL}" ] && [ -z "${AWS_PROFILE:-}" ]; then export AWS_PROFILE="${AWS_PROFILE_VAL}" fi -# Unset any credentials inherited from build_context so assume_role uses the -# pod's IRSA identity instead of trying to re-assume an already-assumed role. -unset AWS_ACCESS_KEY_ID -unset AWS_SECRET_ACCESS_KEY -unset AWS_SESSION_TOKEN -unset ASSUME_ROLE_ARN - -# Resolve the assume-role ARN from the "AWS IAM" provider. This service operates -# on S3 resources in the bucket's account, so it uses the same selector as the -# service actions (service slug, override via assume_role_selector in values.yaml). -ACCOUNT_NRN=$(echo "$CONTEXT" | jq -r '.service.nrn // .entity_nrn // ""' | sed 's/:namespace=.*$//') -SERVICE_SLUG=$(echo "$CONTEXT" | jq -r '.service.slug // ""') -export ASSUME_ROLE_NRN="$ACCOUNT_NRN" -export ASSUME_ROLE_SELECTOR=$(yaml_value "assume_role_selector" "$SERVICE_SLUG" "$VALUES") - -# shellcheck source=assume_role -. "$(dirname "${BASH_SOURCE[0]}")/assume_role" +# --- AWS credentials -------------------------------------------------------- +# The role was already assumed by the "assume role" workflow step (the same +# selector this link flow needs, since it operates on S3 resources in the +# bucket's account). The temporary credentials are inherited here and +# re-exported in this step's output block, so the IAM user is created as that +# role. No re-assume is needed — doing it once up front avoids the self-assume +# failure that occurred when re-assuming on top of already-assumed credentials. # --- Read service outputs (set by write_service_outputs after bucket creation) - diff --git a/aws-s3-bucket/workflows/aws/create.yaml b/aws-s3-bucket/workflows/aws/create.yaml index f09f3bc..79a0b15 100644 --- a/aws-s3-bucket/workflows/aws/create.yaml +++ b/aws-s3-bucket/workflows/aws/create.yaml @@ -1,4 +1,15 @@ steps: + - name: assume role + type: script + file: $SERVICE_PATH/scripts/aws/assume_role_step + output: + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment + - name: build context type: script file: $SERVICE_PATH/scripts/aws/build_context diff --git a/aws-s3-bucket/workflows/aws/delete.yaml b/aws-s3-bucket/workflows/aws/delete.yaml index 219aa33..29b5eb0 100644 --- a/aws-s3-bucket/workflows/aws/delete.yaml +++ b/aws-s3-bucket/workflows/aws/delete.yaml @@ -1,4 +1,15 @@ steps: + - name: assume role + type: script + file: $SERVICE_PATH/scripts/aws/assume_role_step + output: + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment + - name: build context type: script file: $SERVICE_PATH/scripts/aws/build_context diff --git a/aws-s3-bucket/workflows/aws/link-update.yaml b/aws-s3-bucket/workflows/aws/link-update.yaml index a165cb4..069545d 100644 --- a/aws-s3-bucket/workflows/aws/link-update.yaml +++ b/aws-s3-bucket/workflows/aws/link-update.yaml @@ -1,4 +1,15 @@ steps: + - name: assume role + type: script + file: $SERVICE_PATH/scripts/aws/assume_role_step + output: + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment + - name: build context type: script file: $SERVICE_PATH/scripts/aws/build_context diff --git a/aws-s3-bucket/workflows/aws/link.yaml b/aws-s3-bucket/workflows/aws/link.yaml index a165cb4..069545d 100644 --- a/aws-s3-bucket/workflows/aws/link.yaml +++ b/aws-s3-bucket/workflows/aws/link.yaml @@ -1,4 +1,15 @@ steps: + - name: assume role + type: script + file: $SERVICE_PATH/scripts/aws/assume_role_step + output: + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment + - name: build context type: script file: $SERVICE_PATH/scripts/aws/build_context diff --git a/aws-s3-bucket/workflows/aws/unlink.yaml b/aws-s3-bucket/workflows/aws/unlink.yaml index b5d0856..f367fdd 100644 --- a/aws-s3-bucket/workflows/aws/unlink.yaml +++ b/aws-s3-bucket/workflows/aws/unlink.yaml @@ -1,4 +1,15 @@ steps: + - name: assume role + type: script + file: $SERVICE_PATH/scripts/aws/assume_role_step + output: + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment + - name: build context type: script file: $SERVICE_PATH/scripts/aws/build_context diff --git a/aws-s3-bucket/workflows/aws/update.yaml b/aws-s3-bucket/workflows/aws/update.yaml index f09f3bc..79a0b15 100644 --- a/aws-s3-bucket/workflows/aws/update.yaml +++ b/aws-s3-bucket/workflows/aws/update.yaml @@ -1,4 +1,15 @@ steps: + - name: assume role + type: script + file: $SERVICE_PATH/scripts/aws/assume_role_step + output: + - name: AWS_ACCESS_KEY_ID + type: environment + - name: AWS_SECRET_ACCESS_KEY + type: environment + - name: AWS_SESSION_TOKEN + type: environment + - name: build context type: script file: $SERVICE_PATH/scripts/aws/build_context