references/iam-credentials.md·Markdown·26 KB·View on GitHub
Credentials MUST be ephemeral-first. Prefer, in order: (1) IAM Identity
Center / SSO, then (2) aws sts assume-role session credentials (which include
AWS_SESSION_TOKEN). Do NOT ask the seller to provide, paste, or export long-lived IAM
user access keys, and do NOT ask for credentials outright as a first step — have the
seller configure one of the ephemeral paths in their own environment. Never log or echo
credential values.
# ⚠️ LAST RESORT — prefer SSO (A) or sts assume-role (B) above.# Use ONLY short-lived STS session credentials (with AWS_SESSION_TOKEN), never# long-lived IAM user access keys. If credentials must be stored, use AWS Secrets# Manager or SSM Parameter Store — never plaintext files or source control.export AWS_ACCESS_KEY_ID=<temporary-key>export AWS_SECRET_ACCESS_KEY=<temporary-secret>export AWS_SESSION_TOKEN=<session-token> # REQUIRED — do not use keys without a session token
Verify credentials immediately after configuration:
bash
aws sts get-caller-identity
Confirm the output shows the correct seller account ID. Do not proceed until this succeeds. These same credentials will be used for all subsequent operations: Catalog API validation, stack deployment, and integration testing.
If the seller asks “what permissions do I need?”, provide the policy below. Otherwise, proceed with whatever credentials they provide — if a permission is missing, the error will indicate exactly which action is needed.
Tag-authorization principle (why the policy is split into Create / Manage / Read buckets): Tags supplied at creation are authorized with aws:RequestTag, neveraws:ResourceTag — on a create the resource does not exist yet, so it has no tags and an aws:ResourceTag/* condition is always false, denying the create. Services that attach tags/targets/sub-resources in a separate call after the parent resource is created — CloudFormation, API Gateway, EventBridge — cannot be aws:ResourceTag-gated for their create/rollback lifecycle at all (gating them also wedges rollback in DELETE_FAILED/ROLLBACK_FAILED). Only services CloudFormation tags atomically at creation (Lambda, DynamoDB, IAM roles) can keep aws:ResourceTag/ManagedBy on their non-create operations. Read actions (Get*/Describe*/List*/Query/Scan/apigateway:GET) are also never tag-gated, because CloudFormation issues describe/GET waiters during creation (e.g. dynamodb:DescribeTable waiting for ACTIVE, lambda:GetFunction) against a just-created, not-yet-tagged resource.
Buckets: Create — no condition, name-scoped awsmp-*. Manage — aws:ResourceTag/ManagedBy for mutations of existing resources. Read — no condition, name-scoped. Special — iam:PassRole scoped by iam:PassedToService; unconditioned aws-marketplace catalog read.
To create this role, the seller can run the following. The trust policy MUST be
conditioned — an unconditioned Principal: {"AWS": "...:root"} lets ANY
principal in the account assume the deployer. The DEFAULT below targets a headless-agent
caller: a named role-ARN principal + an sts:ExternalId condition, and no MFA (a
headless agent cannot satisfy MFA). For a human operator you MAY additionally require
MFA ("Bool": {"aws:MultiFactorAuthPresent": "true"}) — that is a human-only variant, not
the default. This role is commonly assumed cross-account (the caller’s account assuming
into the seller’s account); in that case the caller’s own principal ALSO needs an
sts:AssumeRole grant on this role ARN (both sides), in addition to the trust policy below.
First create the permissions boundary the iam:CreateRole statement requires:
bash
# 1. Create the permissions boundary as a MANAGED policy (a boundary MUST be managed,# never an inline put-role-policy). This caps what any awsmp-* role can do.aws iam create-policy --policy-name awsmp-metering-boundary \ --policy-document file://boundary.json# 2. Create the deployer role with a CONDITIONED trust policy (NOT open :root).# DEFAULT = headless agent: role-ARN principal + sts:ExternalId, NO MFA.aws iam create-role --role-name MarketplaceMeteringDeployer --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::<CALLER_ACCOUNT_ID>:role/<AGENT_CALLER_ROLE>"}, "Action": "sts:AssumeRole", "Condition": { "StringEquals": {"sts:ExternalId": "<CHOOSE_A_SECRET_EXTERNAL_ID>"} } }]}'# (Human-operator variant only: add "Bool": {"aws:MultiFactorAuthPresent": "true"} to Condition.)aws iam put-role-policy --role-name MarketplaceMeteringDeployer --policy-name DeployPolicy --policy-document file://policy.json
Permissions boundary — REQUIRED before deployment, and the names MUST match.
The deployer policy’s iam:CreateRole is gated on
iam:PermissionsBoundary = arn:aws:iam::*:policy/awsmp-metering-boundary. IAM only
populates the iam:PermissionsBoundary request key when a boundary is actually attached,
so any awsmp-* role created without that exact boundary is denied with AccessDenied.
SAM auto-generates the Register/Meter/Subscription Lambda execution roles from the inline
Policies in the templates, so the templates set
Globals.Function.PermissionsBoundary: arn:aws:iam::${AWS::AccountId}:policy/${PermissionsBoundaryName}
(parameter PermissionsBoundaryName, default awsmp-metering-boundary) on those
generated roles. The boundary policy MUST already exist in the account before you run
deploy.sh, and its name MUST equal both the deployer condition and the
PermissionsBoundaryName parameter. If you rename it, update all three.
A boundary that caps the generated Lambda roles to exactly what this skill’s Lambdas use
(boundary.json):
Note: the boundary does NOT grant permissions — it caps them. The role’s own inline
policy still governs effective access; the boundary just ensures a self-created awsmp-*
role can never exceed this action set. Each statement is also resource-scoped to
awsmp-* (DynamoDB tables + their indexes, SQS queues, SNS topics, and the
/aws/lambda/awsmp-* log groups), and kms:Decrypt/kms:GenerateDataKey are constrained
by a kms:ViaService condition so keys can only be used through DynamoDB, SQS, and SNS. It
deliberately omits dynamodb:Scan, dynamodb:DeleteItem, and any iam:*, so a boundary’d
role cannot broaden its own DynamoDB access or escalate via IAM.
Important: Do NOT use an admin role. Create a dedicated role with only the permissions above. Using admin credentials violates least-privilege and poses a security risk if the agent environment is compromised.
Key Facts — Dimension Validation (ALWAYS mention when asked about new dimensions):
Check that dimension appears with ExternallyMetered type — case-sensitive match required
If the dimension is not a defined pricing dimension for the product, BatchMeterUsage REJECTS the record with InvalidUsageDimensionException (not billed)
Verify dimension keys against the Catalog API BEFORE deploying the metering Lambda