AWS Builder Center
Amazon S3 as a living data layer for your AWS Transform agent

Amazon S3 as a living data layer for your AWS Transform agent

Give your AWS Transform agent direct S3 access to read config files, write structured output, and trigger downstream workflows via EventBridge. Three patterns, one integration surface.

Enabling partners and customers to build migration agents on AWS Transform
Your AWS Transform  agent runs in your account, with your IAM role, and your credentials. That means it can talk to Amazon S3  the same way any other workload in your account does. No special SDK. No connector. Just boto3.client("s3") and whatever permissions you put on the role.
This article walks through three patterns for using S3 as the integration surface between your agent and the rest of your AWS environment: input (other systems feed the agent), output (the agent writes for downstream consumers), and events (S3 triggers actions when the agent writes).

Prerequisites

  • An AWS account with AWS Transform  configured
  • An agent deployed to AWS Transform (the Agent Builder Toolkit in Kiro  is the fastest path)
  • Basic IAM knowledge (inline policies, role trust)
  • An S3 bucket in the same account (or cross-account with appropriate trust)

Why go beyond the Artifacts Store

AWS Transform gives you an Artifacts Store out of the box. You upload files through the webapp, the agent reads and writes during a job. You can bring your own S3 bucket  as the backing store.
That covers the job lifecycle. But your agent exists inside a broader architecture. It might need to:
  • Read a config file that your ops team updates independently of any job
  • Write structured output to a location a dashboard already consumes
  • Trigger a notification pipeline the moment it finishes work
Direct S3 access makes your agent a first-class citizen of your AWS account, not just a participant in the Transform workflow.

How your agent gets S3 access

The credential model:
  1. Your agent runs on AWS Transform in your AWS account.
  2. AWS Transform runs your agent via Amazon Bedrock AgentCore , which assumes your agent's execution role and injects credentials into the runtime environment. If you deployed with the Agent Builder Toolkit in Kiro, this role is named AgentCoreExecutionRole by default.
  3. Your agent code calls boto3.client("s3"). The standard credential chain picks up the injected credentials automatically.
  4. Whatever permissions are on that role, your agent has.
No credential fetching code. No token refresh logic. No role assumption calls. AWS Transform handles credential rotation for the duration of the job.
The recommended role template does not include S3 access by default. You add it.

Grant your agent S3 access

Attach a scoped inline policy to your agent's execution role (substitute your actual role name if different):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
aws iam put-role-policy \
--role-name AgentCoreExecutionRole \
--policy-name S3DataLayerAccess \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::my-agent-data-bucket",
"arn:aws:s3:::my-agent-data-bucket/*"
]
}]
}'
Scope it to exactly the bucket or prefix your agent needs. Do not use s3:* on *. The IAM security best practices  apply here the same as anywhere else.

Pattern 1: S3 as input

Any process that can write to S3 can feed your agent. Your ops team, a CI/CD pipeline, another service. The agent reads the latest version at job start without any code change or redeployment.
Example: A mapping table that translates legacy system codes to modern equivalents. Your domain team maintains it. The agent consumes it.
1
2
3
4
5
6
7
8
9
10
import json
import boto3
from strands import tool

@tool
def load_mapping(bucket: str) -> dict:
"""Load the current mapping table from S3."""
s3 = boto3.client("s3")
response = s3.get_object(Bucket=bucket, Key="config/mapping.json")
return json.loads(response["Body"].read())
The @tool decorator from Strands Agents SDK  exposes this function to the agent's orchestrator. The agent decides when to call it based on context.

Pattern 2: S3 as output

Your agent writes structured output to an agreed-upon S3 location. Downstream systems consume it. The agent doesn't need to know about those systems, and they don't need to know about the agent's internals.
Example: The agent writes a migration assessment as JSON. A separate AWS Lambda  function picks it up and updates a QuickSight dashboard. The agent never touches QuickSight.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import json
import boto3
from strands import tool

@tool
def publish_assessment(bucket: str, job_id: str, assessment: dict) -> str:
"""Publish assessment results for downstream consumption."""
s3 = boto3.client("s3")
key = f"assessments/{job_id}/result.json"
s3.put_object(
Bucket=bucket,
Key=key,
Body=json.dumps(assessment, indent=2).encode("utf-8"),
ContentType="application/json"
)
return f"s3://{bucket}/{key}"
The contract between agent and consumer is the S3 prefix and the JSON schema. Nothing more.

Pattern 3: S3 events

When the agent writes to S3, Amazon EventBridge  can fire an event. Other systems react. The agent doesn't call them. The event does.
Example: The agent writes result.json. EventBridge fires. A Lambda sends a Slack notification.
Enable EventBridge notifications on your bucket:
1
2
3
aws s3api put-bucket-notification-configuration \
--bucket my-agent-data-bucket \
--notification-configuration '{"EventBridgeConfiguration":{}}'
Create an EventBridge rule that matches PutObject events on the assessments prefix:
1
2
3
4
5
6
7
8
9
10
aws events put-rule \
--name agent-output-written \
--event-pattern '{
"source": ["aws.s3"],
"detail-type": ["Object Created"],
"detail": {
"bucket": { "name": ["my-agent-data-bucket"] },
"object": { "key": [{ "prefix": "assessments/" }] }
}
}'
Lambda target that posts to Slack:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import json
import urllib.request

SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/..."

def handler(event, context):
bucket = event["detail"]["bucket"]["name"]
key = event["detail"]["object"]["key"]

message = f"Agent completed: s3://{bucket}/{key}"
payload = json.dumps({"text": message}).encode("utf-8")
req = urllib.request.Request(
SLACK_WEBHOOK_URL,
data=payload,
headers={"Content-Type": "application/json"}
)
urllib.request.urlopen(req)

return {"statusCode": 200}
The agent wrote a file. The rest happened without the agent knowing or caring.

Putting it together

Figure 1. Amazon S3 as the integration surface between your agent and the rest of your AWS account.
A worked example combining all three patterns:
  1. Before the job: Your ops team uploads mapping.json to s3://my-bucket/config/. The agent reads it at job start.
  2. During the job: The agent processes customer workloads using the mapping data.
  3. After the job: The agent writes assessment.json to s3://my-bucket/assessments/{job-id}/.
  4. Automatically: EventBridge catches the PutObject event. Lambda sends a Slack message. Downstream workflows pick up the output.
The agent stays focused on its domain logic. S3 is the seam between it and everything else.

Get started

  1. Create your S3 bucket (standard, nothing AWS Transform-specific about it)
  2. Add a scoped IAM policy to AgentCoreExecutionRole granting access to that bucket
  3. Deploy your agent. If already deployed, the updated IAM policy takes effect on the next job invocation.
  4. Use boto3.client("s3") in your agent code. It just works.

Conclusion

S3 gives your agent a way to participate in your broader AWS architecture without coupling to downstream systems. Three patterns, one integration surface:
  • Input: anything that writes to S3 can feed your agent
  • Output: anything that reads from S3 can consume your agent's work
  • Events: anything that listens to EventBridge can react when your agent writes
These are intentionally simple examples. Your agent doesn't need to know who's reading its output or who's updating its config. That's the point. S3 is the data layer. The agent stays focused on what it's good at.
The same structure scales: swap the mapping file for a feature flag system, the assessment JSON for a multi-account inventory, the Slack notification for a Step Functions workflow that another team owns. The patterns don't change. Your agent writes to S3, and the rest of your architecture reacts.

This information is accurate as of July 2026. AWS Transform and Amazon Bedrock AgentCore are actively evolving services. Consult the reference documents linked above for the most current security guidance.
Building agents on Transform? I'd like to hear about it. Find me on LinkedIn .
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article