Choose the simplest safe access pattern, trace every run and prove that disabling your agent actually stops its AWS access.

Your AWS-connected agent works. Now someone asks who can disable it, which customer data it can reach, and whether yesterday’s credentials will still work after you switch it off. Those are production access questions, not prompting questions. Use a simple lifecycle framework—issue, constrain, attribute, deny—to choose the right access pattern and build a shutdown procedure you can demonstrate. Start with one read-only S3 task, then prove both that new sessions stop and that previously issued credentials lose access.
Choose the simplest access pattern you can safely disable
Issue credentials that can only read this task’s files
Trace an AWS request back to one authorised run
Prove the boundary before allowing autonomous writes
Run a shutdown drill against credentials already issued
Budget for evidence and ownership rather than unnecessary machinery
Start with the deployment boundary, not a credential broker. The simplest safe design keeps AI agent IAM permissions narrow and gives someone a tested way to stop access.
Use Issue → Constrain → Attribute → Deny as the access framework. Assign a named human owner at every stage, even if one engineer owns all four:
These are implementation choices, not competing credential types. All three can use temporary credentials. Add machinery when the trust boundary demands it, not simply because an agent exists.
| Pattern | Use it when | Boundary and trade-off |
|---|---|---|
| Dedicated execution role | One internal, read-only agent; one data boundary. | An ECS task role or Lambda execution role has fixed, narrow permissions. Application-level run attribution is acceptable. |
| Per-task STS | Each run needs distinct scope and attribution. | Trusted orchestration assumes a target role separately for each run, applying task-specific scope and run identity. |
| Credential broker | Untrusted third-party runtimes, multiple tenants or centrally enforced approvals. | An authenticated service outside the agent validates identity, tenant, approved tool and task. It issues scoped STS credentials or executes the operation itself. |
AWS distinguishes agent identities from human identities and recommends attributable access. The architecture is: authenticated requester → trusted orchestrator or broker → STS → narrowly scoped role → approved AWS resource. CloudTrail records the access path; enable relevant data events for operations such as S3 object reads.
Deleting a worker is not a credential-revocation procedure. Issued credentials may remain usable elsewhere until expiry unless an effective deny blocks access.
Choosing the deployment boundary is an architectural decision; Cloudline’s AWS architecture and cloud security support covers that responsibility alongside platform design.
Temporary credentials are a lease, not a kill switch.
Share this quote

Separate three controls: who can assume the role, what the role permits, and what this task permits. AI agent least privilege needs all three.
Use account 111122223333, region eu-west-2, and a uniquely named sandbox bucket represented by BUCKET. It contains existing SSE-S3 objects support/run-001/brief.txt and support/run-002/brief.txt. Replace BUCKET in both policies with its actual name. Do not add bucket policies granting access directly to session principals.
Set this trust policy on AgentSupportRead. It trusts AgentIssuer, with explicit permission to set SourceIdentity, which AWS records for attribution.
{
"Version":"2012-10-17",
"Statement":[{
"Effect":"Allow",
"Principal":{"AWS":"arn:aws:iam::111122223333:role/AgentIssuer"},
"Action":["sts:AssumeRole","sts:SetSourceIdentity"],
"Condition":{
"StringLike":{
"sts:RoleSessionName":"agent-*",
"sts:SourceIdentity":"run-*"
}
}
}]
}
Add this identity-policy statement to AgentIssuer:
{
"Effect":"Allow",
"Action":["sts:AssumeRole","sts:SetSourceIdentity"],
"Resource":"arn:aws:iam::111122223333:role/AgentSupportRead"
}
Give AgentSupportRead only these base permissions:
{
"Version":"2012-10-17",
"Statement":[{
"Effect":"Allow",
"Action":"s3:GetObject",
"Resource":"arn:aws:s3:::BUCKET/support/*"
}]
}
Save the task restriction as task-session.json:
{
"Version":"2012-10-17",
"Statement":[{
"Effect":"Allow",
"Action":"s3:GetObject",
"Resource":"arn:aws:s3:::BUCKET/support/run-001/*"
}]
}
ListBucket is deliberately unnecessary: this tool reads known keys. Session permissions normally intersect with role permissions; they do not add permissions.
Naming conditions do not require a session policy. A model-controlled issuer could omit it and read both runs. Trusted orchestration must construct scope from authorised task metadata, not model-supplied paths.
Run this Bash snippet as AgentIssuer, with AWS CLI and jq installed. AssumeRole permits a minimum requested duration of 900 seconds; role chaining has a one-hour maximum.
set +x
umask 077
session_file=$(mktemp) || exit 1
trap 'rm -f "$session_file"' EXIT
aws sts assume-role \
--region eu-west-2 \
--role-arn arn:aws:iam::111122223333:role/AgentSupportRead \
--role-session-name agent-run-001 \
--source-identity run-001 \
--duration-seconds 900 \
--policy file://task-session.json \
>"$session_file" || exit 1
agent_aws() (
set +x
values=$(jq -er \
'.Credentials | [.AccessKeyId,.SecretAccessKey,.SessionToken] | @tsv' \
"$session_file") || exit 1
IFS=$'\t' read -r \
AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN \
<<<"$values"
export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
unset AWS_PROFILE AWS_DEFAULT_PROFILE
export AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null
export AWS_PAGER=""
aws --region eu-west-2 "$@"
)
The helper loads exactly that file’s three credential values in a subshell. Automatic refresh cannot silently replace them during later tests. Keep tracing disabled; never print the credential file.
In production, keep credentials inside the tool runner, outside prompts and model-visible files. Bring existing roles under reviewed Terraform ownership, rather than maintaining these boundaries through console edits.

Test the saved run-001 AWS agent temporary credentials against disposable, pre-created fixtures. An administrator must create both briefs, ./harmless-fixture.txt and a real sandbox escape-test role that does not trust this agent. Set ESCAPE_ROLE_ARN to that role’s ARN.
Run every table row in order, in the same harness shell. The positive control must succeed; every negative probe must return AccessDenied. A missing object is not evidence of isolation.
set -eu
export AWS_PAGER=""
LOG=$(mktemp)
trap 'rm -f "$LOG"' EXIT
must_deny() {
if "$@" >"$LOG" 2>&1; then
printf 'FAIL: unexpected success\n' >&2
exit 1
fi
if ! grep -Fq 'An error occurred (AccessDenied)' "$LOG"; then
cat "$LOG" >&2
exit 1
fi
}
| Probe | Command | Required result |
|---|---|---|
| Authorised read | agent_aws s3api get-object --bucket "$BUCKET" --key support/run-001/brief.txt /tmp/agent-brief.txt | Success |
| Other run’s existing file | must_deny agent_aws s3api get-object --bucket "$BUCKET" --key support/run-002/brief.txt /tmp/other-brief.txt | AccessDenied, not NoSuchKey |
| Write within own prefix | must_deny agent_aws s3api put-object --bucket "$BUCKET" --key support/run-001/negative-test.txt --body ./harmless-fixture.txt | AccessDenied |
| IAM discovery | must_deny agent_aws iam list-roles | AccessDenied |
| Role escape | must_deny agent_aws sts assume-role --role-arn "$ESCAPE_ROLE_ARN" --role-session-name escape-test | AccessDenied |
Clean up fixtures and any unexpected writes using a separate administrator identity. Never run destructive probes against production. These tests exercise this fixture’s intended boundary, not a universal security certification. Inspect resource-based policies too: their grants can alter effective permissions. The session policy is not the only control.
MCP AWS security needs an IAM boundary. Tool allowlists help, but the runner’s IAM rights remain decisive when shell access or another AWS client bypasses MCP. AWS’s 14 April 2026 guidance explicitly recognises this bypass.
Hypothetically, an agent selects production instead of staging. A tool allowlist cannot correct an authorised but wrongly targeted request. Keep IAM or trust changes, secrets access, destructive data operations, production networking and unbounded infrastructure creation outside autonomous execution.
For a narrow, reversible write, require a deterministic wrapper, fixed target, bounded retries, idempotency and rollback. Approval must bind the exact action and target outside the model; a chat message saying “approved” is not that control. Apply staged rollout and rollback thinking before widening access.

Use an isolated AgentSupportRead test role and two terminals: saved agent credentials in A, a separate incident administrator identity in B. Never let the test helper refresh credentials. Record the credential expiration, UTC timestamps and original successful GetObject, including bucket and key.
Production containment must block issuance and deny current access together. The two-phase sequence below deliberately separates them to demonstrate the trap safely.
Freeze terminal A. Load the saved STS response from saved-session.json into environment credentials. These do not automatically refresh. Use the same object throughout.
export AWS_ACCESS_KEY_ID="$(jq -r '.Credentials.AccessKeyId' saved-session.json)"
export AWS_SECRET_ACCESS_KEY="$(jq -r '.Credentials.SecretAccessKey' saved-session.json)"
export AWS_SESSION_TOKEN="$(jq -r '.Credentials.SessionToken' saved-session.json)"
export AWS_EC2_METADATA_DISABLED=true
jq -r '.Credentials.Expiration' saved-session.json
date -u
aws s3api get-object --bucket "$BUCKET" --key "$KEY" /tmp/agent-drill-object
Phase A: block issuance. Stop the worker and broker issuance. Save this as blocked-trust.json, then apply it in terminal B using the incident administrator identity.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Principal": "*",
"Action": "sts:AssumeRole"
}]
}
aws iam update-assume-role-policy --role-name AgentSupportRead --policy-document file://blocked-trust.json
Retry the issuer’s original AssumeRole request until denied. Repeat A’s successful GetObject. Changing trust does not itself remove an already issued session’s permissions.
Phase B: deny existing use. In B, attach the deny policy:
aws iam attach-role-policy --role-name AgentSupportRead --policy-arn arn:aws:iam::aws:policy/AWSDenyAll
Record the application time. In A, poll the same GetObject every five seconds with the exact saved credentials until AccessDenied. Record elapsed time; expiration or another error is not proof.
Updated permissions apply to existing sessions, but policy propagation can take a few minutes. Do not promise instantaneous shutdown. Do not use GetCallerIdentity as the criterion: it can return identity despite an explicit deny.
As an alternative to AWSDenyAll, save this as revoke-before.json. Replace the illustrative timestamp with the drill’s recorded UTC cutoff.
{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"*","Resource":"*","Condition":{"DateLessThan":{"aws:TokenIssueTime":"2026-10-31T12:00:00Z"}}}]}
aws iam put-role-policy --role-name AgentSupportRead --policy-name AWSRevokeOlderSessions --policy-document file://revoke-before.json
This denies every matching session of that role issued before the cutoff, not future sessions. Keep issuance blocked. Retain the deny until affected credentials expire; review restoration as a separate change.
To review your agent shutdown procedure, include offboarding: disable triggers and OAuth refresh paths, remove legacy static keys, and preserve audit evidence. Reconcile out-of-band IAM changes with Terraform; check downstream assumed roles and delegated jobs separately.
Your agent is not off until its old credentials fail.
Share this quote

Budget for evidence and a named owner before building a credential broker. Tight AI agent IAM permissions, searchable run records and a tested shutdown path matter more than another service to maintain.
The examples below use AWS published USD rates, accessed 31 October 2026. Volumes are explicit workload assumptions, not typical agent traffic.
| Logging option | Rate and illustrative cost |
|---|---|
| Management-event history | Free. |
| First management-event trail copy | No CloudTrail delivery charge; S3 charges remain. |
| Selected S3 data events | $0.10 per 100,000 events. Assumed one million monthly: $1.00 delivery. |
| Trail plus Athena SQL | $1.00 delivery + $0.50 for an assumed 0.1 TB scan at $5/TB = $1.50. |
| CloudTrail Lake, one-year extendable option | $0.75/GB ingestion; $0.005/GB scanned. Assumed 10 GB ingested + 100 GB scanned = $8.00, including the first year’s retention. |
These are not all-in bills. Exclusions: VAT, foreign exchange, S3 storage and requests, KMS, Glue where applicable, additional copies, broker runtime and broader observability. Scan volume matters: querying every object repeatedly can dwarf event-delivery costs.
This explicitly hypothetical budget assumes £800 per engineer-day, existing workload identity and logging infrastructure, one AWS account and one tool. These are neither Cloudline prices nor measured benchmarks.
| Access pattern | Illustrative effort | Budget |
|---|---|---|
| Dedicated role | 0.5–1 day | £400–£800 |
| Per-task sessions | 1–2 days | £800–£1,600 |
| Small broker proof of concept | 3–5 days | £2,400–£4,000 |
Estimates exclude production hardening, availability engineering and ongoing support. Assign ownership for policy changes, evidence retention and shutdown drills; a broker creates another production dependency.
For a second pair of eyes, talk to a cloud engineer about an agent-access security review. We can review issuer trust, effective permissions, per-run evidence, negative tests and observed shutdown behaviour.
Choose the smallest access mechanism that fits your task, then make its lifecycle observable. Your release gate should include a successful authorised request, failed out-of-scope requests, a traceable run owner and a shutdown drill using credentials issued before the stop. Keep high-impact writes behind independent approval. Cloudline Consulting can help review the trust boundary, permission policies and offboarding procedure, so your team has evidence that the agent’s access ends when you intend it to.
Give the agent a separate role containing only the actions and resources its tools require. Start with a fixed read-only task rather than a broad managed policy. Separate human and agent identities, then test both permitted and denied requests against the real resource boundary. Broader access needs a specific operational justification.
Yes. A workload role can supply temporary credentials, or trusted orchestration can request a separate STS AssumeRole session for each task. An AssumeRole request can specify 900 seconds as its minimum duration. Keep credential issuance outside model-controlled code and pass credentials only to the tool runner that needs them.
No. Blocking the trust relationship prevents new assumptions after the change propagates; it does not by itself deny requests signed with existing session credentials. Deny the role’s permissions or apply a timestamp-based revocation policy as well. Confirm the outcome by repeating a resource request that succeeded before shutdown.
Not by itself. MCP can expose narrow tools, but the server still needs an appropriately restricted AWS identity. A shell tool or another AWS client may bypass MCP entirely. Restrict the runner’s effective permissions, isolate credential issuance and test direct AWS requests as well as the advertised tool interface.
Record a trusted run identifier when issuing the session and retain its authenticated owner in your application audit record. Query CloudTrail for the issuance event and subsequent requests. For S3 object reads and writes, enable the relevant data events; management-event history alone is not sufficient evidence of object access.