AWS Builder Center
AWSome Salesforce - Automating Zero Copy Iceberg Federation with Kiro and Headless Data 360

AWSome Salesforce - Automating Zero Copy Iceberg Federation with Kiro and Headless Data 360

Using Headless 360 MCP and AWS CDK to automate the integration of Salesforce Data 360 and AWS S3 with Iceberg Federation.

disclaimer: These are my personal opinions and work, and does not represent AWS documentation or the views of Amazon. Use at your own risk!
I'm proud to have recently published Zero Copy Access to Apache Iceberg Tables in Amazon S3 from Salesforce Data 360 , with Avijit Goswami, Pratik Das, and Srividya Parthasarathy. If you haven't read it already, jump off and do that first. It's an important blog that highlights the now-GA feature enabling Salesforce Data 360  to query Iceberg tables in Amazon S3 directly, and most importantly the basis for the Headless 360 experience we will be trying here.
The blog above is a manual walkthrough. You click through the AWS console to stand up the S3 bucket, Glue database, and Lake Formation grants, then click through the Salesforce UI to configure the connector and data stream. That got me thinking: with Headless Data 360 and a bit of help from our friends Claude and Kiro, could we automate the process entirely? Headless Data 360 exposes the same API that powers the UI, and the new Data 360 MCP Server puts those operations in reach of an AI agent. So instead of a human following screenshots, give an agent access to the API and let it do the work. In this post, we show how we automated the entire zero copy federation path, using CDK for the AWS infrastructure and the Data 360 MCP Server driven by a Kiro skill for the Salesforce configuration, collapsing the manual UX steps from the original blog into a single agentic session.

The approach: CDK for AWS, MCP for Salesforce

The AWS side is straightforward infrastructure-as-code. Three CDK stacks deploy in sequence. An Athena query creates the Iceberg table and loads sample data. Lake Formation for security, nothing fancy.
The Salesforce side is where it gets interesting. The Data 360 MCP Server  (Developer Preview) exposes the Data 360 Connect API as MCP tools. Rather than clicking through the Salesforce UI, we point Kiro at the MCP server and let it drive the connector setup programmatically.

How the MCP automation works

The Data 360 MCP Server wraps roughly 190 REST API operations behind three facade tools (search, payload_examples, execute). For our use case, the relevant operations are:
  • d360_connection_create: registers the Glue Iceberg REST endpoint with credentials
  • d360_connection_test: validates connectivity end-to-end (SigV4 auth, catalog access, Lake Formation permissions)
  • d360_connection_objects_list: confirms the Iceberg table is discoverable through the connector
We encoded this workflow as a Kiro skill, a markdown file in .kiro/skills/ that gives Kiro structured instructions for a multi-step task. This skill can be used by Claude, Agentforce Vibes, or any other agentic tool of course. The skill loads credentials from a .env file (populated by the deploy script), checks for an existing connection, creates one if needed, tests it, and verifies table visibility - .kiro/skills/configure-data360-connector.md .
In practice, you deploy the AWS stacks, then tell Kiro: "Set up the Data 360 connector." It reads the skill, loads your credentials, and executes the MCP calls. The connection is created, tested, and verified in about 10 seconds without opening a browser.

What a Kiro skill adds over raw MCP

You could call the MCP tools manually. The skill adds:
  • Sequencing with precondition checks: it verifies the .env exists and stacks are deployed before attempting connector creation
  • Idempotency: it checks for an existing connection first, skipping creation if one already exists
  • Credential injection: it maps environment variables to the correct parameter structure without the user constructing JSON payloads
  • Error context: troubleshooting steps for common failures (expired keys, missing permissions, table not visible)
The skill is declarative. It describes what to do, not how to manipulate the UI. Kiro interprets it in context and adapts if something changes (a different connection name, a different database, and so on).

The last remaining UX piece

One step remains manual: creating the data stream itself. This is the operation that binds a specific table in the connector to a Data Lake Object in Data 360.
The Data 360 Connect REST API (POST /ssot/data-streams) does not currently support creating file federation data streams for the AWS Glue connector (GLUE_IRC). The API requires an advancedAttributes.schema field, but AWS Glue Data Catalog only supports single-level namespaces . In Glue, the database is the schema, and there's no second level to reference.
The Salesforce UI handles this correctly through an internal controller that bypasses the REST API and passes externalSchemaName: null. The REST API will need to accept null or omitted schema values for single-level namespace catalogs to enable programmatic creation.
We've filed this as a PR and issue on the d360-mcp-server repo . When the API adds support, the MCP server will work without further changes (we've already fixed a validation annotation that was incorrectly blocking the request at the MCP layer). We expect to update this project to fully automate data stream creation once the API supports it.
In the meantime, the data stream is just five clicks in the UI: select the connection, database, and table, set a primary key, and deploy.

The technical architecture

Here's what gets deployed end-to-end:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CDK (3 stacks) Data 360 MCP (Kiro skill)
───────────────────────────────────── ───────────────────────────────
Foundation Stack d360_connection_create
└─ LF admin settings └─ GLUE_IRC connector
catalogUrl, catalogId,
Iceberg Federation Stack signingRegion, credentials
├─ S3 bucket (Parquet storage)
├─ Glue database (iceberg_db) d360_connection_test
├─ Athena workgroup └─ validates SigV4 + LF grants
└─ IAM policy (GetDataAccess)
d360_connection_objects_list
Athena (between stacks) └─ confirms "churn" visible
└─ CREATE TABLE ... TBLPROPERTIES
('table_type'='ICEBERG') Data Stream (UI, 5 clicks)
└─ INSERT INTO ... VALUES (...) └─ binds table → DLO

Lake Formation Stack
├─ RegisterResource (S3 location)
├─ IAM role (credential vending)
└─ PrincipalPermissions (SELECT)
At runtime, Data 360 queries flow through:
  1. Data 360 SQL to the Glue Iceberg REST endpoint (GET /v1/catalogs/{id}/namespaces/{db}/tables/{table})
  2. Glue returns table metadata (the Iceberg manifest location in S3)
  3. Data 360 requests temporary credentials via Lake Formation GetDataAccess
  4. Lake Formation vends scoped credentials for the registered S3 prefix
  5. Data 360 reads Parquet files directly from S3
No data copies. No ETL. The Iceberg table stays in your S3 bucket, under your governance, queryable from Data 360 in real time.

Key design decisions

Three stacks, not one. Lake Formation registration blocks IAM-based S3 writes. The Iceberg table must exist (written by Athena through normal IAM) before registration. Separate stacks enforce this ordering through CDK dependencies.
CfnPrincipalPermissions** over CfnPermissions.** The v1 Lake Formation API has a chicken-and-egg problem: the execution role must already be a LF admin to grant permissions. The v2 API (CfnPrincipalPermissions) works correctly once the Foundation stack establishes the admin.
Kiro skill over a script. A bash script with cur
l calls would work, but it's brittle: hardcoded URLs, no error handling, no idempotency. The skill is declarative, adapts to the current org state, and benefits from Kiro's ability to inspect responses and retry or troubleshoot.
MCP over Metadata API. Salesforce's Metadata API can deploy data stream definitions, but it requires constructing XML packages, managing definition IDs, and a multi-step deploy-then-POST workflow. The MCP server reduces this to a single tool call with a JSON payload.

Running it yourself

1
2
3
4
5
6
7
8
9
10
11
12
13
git clone https://github.com/YOUR_ORG/s3-iceberg-zero-copy
cd s3-iceberg-zero-copy

# Deploy AWS (creates .env with credentials)
./scripts/deploy.sh

# In Kiro: "Set up the Data 360 connector"
# (requires Data 360 MCP server configured in ~/.kiro/settings/mcp.json)

# Create data stream in Salesforce UI (5 clicks)

# Verify everything works
pytest tests/test_acceptance.py -v
The acceptance tests verify both sides: AWS infrastructure (table exists, 20 rows, correct schema) and Data 360 federation (stream is ACTIVE, SQL returns data, zero rows ingested, confirming zero copy).

What's next

  • Full data stream automation: once the REST API supports schema=null for single-level namespace catalogs, we'll update the skill to create the data stream programmatically
  • Multi-table support: parameterize the skill to handle multiple tables from the same Glue database
  • Cross-account federation: extend the CDK to support Glue resource links and cross-account Lake Formation grants
The Headless Data 360 capabilities (MCP plus Connect API) are moving fast. The pattern of combining CDK for AWS infrastructure with MCP-driven Salesforce configuration, orchestrated by an AI agent through a declarative skill, is how we think multi-cloud automation should work: reproducible, inspectable, and progressively more automated as the APIs mature.

References

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