<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">

<channel>
	<title>AWS Big Data Blog</title>
	<atom:link href="https://aws.amazon.com/blogs/big-data/feed/" rel="self" type="application/rss+xml"/>
	<link>https://aws.amazon.com/blogs/big-data/</link>
	<description>Official Big Data Blog of Amazon Web Services</description>
	<lastBuildDate>Fri, 28 Aug 2026 16:25:02 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Razor Group’s journey to a modern data lakehouse on AWS</title>
		<link>https://aws.amazon.com/blogs/big-data/razor-groups-journey-to-a-modern-data-lakehouse-on-aws/</link>
		
		<dc:creator><![CDATA[Yaswanth Kothainti]]></dc:creator>
		<pubDate>Fri, 28 Aug 2026 16:25:02 +0000</pubDate>
				<category><![CDATA[Amazon Redshift]]></category>
		<category><![CDATA[Amazon S3 Tables]]></category>
		<category><![CDATA[Customer Solutions]]></category>
		<guid isPermaLink="false">16ed8f6a783968433a35636d38a49b0ef357f218</guid>

					<description>Razor Group, one of Europe's leading ecommerce aggregators managing 250+ brands, migrated from always-on Amazon Redshift clusters to an open lakehouse on Apache Iceberg, Amazon S3 Tables, and Apache Spark. Learn the architectural decisions, the five-phase migration, and the results: 65% faster P95 queries and a 63% infrastructure cost reduction.</description>
										<content:encoded>&lt;p&gt;Razor Group is one of Europe’s leading ecommerce aggregators, operating 250+ brands across multiple global marketplaces. With a portfolio exceeding $400M in revenue, the company relies on data to power every critical business decision, from dynamic pricing and inventory optimization to advertising spend and supply chain orchestration.&lt;/p&gt; 
&lt;p&gt;At the heart of this operation sits the Razor Operating System (ROS), a proprietary platform that processes 370M+ API calls monthly through 9,300+ data pipelines, transforming marketplace signals into automated actions at scale.&lt;/p&gt; 
&lt;p&gt;In this post, we share how Razor Group optimized their data platform by implementing a &lt;a href="https://aws.amazon.com/what-is/data-lakehouse/" target="_blank" rel="noopener"&gt;lakehouse architecture&lt;/a&gt; on AWS. We cover the architectural decisions, the phased migration approach, and the measurable business outcomes. Whether you’re looking to optimize workload performance, reduce infrastructure costs, or unlock multi-engine flexibility for your analytics, this blueprint provides actionable insights you can adapt for your organization.&lt;/p&gt; 
&lt;h2 id="the-business-challenge-scaling-data-infrastructure-for-hypergrowth"&gt;The business challenge: Scaling data infrastructure for hypergrowth&lt;/h2&gt; 
&lt;p&gt;As Razor Group’s brand portfolio expanded rapidly, the demands on their data platform grew significantly. The company needed their analytics infrastructure to keep pace with the speed of ecommerce, where pricing decisions, stock replenishment, and advertising bids happen in near real time.&lt;/p&gt; 
&lt;p&gt;Their existing architecture, built on Amazon Redshift provisioned clusters, had served them well during earlier growth stages. As workloads diversified and data volumes surged, several optimization opportunities emerged:&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5931-1.png" alt="Razor Operating System data architecture before the migration: signal sources such as Amazon Selling Partner API, Shopify, NetSuite, Walmart, and Target ingested through AWS Lambda and Amazon MSK, stored in Amazon S3 and Amazon DynamoDB, modeled in Amazon Redshift, and consumed by ML notebooks, ML jobs on AWS Batch, and Tableau dashboards, orchestrated by Apache Airflow" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: The Razor Operating System data architecture before the migration&lt;/p&gt;
&lt;/div&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Workload contention&lt;/strong&gt;: Over 1,000 SQL models for ETL, transformation, and analytics competed for the same compute resources, creating resource contention during peak processing windows.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Cost-to-utilization mismatch&lt;/strong&gt;: Always-on clusters ran 24/7, but workload analysis revealed that 98% of compute demand came from batch ETL rather than interactive analytics, which resulted in significant idle capacity during off-peak hours.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Data freshness gaps&lt;/strong&gt;: Batch-oriented pipelines delivered data with 4–6 hour latency, limiting the team’s ability to react to fast-moving marketplace dynamics.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Scaling constraints&lt;/strong&gt;: As concurrent users and pipeline complexity grew, vertical scaling alone couldn’t address the need for workload isolation and elastic capacity.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;These weren’t failures of any single service. They were signals that the architecture needed to evolve to match the scale and diversity of Razor Group’s workloads.&lt;/p&gt; 
&lt;h2 id="why-a-lakehouse-architecture"&gt;Why a lakehouse architecture?&lt;/h2&gt; 
&lt;p&gt;Rather than replacing their existing investments, Razor Group recognized the opportunity to &lt;strong&gt;optimize workload placement&lt;/strong&gt; by adopting a modern lakehouse architecture. The core principles driving this decision:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Open table formats&lt;/strong&gt;: Apache Iceberg provides ACID transactions, time travel, and schema evolution. Data is stored once and accessed by any compatible engine without duplication.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Elastic, per-workload scaling&lt;/strong&gt;: With data persisted on Amazon Simple Storage Service (Amazon S3), each engine independently scales compute to match its workload. Each engine spins up for peak processing and scales to zero when idle, without over-provisioning shared infrastructure.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Multi-engine flexibility&lt;/strong&gt;: Different workloads have different requirements. Heavy ETL benefits from distributed Spark processing, ad hoc exploration from serverless queries, and business intelligence (BI) dashboards from high-performance warehouse engines, each optimized for its purpose.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;This approach allowed Razor Group to &lt;strong&gt;right-size each workload to the best-fit engine&lt;/strong&gt; while maintaining a single, governed copy of data accessible across the entire platform.&lt;/p&gt; 
&lt;h2 id="solution-overview"&gt;Solution overview&lt;/h2&gt; 
&lt;p&gt;Razor Group partnered with AWS to implement a comprehensive lakehouse architecture that brings together multiple AWS services, each playing a complementary role:&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5931-2.png" alt="New lakehouse architecture on AWS: the same signal sources ingested through AWS Lambda and Amazon MSK, stored and modeled as Bronze, Silver, and Gold Apache Iceberg tables using Apache Spark Connect on Amazon EC2 with AWS Lake Formation and AWS Glue Data Catalog, served through Amazon Redshift, and consumed by ML notebooks, ML jobs on AWS Batch, and Tableau dashboards" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: End-to-end lakehouse architecture on AWS&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="designing-for-scale-the-lakehouse-vision"&gt;Designing for scale: The lakehouse vision&lt;/h2&gt; 
&lt;p&gt;The core insight driving Razor Group’s new architecture was simple: build a single, open format data lake that any engine can query. In the old model, each tool maintained its own copy of the data. In the new model, a single open-format data lake on Amazon S3 serves as the source of truth, and multiple purpose-built compute engines read from it based on the workload at hand.&lt;/p&gt; 
&lt;p&gt;This shift, commonly called a lakehouse architecture, combines the cost economics and scalability of a data lake with the query performance and governance of a data warehouse. Its open table format, &lt;strong&gt;Apache Iceberg&lt;/strong&gt;, provides ACID transactions, schema evolution, time travel, and no vendor lock-in.&lt;/p&gt; 
&lt;h3 id="storage-and-governance-the-open-data-foundation"&gt;Storage and governance: The open data foundation&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon S3 Tables (a capability of Amazon S3) with Apache Iceberg&lt;/strong&gt; — The primary storage layer, providing open-format tables with ACID transactions, partition evolution, and time travel. Data is stored once and accessible by any Iceberg-compatible engine.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Glue Data Catalog&lt;/strong&gt; — A unified metadata repository for consistent data discovery across all compute engines.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Lake Formation&lt;/strong&gt; — Fine-grained access control with column-level and row-level security so that governance scales with the platform.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3 id="compute-right-engine-for-the-right-workload"&gt;Compute: Right engine for the right workload&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Apache Spark on Amazon Elastic Compute Cloud (Amazon EC2)&lt;/strong&gt; — Elastic, distributed compute for heavy ETL and transformation workloads. It uses AWS Graviton instances and Amazon EC2 Spot Instances for cost optimization.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon Athena&lt;/strong&gt; — Serverless SQL for ad hoc exploration and lightweight queries directly on Iceberg tables, with no infrastructure to manage.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon Redshift Serverless&lt;/strong&gt; — High-performance serving layer for BI dashboards, Tableau workloads, and interactive analytics. Amazon Redshift Serverless automatically scales to meet demand and pauses when idle, so it stays cost-efficient for the analytics workloads it serves best.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3 id="orchestration-and-observability"&gt;Orchestration and observability&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Apache Airflow&lt;/strong&gt; — Pipeline orchestration that manages 9,300+ data pipelines with dependency tracking and service level agreement (SLA) monitoring.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Comprehensive observability stack&lt;/strong&gt; — Cost attribution, pipeline health monitoring, and data quality checks across all layers.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When the architecture was originally designed, Amazon Redshift lacked Iceberg write support, making self-managed Spark the only viable ingestion path. This constraint has since been removed. &lt;a href="https://aws.amazon.com/blogs/big-data/getting-started-with-apache-iceberg-write-support-in-amazon-redshift-part-1/" target="_blank" rel="noopener"&gt;Amazon Redshift now supports full Apache Iceberg DML&lt;/a&gt; (UPDATE, DELETE, MERGE), complementing its earlier CREATE/INSERT capabilities and AWS Glue Iceberg materialized views. This makes it a complete read/write Iceberg engine.&lt;/p&gt; 
&lt;h2 id="migration-approach"&gt;Migration approach&lt;/h2&gt; 
&lt;p&gt;Rather than a risky big-bang cutover, Razor Group adopted a phased migration of five stages, each delivering standalone value while building the foundation for the next. Both Amazon Redshift and Spark pipelines ran in parallel during the transition, which maintained business continuity and let the team compare outputs with confidence. At no point was a production pipeline paused or a dashboard unavailable.&lt;/p&gt; 
&lt;h2 id="the-migration-journey-five-phases"&gt;The migration journey: Five phases&lt;/h2&gt; 
&lt;p&gt;The migration unfolded across five structured phases, each building on the previous one and delivering incremental value before the next began.&lt;/p&gt; 
&lt;h2 id="phase-1-establish-the-lakehouse-foundation"&gt;Phase 1: Establish the lakehouse foundation&lt;/h2&gt; 
&lt;p&gt;Before migrating a single query, Razor Group needed to answer three questions: where does the data live, how is it managed, and how do we query it?&lt;/p&gt; 
&lt;h3 id="why-s3-tables-over-self-managed-iceberg"&gt;Why S3 Tables over self-managed Iceberg&lt;/h3&gt; 
&lt;p&gt;Razor Group had already committed to Apache Iceberg as the table format: open, engine-agnostic, and equipped with ACID transactions and time travel. The question was whether to self-manage Iceberg on standard S3 buckets or use Amazon S3 Tables.&lt;/p&gt; 
&lt;p&gt;Self-managed Iceberg is powerful but operationally expensive. Someone has to run compaction jobs to prevent small-file proliferation. Someone has to expire old snapshots before metadata bloat degrades query planning. Someone has to clean up orphaned data files after interrupted writes. With 700+ models running across 40+ schemas, many of them materializing multiple times per day, that maintenance burden would scale with the platform rather than shrink.&lt;/p&gt; 
&lt;p&gt;S3 Tables eliminated this entire category of work. Compaction, snapshot management, and unreferenced file removal run continuously and automatically. The integrated Iceberg REST Catalog API means any compatible engine, such as Spark, Trino, Athena, Amazon Redshift, and Flink, can discover and query tables without maintaining a separate metastore. Discovery is unified through AWS Glue Data Catalog, which now exposes the Iceberg REST Catalog protocol as its access interface. Because tables are first-class AWS resources, access control, encryption, and lifecycle policies operate at the table level rather than through complex S3 bucket policies layered on top of file-path conventions.&lt;/p&gt; 
&lt;p&gt;For a company that didn’t want the operational burden of self-managing open table format maintenance, this was the deciding factor.&lt;/p&gt; 
&lt;p&gt;AWS Glue Data Catalog provides unified metadata discovery across all tiers. Lake Formation handles column- and table-level access control, with AWS Identity and Access Management (IAM) roles that follow least-privilege principles and AWS CloudTrail turned on for a full audit trail.&lt;/p&gt; 
&lt;h3 id="choosing-the-query-protocol"&gt;Choosing the query protocol&lt;/h3&gt; 
&lt;p&gt;Prior to the rearchitecture, the Amazon Redshift cluster was 98% ETL, and only a fraction of compute hours were analyst &lt;code&gt;SELECT&lt;/code&gt; queries. The replacement engine needed to handle both heavy batch transformations and interactive ad hoc queries.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Traditional Spark&lt;/strong&gt; (spark-submit) handles batch ETL well, but couples clients to the cluster. Every job requires packaging driver JARs, managing classpaths, and submitting from within the cluster. For a platform running 200+ production directed acyclic graphs (DAGs) that process massive data volumes daily, this operational friction was a non-starter.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Spark Connect&lt;/strong&gt; is the gRPC-based client-server protocol introduced in Spark 3.4, and it solved the coupling problem entirely. The cluster runs a persistent gRPC endpoint. Clients connect remotely and submit queries over the wire. Airflow operators become thin clients: they open a session, submit SQL, and get results, with success and failure mapping directly to task states. There are no driver JARs and no polling. Multiple consumers, including pipeline orchestrators, the web application, and developer notebooks, share one cluster without any of them needing Spark installed locally.&lt;/p&gt; 
&lt;h3 id="deploying-spark-connect"&gt;Deploying Spark Connect&lt;/h3&gt; 
&lt;p&gt;Razor Group deployed a self-hosted Spark cluster on Amazon EC2: an on-demand AWS Graviton leader node, Spot workers at about 70% cost savings, and the Spark Connect endpoint exposed through an internal Network Load Balancer. Custom Amazon Machine Images (AMIs) bake in the full Spark, Iceberg, and S3 Tables stack, so private-subnet nodes have everything they need without internet access at runtime.&lt;/p&gt; 
&lt;p&gt;This phase produced no immediate business value, but it made everything that followed possible.&lt;/p&gt; 
&lt;h2 id="phase-2-migrate-data-ingestion"&gt;Phase 2: Migrate data ingestion&lt;/h2&gt; 
&lt;p&gt;Razor Group’s ingestion layer pulls data from Amazon Selling Partner API, Seller Central portals, NetSuite ERP, and custom web scrapers. In the previous architecture, all of this landed in Amazon Redshift through COPY commands, which meant data freshness was dictated by batch job schedules and competed for resources on the same cluster that served analytical queries.&lt;/p&gt; 
&lt;p&gt;Razor Group migrated these pipelines to AWS Lambda functions orchestrated by Apache Airflow, writing data directly to S3 Tables in Iceberg format. The shift from schedule-driven to event-driven significantly improved freshness. Lambda functions spin up only when there’s data to process, and Airflow sensors trigger downstream transformations the moment new data lands. This replaced rigid hourly batch windows with data freshness measured in minutes.&lt;/p&gt; 
&lt;p&gt;The orchestration layer manages 200+ DAGs across 90+ flows and processes data from dozens of sources at scale. The migration required rewiring destinations from Amazon Redshift COPY to Iceberg writes, but the orchestration logic itself carried over with minimal changes.&lt;/p&gt; 
&lt;p&gt;This phase alone eliminated roughly 40% of compute costs by severing the always-on cluster dependency for ingestion.&lt;/p&gt; 
&lt;h2 id="phase-3-transform-processing-pipelines"&gt;Phase 3: Transform processing pipelines&lt;/h2&gt; 
&lt;p&gt;This was the most technically demanding phase, and where Razor Group learned the most. The team migrated 1,000+ SQL models from Amazon Redshift to Apache Spark, working incrementally up the dependency chain across 40+ schemas. The models moved through a medallion structure: Bronze for raw ingested data, Silver for cleaned and conformed data, and Gold for business-ready aggregates.&lt;/p&gt; 
&lt;p&gt;Razor Group built automated conversion tooling and a validation framework that ran both Amazon Redshift and Spark outputs in parallel, comparing results row-by-row before decommissioning anything. Several categories of transformation pushed the limits of what automation could handle:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Window functions&lt;/strong&gt;: The QUALIFY clause in Amazon Redshift has no Spark equivalent. Each instance required wrapping in a subquery with explicit row numbering, which affected dozens of models in the inventory schema alone.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;JSON serialization&lt;/strong&gt;: The most time-consuming category. Complex columns stored as JSON STRING in Amazon Redshift needed &lt;code&gt;from_json()&lt;/code&gt; with hand-written STRUCT definitions in Spark. Every nested payload column across ads, orders, and transaction pipelines required schema introspection, with no shortcuts.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Function dialect&lt;/strong&gt;: More than 20 function-level conversions, including NVL to COALESCE, DATEADD to interval arithmetic, and LISTAGG to ARRAY_JOIN(COLLECT_LIST()).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Snapshot elimination&lt;/strong&gt;: The single biggest hidden cost. Full table copies that ran multiple times daily only to preserve point-in-time state consumed more than 35 hours of weekly Amazon Redshift compute. With Iceberg’s native time travel, these became zero-cost operations overnight.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;When migrating 1,000+ SQL models, automated tooling handles the mechanical syntax conversions well. But roughly 30% of the models required human judgment: those with complex JSON payloads, deeply nested window functions, or cross-schema snapshot dependencies. These models consumed 70% of the migration effort.&lt;/p&gt; 
&lt;p&gt;Razor Group built a structured migration workflow that used Claude to accelerate this work: read source SQL, identify dependencies, convert syntax, resolve missing base tables, add JSON parsing, validate outputs, and write to the lakehouse. The system did more than translate SQL. It applied schema context, traced cross-model dependencies, and flagged edge cases that would have taken engineers hours to find manually. What could have been a multi-year effort became a systematic, repeatable process measured in weeks. This approach fundamentally changed the speed of migration.&lt;/p&gt; 
&lt;h2 id="phase-4-unify-the-serving-layer"&gt;Phase 4: Unify the serving layer&lt;/h2&gt; 
&lt;p&gt;With data flowing through Iceberg tables, Razor Group collapsed the serving layer. End users query Gold-layer Iceberg tables through Amazon Redshift Serverless, and internal exploration and machine learning (ML) workloads read the same tables through Spark Connect. This removed the need to maintain separate data copies, materialized views, or extract jobs for different consumers.&lt;/p&gt; 
&lt;p&gt;This is the strategic payoff of an open table format. Iceberg tables on S3 are engine-agnostic: Spark for batch transforms today, Trino for interactive queries tomorrow, Flink for streaming next quarter. Any engine that speaks Iceberg can read the data without conversion or migration. Razor Group went from being locked into a single vendor’s SQL dialect to having the freedom to adopt new engines without touching the storage layer.&lt;/p&gt; 
&lt;h2 id="phase-5-operationalize-and-observe"&gt;Phase 5: Operationalize and observe&lt;/h2&gt; 
&lt;p&gt;The final phase made the lakehouse production-grade. Razor Group built a comprehensive observability stack that aggregates metrics, traces, and logs from every pipeline component into a unified view. This view supports centralized log search, anomaly detection, and automated alerting that correlates failures across the entire data platform.&lt;/p&gt; 
&lt;p&gt;This observability layer did more than provide visibility. It gave the team confidence. When you’re running thousands of pipeline executions daily, you need to know within minutes when something breaks, what caused it, and which downstream consumers are affected. That’s the difference between reactive firefighting and proactive operations.&lt;/p&gt; 
&lt;p&gt;Pipeline orchestration consolidated around three patterns: a daily pipeline (ingestion to materialization to export to AI agent analysis), an operations worker polling every 15 minutes, and weekly scraper jobs.&lt;/p&gt; 
&lt;p&gt;The cutover was zero-downtime by design: both schedulers ran in parallel for two weeks. Automated comparison checks validated that every pipeline produced identical outputs before the prior architecture system was disabled.&lt;/p&gt; 
&lt;h2 id="results-and-business-impact"&gt;Results and business impact&lt;/h2&gt; 
&lt;p&gt;The lakehouse architecture delivered measurable improvements across every dimension:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Metric&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Improvement&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;P95 query runtime&lt;/td&gt; 
   &lt;td&gt;180 seconds&lt;/td&gt; 
   &lt;td&gt;63 seconds&lt;/td&gt; 
   &lt;td&gt;65% faster&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Infrastructure cost&lt;/td&gt; 
   &lt;td&gt;Always-on provisioned clusters&lt;/td&gt; 
   &lt;td&gt;Elastic, workload-optimized&lt;/td&gt; 
   &lt;td&gt;63% reduction&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Data freshness&lt;/td&gt; 
   &lt;td&gt;4–6 hour batch cycles&lt;/td&gt; 
   &lt;td&gt;Event-driven pipelines&lt;/td&gt; 
   &lt;td&gt;15-minute freshness&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Concurrent capacity&lt;/td&gt; 
   &lt;td&gt;Limited by cluster size&lt;/td&gt; 
   &lt;td&gt;Elastic, independent scaling&lt;/td&gt; 
   &lt;td&gt;Unlimited&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Engine flexibility&lt;/td&gt; 
   &lt;td&gt;Single engine&lt;/td&gt; 
   &lt;td&gt;Multi-engine (Spark, Athena, Amazon Redshift)&lt;/td&gt; 
   &lt;td&gt;Open format portability&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The 63% reduction compares the lakehouse run-rate (January–March 2026) with the pre-rearchitecture run-rate (October–December 2025), the trailing three months before the rearchitecture. The figure is an apples-to-apples blended infrastructure number that includes compute and storage across both architectures. The before column covers Amazon Redshift cluster compute and managed storage. The after column covers Amazon EC2 (Spark workers, both on-demand and Spot), AWS Lambda, AWS Glue, Amazon Athena, Amazon Redshift Serverless, and S3 Tables storage. Data-transfer and ancillary services are excluded because they were not materially different between the two periods. Workload mix (the number of pipelines, models, and end-user query volume) was held broadly comparable across the two windows.&lt;/p&gt; 
&lt;h2 id="lessons-learned"&gt;Lessons learned&lt;/h2&gt; 
&lt;p&gt;&lt;strong&gt;Start with the decision loops, not the tools, and know your workload before you replace your warehouse.&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;The most valuable activity of the entire migration wasn’t writing a line of code. It was the Amazon Redshift workload analysis we ran before making any architectural decisions. Discovering that 98% of compute was ETL, with only a sliver going to analyst queries, validated the move to on-demand Spark. It also prevented us from over-provisioning the replacement infrastructure for interactive workloads that barely existed. Architecture decisions should always trace back to core business requirements: pricing accuracy, promotional responsiveness, intraday P&amp;amp;L visibility. Start there, not with the technology.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Design for multiple compute engines, and choose the right engine per workload.&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;One of the clearest lessons from running a single-engine architecture is what you give up. Avoid locking yourself into one compute layer for BI, ingestion, backfills, and ML alike, because they have fundamentally different cost and performance profiles. Iceberg, Spark, and S3 Tables work well together out of the box once you make the shift. The technology isn’t the hard part. The hard part is mapping 1,000+ models across 40+ schemas, tracing dependencies through 200+ DAGs, and discovering that a column is actually a JSON string silently serialized differently between two engines. Migration is as much an excavation project as an engineering one.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Automate conversion, but budget for the 30%.&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Automated tooling handles mechanical syntax conversions well, and it should be the first tool you reach for. But models with complex JSON payloads, deeply nested window functions, or cross-schema snapshot dependencies require human judgment, and that work doesn’t compress. Roughly 30% of our models needed significant manual intervention, and those models consumed 70% of the total migration effort. Plan for it honestly from the start.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Observability must include cost attribution, and watch out for hidden cost bombs.&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Snapshot operations were our biggest surprise. Full table copies that ran multiple times daily to preserve point-in-time state were costing more than 35 hours of weekly compute, and nobody questioned it because “that’s how snapshots work.” Iceberg’s time-travel capability eliminated their cost, and that single feature justified a meaningful portion of the migration on its own. More broadly, you cannot optimize what you cannot see, so track query-level usage and attribute it to teams and functions. Cost observability is not a nice-to-have. It’s foundational.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Governance isn’t optional. Build it into the foundation, and align stakeholders from day one.&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Catalog and access control need to come first, before you scale adoption, not after. The same principle applies to people: migration is a cross-functional program, not an infrastructure project. Our two-week parallel run caught edge cases that row-level validation missed entirely: time zone differences between Amazon Redshift and Spark, partition pruning behavior under concurrent writes, and subtle ordering differences in non-deterministic window functions. That parallel run wasn’t a safety net. It was where the migration actually proved itself. None of it works without the right stakeholders involved and aligned from the very beginning.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Razor Group’s journey offers valuable lessons for organizations looking to optimize their data architectures:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Analyze your workload mix first.&lt;/strong&gt; Understanding that 98% of compute was ETL rather than interactive queries guided the decision to offload heavy processing to elastic Spark, while preserving &lt;a href="https://aws.amazon.com/redshift/" target="_blank" rel="noopener"&gt;Amazon Redshift&lt;/a&gt; Serverless for the interactive analytics it handles best.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Design for multi-engine flexibility.&lt;/strong&gt; Open table formats like &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/apache-iceberg-on-aws/introduction.html" target="_blank" rel="noopener"&gt;Apache Iceberg&lt;/a&gt; eliminate the need to choose a single engine. Each workload runs on the engine best suited to its access pattern, cost profile, and performance requirements.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Automate migration, but budget for complexity.&lt;/strong&gt; Automated transpilation handled 70% of SQL models, but the remaining 30% consumed 70% of engineering effort. Plan accordingly.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Observability must include cost attribution.&lt;/strong&gt; Without per-workload cost visibility, optimization is guesswork. Razor Group discovered that Iceberg snapshot maintenance alone consumed more than 35 hours of compute weekly, a hidden cost that observability surfaced and automation resolved.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Build governance into the foundation.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/lake-formation/" target="_blank" rel="noopener"&gt;AWS Lake Formation&lt;/a&gt; and AWS Glue Data Catalog provided fine-grained access control from day one, not retrofitted after the migration.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Validate with parallel systems.&lt;/strong&gt; A two-week parallel run between old and new architectures caught edge cases that automated testing missed, which supported a confident production cutover.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="the-road-ahead"&gt;The road ahead&lt;/h2&gt; 
&lt;p&gt;With the lakehouse foundation in place, Razor Group is positioned to accelerate innovation, from real-time pricing models to AI-driven inventory optimization, all powered by a unified, open, and governed data platform on AWS.&lt;/p&gt; 
&lt;p&gt;The company’s transformation demonstrates that modern data architectures aren’t about choosing between services. They’re about &lt;strong&gt;placing each workload where it performs best&lt;/strong&gt;, using open formats to eliminate silos, and scaling each layer independently as the business grows.&lt;/p&gt; 
&lt;p&gt;To learn how other organizations are implementing similar lakehouse architectures on AWS, see &lt;a href="https://aws.amazon.com/blogs/big-data/how-bigbasket-uses-the-iceberg-based-lakehouse-architecture-on-aws-to-power-lightning-fast-grocery-delivery-across-india/" target="_blank" rel="noopener"&gt;How BigBasket uses the Iceberg-based lakehouse architecture on AWS to power lightning-fast grocery delivery across India&lt;/a&gt;.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="alignleft size-full wp-image-93832" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/Yaswanth.png" alt="" width="100" height="133"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Yaswanth Kothainti&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/yaswanth-kothainti/" target="_blank" rel="noopener"&gt;Yaswanth&lt;/a&gt; is VP of Data Engineering &amp;amp; Platform at Razor Group, a $400M+ ecommerce enterprise, where he built the company’s data platform from the ground up and leads a 65-member global engineering organization. His core expertise spans enterprise data platforms, data governance, FinOps, and agentic AI systems, with a track record of translating complex platform investments into measurable business outcomes.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2023/09/06/shubham-100.jpeg" alt="Shubham Purwar" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Shubham Purwar&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/shubham-purwar/" target="_blank" rel="noopener"&gt;Shubham&lt;/a&gt; is an Analytics Specialist Solutions Architect at AWS. He helps organizations unlock the full potential of their data by designing and implementing scalable, secure, and high-performance analytics solutions on AWS. In his free time, Shubham loves to spend time with his family and travel around the world.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5931-3.jpg" alt="Ravi Kompella" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ravi Kompella&lt;/h3&gt; 
  &lt;p&gt;&lt;span&gt;&lt;a href="https://www.linkedin.com/in/ravikompella/" target="_blank" rel="noopener noreferrer"&gt;Ravi&lt;/a&gt;&lt;/span&gt; is Principal Analytics Specialist with experience in driving adoption of modern data architectures, enterprise data lakehouses, and real-time data systems across multiple industry verticals in India across all segments including startups and SaaS providers.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>How Picnic configured multiple OAuth providers for Amazon MQ</title>
		<link>https://aws.amazon.com/blogs/big-data/how-picnic-configured-multiple-oauth-providers-for-amazon-mq/</link>
		
		<dc:creator><![CDATA[Oscar Mapfumo Sibanda]]></dc:creator>
		<pubDate>Thu, 27 Aug 2026 16:07:20 +0000</pubDate>
				<category><![CDATA[Advanced (300)]]></category>
		<category><![CDATA[Amazon MQ]]></category>
		<category><![CDATA[Customer Solutions]]></category>
		<guid isPermaLink="false">c150e659af2e19cd7a47fa85f62817d0fa30fb6a</guid>

					<description>Picnic runs RabbitMQ as the messaging backbone for hundreds of microservices on Amazon MQ for RabbitMQ. This post shows how to configure one broker to trust multiple OAuth 2.0 identity providers, Keycloak for operators and AWS IAM for services, so you can eliminate static credentials while maintaining separate identity paths for people and workloads.</description>
										<content:encoded>&lt;p&gt;&lt;em&gt;This post is co-written with Oscar Mapfumo Sibanda from Picnic.&lt;/em&gt;&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://picnic.app/nl/" target="_blank" rel="noopener"&gt;Picnic&lt;/a&gt; is an Amsterdam-based tech scale-up that reinvents how people buy food. It isn’t a supermarket with a digital layer but a tech company that happens to deliver groceries. Picnic is engineered in-house: the customer app, the fulfillment platform, the supply chain, and the routing technology that guides a fleet of thousands of electric vehicles through the Netherlands, Germany, and France. Software doesn’t merely support business. Software is a business.&lt;/p&gt; 
&lt;p&gt;At the center of this system, &lt;a href="https://www.rabbitmq.com/" target="_blank" rel="noopener"&gt;RabbitMQ&lt;/a&gt; is the core component. It’s the communication backbone connecting hundreds of microservices across the entire business lifecycle, from ordering and logistics to delivery and finance. At peak, Picnic’s platform processes close to one million messages per second. At this scale, messaging is no longer only background infrastructure. It becomes part of the company’s operational nervous system. To keep that system highly available, scalable, and resilient as Picnic grows, the company decided to use &lt;a href="https://aws.amazon.com/amazon-mq/" target="_blank" rel="noopener"&gt;Amazon MQ&lt;/a&gt; as a managed service.&lt;/p&gt; 
&lt;p&gt;The next challenge was identity. Picnic’s authentication strategy clearly distinguishes between people and services. Operators sign in through &lt;a href="https://www.keycloak.org/" target="_blank" rel="noopener"&gt;Keycloak&lt;/a&gt;, the company’s single sign-on provider, while Picnic’s &lt;a href="https://aws.amazon.com/eks/" target="_blank" rel="noopener"&gt;Amazon Elastic Kubernetes Service&lt;/a&gt; (Amazon EKS) workloads are adopting &lt;a href="https://aws.amazon.com/iam/" target="_blank" rel="noopener"&gt;AWS Identity and Access Management&lt;/a&gt; (IAM) authentication to eliminate static credentials. A single broker therefore must trust two identity providers at once. The Amazon MQ documentation covers &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/oauth-tutorial.html" target="_blank" rel="noopener"&gt;configuring OAuth 2.0&lt;/a&gt; with a single provider. This post extends that guidance to a multi-provider setup on the same broker.&lt;/p&gt; 
&lt;p&gt;In this post, we show how Picnic solved that problem. You will learn how to configure an Amazon MQ for RabbitMQ broker to accept tokens from multiple OAuth 2.0 identity providers, using Keycloak and IAM as the working example. You will also see how to map each provider’s scopes to RabbitMQ permissions and how to roll the change out on a running broker without disrupting connected users.&lt;/p&gt; 
&lt;h2 id="background-and-prerequisites"&gt;Background and prerequisites&lt;/h2&gt; 
&lt;p&gt;Amazon MQ for RabbitMQ supports &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/oauth-for-amq-for-rabbitmq.html" target="_blank" rel="noopener"&gt;OAuth 2.0 authentication and authorization&lt;/a&gt;, where broker users and their permissions are managed by an external identity provider. User authentication and resource permissions for vhosts, exchanges, queues, and topics are centralized through the OAuth 2.0 provider’s scope system.&lt;/p&gt; 
&lt;p&gt;RabbitMQ’s OAuth 2.0 plugin supports &lt;a href="https://www.rabbitmq.com/docs/4.2/oauth2-examples-multiresource" target="_blank" rel="noopener"&gt;multiple resource servers and audiences&lt;/a&gt;, allowing different OAuth 2.0 providers to issue tokens that a single broker can validate. This capability is essential if you operate in multiple environments or have teams registered with separate identity providers.&lt;/p&gt; 
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt; 
&lt;p&gt;To follow along with this post, you need:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;An active AWS account.&lt;/li&gt; 
 &lt;li&gt;An Amazon MQ for RabbitMQ broker with OAuth 2.0 configured for at least one identity provider (see &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/oauth-tutorial.html" target="_blank" rel="noopener"&gt;Using OAuth 2.0 authentication and authorization for Amazon MQ for RabbitMQ&lt;/a&gt;).&lt;/li&gt; 
 &lt;li&gt;A second OAuth 2.0 identity provider configured and operational.&lt;/li&gt; 
 &lt;li&gt;Outbound web identity federation &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_outbound_getting_started.html" target="_blank" rel="noopener"&gt;enabled for your AWS account&lt;/a&gt; (if using IAM as a provider).&lt;/li&gt; 
 &lt;li&gt;Basic familiarity with RabbitMQ configuration and OAuth 2.0 concepts.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/cli/" target="_blank" rel="noopener"&gt;AWS Command Line Interface&lt;/a&gt; (AWS CLI) version 2.27 or later (required for the &lt;code&gt;get-web-identity-token&lt;/code&gt; command used in the testing section).&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The information in this post reflects Amazon MQ for RabbitMQ features and behavior at the time of publication. We recommend checking the Amazon MQ &lt;a href="https://docs.aws.amazon.com/amazon-mq/" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-release-notes.html" target="_blank" rel="noopener"&gt;release notes&lt;/a&gt; and best practices before implementation.&lt;/p&gt; 
&lt;h2 id="solution-architecture"&gt;Solution architecture&lt;/h2&gt; 
&lt;p&gt;The design rests on a single idea: a RabbitMQ broker can trust &lt;a href="https://www.rabbitmq.com/docs/oauth2-examples-multiresource" target="_blank" rel="noopener"&gt;more than one identity provider&lt;/a&gt; at the same time, and it decides which one to apply per token rather than per broker. RabbitMQ does this by reading the &lt;code&gt;aud&lt;/code&gt; (audience) claim of each incoming token and matching it against a configured resource server. Each resource server is bound to one OAuth 2.0 provider, so the audience determines both which signing keys validate the token and which permission rules apply.&lt;/p&gt; 
&lt;h3 id="architecture-diagram"&gt;Architecture diagram&lt;/h3&gt; 
&lt;p&gt;The following two diagrams show how services and operators authenticate the broker through their respective identity providers.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6191-1.png" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6191-1.png" alt="Services authentication flow from an Amazon EKS workload through AWS STS to the Amazon MQ for RabbitMQ broker over AMQPS" width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 1: Services (IAM) flow&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Services authenticate through IAM. An Amazon EKS workload assumes an IAM role and generates a web identity token (1), which &lt;a href="https://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html" target="_blank" rel="noopener"&gt;AWS Security Token Service&lt;/a&gt; (AWS STS) issues with an audience of &lt;code&gt;rabbitmq-iam&lt;/code&gt; (2). The workload presents that token as its password when it connects to the broker over &lt;a href="https://www.rabbitmq.com/tutorials/amqp-concepts#what-is-amqp" target="_blank" rel="noopener"&gt;Advanced Message Queuing Protocol&lt;/a&gt; (AMQPS) on port 5671 (3). The broker selects the matching resource server, verifies the token’s signature against the AWS STS signing keys (4), and maps the role’s Amazon Resource Name (ARN) to the permissions the workload needs (5).&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6191-2.png" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6191-2.png" alt="Operators authentication flow from the RabbitMQ management console through Keycloak single sign-on to the broker" width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 2: Operators (Keycloak) flow&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Operators authenticate through Keycloak. An operator opens the RabbitMQ management console and initiates login (1), and the console redirects the browser to Keycloak (2). Keycloak authenticates the operator and issues a token whose audience targets the console resource server, &lt;code&gt;rabbitmq-keycloak&lt;/code&gt; (3). The browser presents that token to the broker (4), which verifies the signature against Keycloak’s signing keys (5). The broker then reads the operator’s group membership and grants access (6): the Operator group receives read-only permissions, while the Administrator group receives full control.&lt;/p&gt; 
&lt;p&gt;Two constraints follow this design. First, audience verification is a single broker-wide setting that applies to every provider at once, so each provider must issue tokens carrying the exact audience its resource server expects. Second, the broker is private, deployed inside an &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html" target="_blank" rel="noopener"&gt;Amazon Virtual Private Cloud&lt;/a&gt; (Amazon VPC) with no public exposure. Both providers’ endpoints must be resolvable, either through publicly addressable JSON Web Key Set (JWKS) endpoints or through &lt;a href="https://aws.amazon.com/blogs/big-data/introducing-private-networking-for-amazon-mq-for-rabbitmq/" target="_blank" rel="noopener"&gt;private networking&lt;/a&gt;, because the broker fetches signing keys from those endpoints.&lt;/p&gt; 
&lt;h2 id="implementation-walkthrough"&gt;Implementation walkthrough&lt;/h2&gt; 
&lt;p&gt;This walkthrough configures one Amazon MQ for RabbitMQ broker to trust two identity providers: Keycloak for operators and AWS IAM for services. The steps assume you already have a running broker, a Keycloak realm, and outbound web identity federation enabled for your AWS account. All configurations are applied through a RabbitMQ configuration revision using the AWS Command Line Interface (AWS CLI).&lt;/p&gt; 
&lt;h3 id="enable-oauth-2.0-on-the-broker"&gt;Enable OAuth 2.0 on the broker&lt;/h3&gt; 
&lt;p&gt;The first block activates the OAuth 2.0 backend and keeps the internal backend in place. Internal authentication remains active deliberately: Amazon MQ creates an administrator user when the broker is provisioned, and that user is needed for break-glass access.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-ini"&gt;auth_backends.1 = oauth2
auth_backends.2 = internal
auth_oauth2.verify_aud = true&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Setting &lt;code&gt;verify_aud = true&lt;/code&gt; tells RabbitMQ to reject any token whose &lt;code&gt;aud&lt;/code&gt; claim does not match a configured resource server. This single broker-wide setting governs every provider you add.&lt;/p&gt; 
&lt;h3 id="add-the-first-identity-provider-keycloak"&gt;Add the first identity provider (Keycloak)&lt;/h3&gt; 
&lt;p&gt;A resource server binds an audience value to a provider and a set of permission rules. The Keycloak resource server uses the id &lt;code&gt;rabbitmq-keycloak&lt;/code&gt;, which is the audience the realm must place in its tokens. RabbitMQ reads the operator’s group membership from the &lt;code&gt;group_membership&lt;/code&gt; claim and resolves it through scope aliases.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-ini"&gt;auth_oauth2.resource_servers.1.id = rabbitmq-keycloak
auth_oauth2.resource_servers.1.oauth_provider_id = keycloak
auth_oauth2.resource_servers.1.scope_prefix = rabbitmq.
auth_oauth2.resource_servers.1.additional_scopes_key = group_membership
auth_oauth2.resource_servers.1.preferred_username_claims.1 = email
auth_oauth2.resource_servers.1.scope_aliases.1.alias = Operator
auth_oauth2.resource_servers.1.scope_aliases.1.scope = rabbitmq.read:*/* rabbitmq.write:^$ rabbitmq.configure:^$ rabbitmq.tag:monitoring
auth_oauth2.resource_servers.1.scope_aliases.2.alias = Administrator
auth_oauth2.resource_servers.1.scope_aliases.2.scope = rabbitmq.read:*/* rabbitmq.write:*/* rabbitmq.configure:*/* rabbitmq.tag:administrator&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The Operator group is read-only: it can read any resource and view the management UI through the monitoring tag. The Administrator group receives full permissions plus the administrator tag. This role is least privilege by design.&lt;/p&gt; 
&lt;p&gt;The provider is configured with its issuer and JWKS endpoint:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-ini"&gt;auth_oauth2.oauth_providers.keycloak.https.hostname_verification = wildcard
auth_oauth2.oauth_providers.keycloak.issuer = https://keycloak.example.com/auth/realms/test
auth_oauth2.oauth_providers.keycloak.jwks_uri = https://keycloak.example.com/auth/realms/test/protocol/openid-connect/certs&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;To let operators sign in from the management console, expose Keycloak as a management resource:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-ini"&gt;management.oauth_enabled = true
management.oauth_disable_basic_auth = false
management.oauth_scopes = openid email profile
management.oauth_resource_servers.1.id = rabbitmq-keycloak
management.oauth_resource_servers.1.oauth_client_id = rabbitmq-keycloak
management.oauth_resource_servers.1.label = Keycloak SSO&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="add-the-second-identity-provider-aws-iam"&gt;Add the second identity provider (AWS IAM)&lt;/h3&gt; 
&lt;p&gt;Adding a second provider means adding a second resource server and a second entry under &lt;code&gt;oauth_providers&lt;/code&gt;. The IAM resource server uses the id &lt;code&gt;rabbitmq-iam&lt;/code&gt; because the audience is set when minting the token.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-ini"&gt;auth_oauth2.resource_servers.2.id = rabbitmq-iam
auth_oauth2.resource_servers.2.oauth_provider_id = aws_iam
auth_oauth2.resource_servers.2.scope_prefix = rabbitmq/
auth_oauth2.resource_servers.2.additional_scopes_key = sub
auth_oauth2.resource_servers.2.scope_aliases.1.alias = arn:aws:iam::123456789012:role/EKSWorkloadRole
auth_oauth2.resource_servers.2.scope_aliases.1.scope = rabbitmq/read:*/* rabbitmq/write:*/* rabbitmq/configure:*/* rabbitmq/tag:policymaker
auth_oauth2.oauth_providers.aws_iam.https.hostname_verification = wildcard
auth_oauth2.oauth_providers.aws_iam.issuer =
auth_oauth2.oauth_providers.aws_iam.jwks_uri =&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The IAM workload receives the &lt;code&gt;policymaker&lt;/code&gt; tag. It can publish, consume, and manage policies but does not receive the administrator tag.&lt;/p&gt; 
&lt;p&gt;Apply the configuration and restart the broker:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;CONFIG_ID=$(aws mq describe-broker --broker-id $BROKER_ID \
  --query 'Configurations.Current.Id' --output text)
REVISION=$(aws mq update-configuration --configuration-id $CONFIG_ID \
  --data "$(cat rabbitmq.conf | base64 | tr -d '\n')" \
  --query 'LatestRevision.Revision' --output text)
aws mq update-broker --broker-id $BROKER_ID \
  --configuration Id=$CONFIG_ID,Revision=$REVISION
aws mq reboot-broker --broker-id $BROKER_ID&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The base64 command syntax differs between Linux and macOS. The preceding command (&lt;code&gt;cat file | base64 | tr -d '\n'&lt;/code&gt;) is portable on both operating systems. If running exclusively on Linux, you can also use &lt;code&gt;base64 --wrap=0 rabbitmq.conf&lt;/code&gt;. On macOS, the equivalent command is &lt;code&gt;base64 -i rabbitmq.conf&lt;/code&gt;.&lt;/p&gt; 
&lt;h3 id="testing-and-validation"&gt;Testing and validation&lt;/h3&gt; 
&lt;p&gt;Validate each provider independently. For IAM, assume the role and request a token from AWS STS, then present it as the AMQP password:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;TOKEN=$(aws sts get-web-identity-token \
  --audience "rabbitmq-iam" \
  --signing-algorithm ES384 \
  --duration-seconds 300 \
  --query 'WebIdentityToken' --output text)
# Username is empty (ignored by the OAuth plugin); the token is passed as the password
curl -u ":$TOKEN" https://&amp;lt;broker-id&amp;gt;.mq.&amp;lt;region&amp;gt;.on.aws/api/overview&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;get-web-identity-token&lt;/code&gt; API requires outbound &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_outbound_getting_started.html" target="_blank" rel="noopener"&gt;web identity federation&lt;/a&gt; to be enabled on your AWS account and AWS CLI version 2.27 or later.&lt;/p&gt; 
&lt;p&gt;A successful response confirms the IAM resource server accepted the token. For Keycloak, open the management console, choose &lt;strong&gt;Keycloak SSO&lt;/strong&gt;, and sign in as an operator.&lt;/p&gt; 
&lt;p&gt;When a login fails, decode the JSON Web Token (JWT) and check two claims. The &lt;code&gt;aud&lt;/code&gt; claim must exactly match a resource server id. With &lt;code&gt;verify_aud = true&lt;/code&gt;, a missing or mismatched audience is the most common cause of rejection. If the audience is correct but permissions are missing, verify the &lt;code&gt;scope_prefix&lt;/code&gt; is set correctly.&lt;/p&gt; 
&lt;h2 id="operational-considerations"&gt;Operational considerations&lt;/h2&gt; 
&lt;p&gt;A few points deserve attention before you run this pattern in production.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Key rotation&lt;/strong&gt;: When rotating signing keys at a provider, publish the new key in the JWKS endpoint before revoking the old one. The broker caches keys, so overlapping both during the transition window prevents authentication failures while the cache refreshes.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Audience validation:&lt;/strong&gt; Audience remains the linchpin. With &lt;code&gt;verify_aud&lt;/code&gt; enabled, every provider must issue tokens carrying the audience its resource server expects, so confirm this whenever you onboard a new one. Don’t disable audience validation in production. The &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/oauth-for-amq-for-rabbitmq.html" target="_blank" rel="noopener"&gt;RabbitMQ OAuth 2.0 plugin does not perform token revocation checks&lt;/a&gt;, which makes audience binding a critical control that prevents tokens issued for other services from granting access.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Scope prefix&lt;/strong&gt;: &lt;code&gt;scope_prefix&lt;/code&gt; values are optional. They’re needed only if the tokens don’t follow the &lt;a href="https://www.rabbitmq.com/docs/oauth2#scope-translation" target="_blank" rel="noopener"&gt;default format&lt;/a&gt;. RabbitMQ only reads scopes carrying the expected prefix, so a token can authenticate yet grant nothing if the prefix is missing. Map each provider to the least privilege its principals need. For example, prefer narrow scopes like &lt;code&gt;read:orders&lt;/code&gt; over blanket &lt;code&gt;read:all&lt;/code&gt; to limit the scope of impact if a single provider’s credentials are compromised.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Token lifetime&lt;/strong&gt;: Because the plugin does not support token revocation, token lifetime is your primary control over leaked credentials. &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-caching-tokens.html" target="_blank" rel="noopener"&gt;Issue short-lived access tokens&lt;/a&gt; and have your client applications refresh them proactively at approximately 75 percent of the token’s lifetime to avoid connection disruptions when a token expires mid-session.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt; Authentication failures and refused tokens are recorded in the broker’s connection log group in &lt;a href="https://aws.amazon.com/cloudwatch/" target="_blank" rel="noopener"&gt;Amazon CloudWatch&lt;/a&gt;, which can be reached through the Amazon CloudWatch Logs link on the broker’s page in the Amazon MQ console. Beyond logs, set up &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/best-practices-network-resilience.html" target="_blank" rel="noopener"&gt;CloudWatch alarms&lt;/a&gt; on &lt;code&gt;RabbitMQMemUsed&lt;/code&gt;, &lt;code&gt;RabbitMQDiskFree&lt;/code&gt;, and &lt;code&gt;ConnectionCount&lt;/code&gt;. An unexpected spike in failed connections is often the first sign of a token or audience misconfiguration. For unaggregated, per-node visibility, consider enabling the &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/rabbitmq-prometheus-metrics.html" target="_blank" rel="noopener"&gt;Prometheus metrics endpoint&lt;/a&gt;: metrics such as &lt;code&gt;rabbitmq_auth_attempts_failed_total&lt;/code&gt; surface OAuth rejections faster than the CloudWatch one-minute polling interval.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Network controls:&lt;/strong&gt; Enforce defense in depth by restricting broker access using &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/connecting-to-amazon-mq.html" target="_blank" rel="noopener"&gt;security groups&lt;/a&gt; so that only authorized VPCs and IP ranges can reach the AMQPS and management endpoints. This matters especially in an OAuth setup because, once a token has been issued, the broker cannot revoke it before it expires.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="cleanup"&gt;Cleanup&lt;/h3&gt; 
&lt;p&gt;To avoid incurring future costs, delete the resources created during this walkthrough if you no longer need them:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Delete Amazon MQ broker and configurations.&lt;/li&gt; 
 &lt;li&gt;Remove test OAuth application registrations from your identity providers.&lt;/li&gt; 
 &lt;li&gt;Delete any IAM roles created for testing.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post, we demonstrated how Picnic configured an Amazon MQ for RabbitMQ broker to authenticate tokens from two OAuth 2.0 identity providers: Keycloak for human operators and AWS IAM for machine-to-machine services on a single broker instance. The key mechanism is RabbitMQ’s support for multiple resource servers, where the audience claim in each token determines which provider’s signing keys and permission rules apply.&lt;/p&gt; 
&lt;p&gt;With this approach, the Picnic team was able to cleanly separate human and machine authentication without the operational overhead of running separate brokers, while retaining fine-grained access control for both token issuers.&lt;/p&gt; 
&lt;p&gt;This pattern works with any combination of OAuth 2.0 providers and is particularly valuable for organizations looking to consolidate messaging infrastructure while maintaining distinct identity boundaries.&lt;/p&gt; 
&lt;p&gt;To learn more about Amazon MQ for RabbitMQ and OAuth 2.0 authentication, see &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-access.html" target="_blank" rel="noopener"&gt;Authentication and authorization for Amazon MQ&lt;/a&gt;. For a hands-on walkthrough of configuring OAuth 2.0 with Amazon MQ for RabbitMQ, see &lt;a href="https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/oauth-tutorial.html" target="_blank" rel="noopener"&gt;Using OAuth 2.0 authentication and authorization for Amazon MQ for RabbitMQ&lt;/a&gt;. The configuration examples in this post are broker-level settings applied through the Amazon MQ API. No standalone code repository is required.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6191-3.jpg" alt="Oscar Mapfumo Sibanda" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Oscar Mapfumo Sibanda&lt;/h3&gt; 
  &lt;p&gt;Oscar is a Senior Site Reliability Engineer at Picnic Technologies in the Netherlands. He builds infrastructure that supports rapid scaling, empowers engineering teams to move independently, and strengthens the security posture across the organization. Outside of work he paints and takes photographs; he is a technology enthusiast in the pursuit of happiness.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6191-4.jpg" alt="Ayush Kumar" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ayush Kumar&lt;/h3&gt; 
  &lt;p&gt;Ayush is a Technical Account Manager at Amazon Web Services based in the Netherlands. He works with enterprise customers to optimize their cloud architectures and accelerate innovation on AWS. You’ll find him experimenting in the kitchen in his spare time.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6191-5.jpg" alt="Amit Singh" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Amit Singh&lt;/h3&gt; 
  &lt;p&gt;Amit is a Senior Solutions Architect at AWS, working with enterprise retail customers in the Benelux region. He helps customers design cloud-native architectures, navigate complex modernization journeys, and adopt AI/ML capabilities at scale. Outside of work, he enjoys exploring new places and chasing the perfect shot, whether through a camera lens or on a running trail.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Build with geospatial and variant types in Iceberg v3 on AWS Glue 6.0</title>
		<link>https://aws.amazon.com/blogs/big-data/build-with-geospatial-and-variant-types-in-iceberg-v3-on-aws-glue-6-0/</link>
		
		<dc:creator><![CDATA[Shoukat Ghouse]]></dc:creator>
		<pubDate>Thu, 27 Aug 2026 16:00:12 +0000</pubDate>
				<category><![CDATA[Advanced (300)]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Glue]]></category>
		<category><![CDATA[Apache Iceberg]]></category>
		<guid isPermaLink="false">4be1580f82b548afe311a58d6de0cbfd1262965d</guid>

					<description>AWS Glue 6.0 with Apache Spark 4.1 adds support for Apache Iceberg v3: native geospatial types, nanosecond-precision timestamps, the VARIANT type, and DEFAULT column values. This post builds a connected vehicle fleet telemetry pipeline that uses all four in a single Iceberg v3 table, from ingestion through spatial, nanosecond, and variant queries.</description>
										<content:encoded>&lt;p&gt;xfAs organizations build data lakes that combine geospatial data, high-frequency event streams, and heterogeneous payloads, the limitations of older table formats become acute. Without a native geospatial type, coordinates require separate float columns (latitude/longitude) with no spatial predicates. Without nanosecond-precision timestamps, sub-microsecond event ordering is lost. Without a variant type, semi-structured data forces a choice between rigid flattening and untyped JSON strings. Each workaround adds complexity, slows queries, and increases maintenance burden.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/glue/latest/dg/migrating-version-60.html" target="_blank" rel="noopener"&gt;AWS Glue 6.0&lt;/a&gt;, powered by &lt;a href="https://spark.apache.org/releases/spark-release-4.1.0.html" target="_blank" rel="noopener"&gt;Apache Spark 4.1&lt;/a&gt;, removes these workarounds by adding support for &lt;a href="https://iceberg.apache.org/spec/#version-3" target="_blank" rel="noopener"&gt;Apache Iceberg v3,&lt;/a&gt; bringing new column-level capabilities to your data lake tables. These include new data types: native geospatial types (GEOMETRY with spatial predicates, and GEOGRAPHY), nanosecond-precision timestamps, and the VARIANT type for semi-structured data with &lt;a href="https://aws.amazon.com/blogs/big-data/beyond-json-blobs-implementing-the-variant-data-type-in-apache-iceberg-v3/#:~:text=VARIANT%20data%20type.-,How%20VARIANT%20works,-When%20you%20insert" target="_blank" rel="noopener"&gt;automatic shredding&lt;/a&gt;. Iceberg v3 also adds support for DEFAULT column values. These are table format features. After they’re written, they’re readable by any Iceberg v3-compatible engine that supports these features.&lt;/p&gt; 
&lt;p&gt;In this post, we build a connected vehicle fleet monitoring pipeline that uses these capabilities in a single Iceberg v3 table. Vehicles emit telemetry events with GPS coordinates (geospatial), sub-microsecond event times (nanosecond), and sensor payloads that vary by vehicle type (variant). We ingest these events, run spatial queries to detect geofence violations, sequence events at nanosecond precision, and extract typed metrics from heterogeneous payloads, all without workarounds, flattening, or external libraries.&lt;/p&gt; 
&lt;h2 id="solution-overview"&gt;Solution overview&lt;/h2&gt; 
&lt;p&gt;A logistics company operates a mixed fleet of delivery vehicles: vans, electric bikes, and delivery robots. Each vehicle type produces telemetry events with a different sensor payload schema. The operations team needs to:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Detect geofence violations:&lt;/strong&gt; flag vehicles that enter restricted zones (airports, pedestrian areas, private property).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Sequence events precisely:&lt;/strong&gt; at fleet scale, many events land in the same microsecond window. Nanosecond timestamps give a deterministic order and prevent ties when sequencing or deduplicating events during processing.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Extract metrics from heterogeneous payloads:&lt;/strong&gt; query battery level from delivery robots, fuel level from vans, and pedal cadence from bikes, all stored in the same column.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;We address all three requirements with a single Iceberg v3 table on AWS Glue 6.0. The following data definition language (DDL) shows the table structure. The AWS Glue job we provision in subsequent steps executes this statement.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;CREATE TABLE fleet_monitoring_db.vehicle_telemetry (
event_id STRING,
vehicle_id STRING,
vehicle_type STRING DEFAULT 'UNKNOWN',
event_time TIMESTAMP_NTZ(9),
location GEOMETRY(4326),
service_area GEOGRAPHY(4326),
sensor_payload VARIANT,
speed_kmh DOUBLE DEFAULT 0.0,
region STRING DEFAULT 'EMEA'
) USING ICEBERG
TBLPROPERTIES (
'format-version' = '3',
'write.delete.mode' = 'merge-on-read'
)
PARTITIONED BY (days(event_time), vehicle_type)&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;In the preceding statement, the database is shown as &lt;code&gt;fleet_monitoring_db&lt;/code&gt; for readability. The deployed stack creates it as &lt;code&gt;fleet_monitoring_&amp;lt;account-id&amp;gt;&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;The following list describes the key columns:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;event_time TIMESTAMP_NTZ(9): Stores the event timestamp at nanosecond precision.&lt;/li&gt; 
 &lt;li&gt;location GEOMETRY(4326): Stores GPS coordinates as native spatial objects using (SRID 4326). You can use predicates like &lt;code&gt;ST_Intersects&lt;/code&gt; directly in SQL, replacing hand-coded spatial math on raw latitude/longitude doubles (&lt;a href="https://en.wikipedia.org/wiki/World_Geodetic_System#:~:text=%5B3%5D-,WGS%C2%A084,-edit" target="_blank" rel="noopener"&gt;WGS 84&lt;/a&gt;).&lt;/li&gt; 
 &lt;li&gt;service_area GEOGRAPHY(4326): Stores geographic coordinates using a spherical (geodesic) model, distinct from GEOMETRY’s planar model. AWS Glue 6.0 writes and reads GEOGRAPHY in Iceberg v3, and the type is portable to any Iceberg v3-compatible engine. Geodesic spatial predicates over GEOGRAPHY are engine-dependent today. In this post we run spatial queries on the GEOMETRY location column, which Glue 6.0 supports natively.&lt;/li&gt; 
 &lt;li&gt;sensor_payload VARIANT: Each vehicle type produces a different JSON schema. Vans report fuel and engine metrics, robots report battery and camera status, bikes report cadence and heart rate. All land in this single column without schema unions or separate tables using &lt;a href="https://iceberg.apache.org/spec/#semi-structured-types" target="_blank" rel="noopener"&gt;variant&lt;/a&gt; data type.&lt;/li&gt; 
 &lt;li&gt;vehicle_type STRING DEFAULT ‘UNKNOWN’ and speed_kmh DOUBLE DEFAULT 0.0: When an ingestion writer omits these fields, Iceberg applies the declared defaults automatically. Useful when multiple producers write to the same table and not all of them populate every column.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The table uses &lt;code&gt;PARTITIONED BY (days(event_time), vehicle_type)&lt;/code&gt; so that analytical queries can prune by date range and vehicle type without scanning the full table. &lt;code&gt;'write.delete.mode' = 'merge-on-read'&lt;/code&gt; supports fast row-level corrections (for example, correcting a misreported GPS coordinate) through compact &lt;a href="https://iceberg.apache.org/spec/#deletion-vectors" target="_blank" rel="noopener"&gt;deletion vectors&lt;/a&gt; (Roaring Bitmaps) instead of accumulating positional delete files.&lt;/p&gt; 
&lt;p&gt;In this post, we insert sample data directly to focus on the new Iceberg data types and how to use them together. In production, these events would stream from Amazon Managed Streaming for Apache Kafka (Amazon MSK) into an AWS Glue 6.0 streaming job.&lt;/p&gt; 
&lt;p&gt;The following diagram illustrates the production architecture for reference:&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-1.png" alt="Architecture diagram showing a vehicle fleet of vans, delivery robots, and electric bikes sending telemetry through Amazon MSK into an AWS account. Within a VPC, a hot path uses AWS Glue 6.0 Spark Real-Time Mode to detect geofence violations and send alerts to a Kafka topic, while a cold path uses a Glue 6.0 micro-batch job to write events into an Apache Iceberg v3 table with GEOMETRY, TIMESTAMP_NTZ(9), VARIANT, and DEFAULT columns. Amazon S3 stores the Iceberg data and the AWS Glue Data Catalog holds metadata. A batch analytics Glue job reads the Iceberg table for geofence detection, nanosecond event sequencing, and per-vehicle-type metric extraction using variant_get" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Reference architecture for a fleet telemetry pipeline on AWS Glue 6.0&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The architecture processes vehicle telemetry through two paths, with a downstream batch analytics layer:&lt;/p&gt; 
&lt;p&gt;Hot path (real-time, milliseconds): A Spark &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/streaming-chapter.html#glue-streaming-real-time-mode" target="_blank" rel="noopener"&gt;Real-Time Mode (RTM) job&lt;/a&gt; reads telemetry from Amazon MSK and evaluates geofence violations using spatial predicates like &lt;code&gt;ST_Intersects&lt;/code&gt;, routing alerts to a downstream Kafka topic within milliseconds.&lt;/p&gt; 
&lt;p&gt;Cold path (near-real-time, seconds): A micro-batch job reads the same MSK topic and writes events into an Iceberg v3 table, converting payloads to GEOMETRY, TIMESTAMP_NTZ(9), and VARIANT columns with DEFAULT values applied.&lt;/p&gt; 
&lt;p&gt;Batch analytics: An AWS Glue job reads the Iceberg v3 table to run batch analytics on geofence detection, nanosecond event sequencing, and per-vehicle-type metric extraction.&lt;/p&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;p&gt;To follow along, you need:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An AWS account and an AWS Region where AWS Glue 6.0 is available.&lt;/li&gt; 
 &lt;li&gt;An AWS Identity and Access Management (IAM) role with permissions to deploy AWS CloudFormation stacks and create resources including AWS Glue, Amazon Simple Storage Service (Amazon S3), and Amazon CloudWatch Logs.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="deploy-the-cloudformation-stack"&gt;Deploy the CloudFormation stack&lt;/h2&gt; 
&lt;p&gt;We provide an AWS CloudFormation template that provisions all the resources needed for this walkthrough.&lt;/p&gt; 
&lt;p&gt;The stack provisions the following resources:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An Amazon S3 bucket for Iceberg table storage.&lt;/li&gt; 
 &lt;li&gt;An IAM role with permissions for AWS Glue, Amazon S3, and Amazon CloudWatch Logs.&lt;/li&gt; 
 &lt;li&gt;An AWS Glue database (&lt;code&gt;fleet_monitoring_&amp;lt;account-id&amp;gt;&lt;/code&gt;).&lt;/li&gt; 
 &lt;li&gt;An AWS Glue job &lt;code&gt;fleet-telemetry-ingest-&amp;lt;account-id&amp;gt;&lt;/code&gt; (PySpark): creates the Iceberg v3 table &lt;code&gt;vehicle_telemetry&lt;/code&gt; described earlier and inserts sample telemetry from three vehicle types.&lt;/li&gt; 
 &lt;li&gt;An AWS Glue job &lt;code&gt;fleet-telemetry-queries-&amp;lt;account-id&amp;gt;&lt;/code&gt; (PySpark): demonstrates geofence detection, nanosecond sequencing, variant extraction, and default values.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Deploy the CloudFormation stack:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Download the &lt;a href="https://github.com/aws-samples/aws-glue-samples/blob/master/examples/glue6-iceberg-v3-data-types/template.yaml" target="_blank" rel="noopener"&gt;CloudFormation template&lt;/a&gt; from the GitHub repository.&lt;/li&gt; 
 &lt;li&gt;Sign in to the &lt;a href="https://console.aws.amazon.com/cloudformation/" target="_blank" rel="noopener"&gt;AWS CloudFormation console&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Create stack&lt;/strong&gt;, &lt;strong&gt;With new resources&lt;/strong&gt;, &lt;strong&gt;Upload a template file&lt;/strong&gt;, and upload the downloaded template.&lt;/li&gt; 
 &lt;li&gt;Acknowledge the IAM capabilities and choose &lt;strong&gt;Create stack&lt;/strong&gt;.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Stack creation takes approximately 2–5 minutes. No parameters are required.&lt;/p&gt; 
&lt;p&gt;After the stack completes, navigate to the &lt;a href="https://console.aws.amazon.com/gluestudio/home#/jobs" target="_blank" rel="noopener"&gt;AWS Glue console&lt;/a&gt; and run the jobs in this order:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Run &lt;code&gt;fleet-telemetry-ingest-&amp;lt;account-id&amp;gt;&lt;/code&gt;. This job creates the Iceberg v3 table and inserts sample data (approximately 2 minutes).&lt;/li&gt; 
 &lt;li&gt;After it succeeds, run &lt;code&gt;fleet-telemetry-queries-&amp;lt;account-id&amp;gt;&lt;/code&gt;. This job executes all demonstration queries (approximately 2 minutes).&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;The following sections describe each job in detail.&lt;/p&gt; 
&lt;h2 id="job-1-ingest-sample-telemetry-data"&gt;Job 1: Ingest sample telemetry data&lt;/h2&gt; 
&lt;p&gt;The ingestion job creates the Iceberg v3 table described earlier and inserts four sample telemetry events: one for each of the three vehicle types (van, robot, bike), plus one with omitted fields to demonstrate DEFAULT values. You can view the complete script in the &lt;a href="https://github.com/aws-samples/aws-glue-samples/tree/master/examples/glue6-iceberg-v3-data-types" target="_blank" rel="noopener"&gt;GitHub repository&lt;/a&gt;. Note that the geospatial types require one additional Spark configuration (&lt;code&gt;spark.sql.geospatial.enabled=true&lt;/code&gt;), which is already set in the job’s &lt;code&gt;--conf&lt;/code&gt; argument by the CloudFormation template. All other types work with no extra configuration.&lt;/p&gt; 
&lt;p&gt;The following are the key snippets from the script:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Van telemetry:&lt;/strong&gt; GPS coordinates with engine metrics and route information:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;spark.sql(f"""
INSERT INTO {TABLE} VALUES (
'EVT-001', 'VAN-042', 'VAN',
CAST('2026-07-28 09:15:30.123456789' AS TIMESTAMP_NTZ(9)),
ST_SetSrid(ST_GeomFromWKB(X'0101000000E17A14AE47E1C0BF1F85EB51B84E4940'), 4326),
ST_SetSrid(ST_GeogFromWKB(X'0101000000E17A14AE47E1C0BF1F85EB51B84E4940'), 4326),
PARSE_JSON('{{"fuel_pct": 0.72, "cargo_kg": 450, "door_open": false,
"engine": {{"rpm": 2100, "temp_c": 88.5}},
"route": {{"stops_remaining": 4, "eta_minutes": 35}}}}'),
35.2, 'EMEA'
)
""")&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Delivery robot telemetry:&lt;/strong&gt; Same table, completely different sensor schema (battery, cameras, navigation):&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;spark.sql(f"""
INSERT INTO {TABLE} VALUES (
'EVT-002', 'ROB-117', 'ROBOT',
CAST('2026-07-28 09:15:30.123456790' AS TIMESTAMP_NTZ(9)),
ST_SetSrid(ST_GeomFromWKB(X'01010000000000000000001040000000000000F03F'), 4326),
ST_SetSrid(ST_GeogFromWKB(X'01010000000000000000001040000000000000F03F'), 4326),
PARSE_JSON('{{"battery_pct": 0.62, "obstacle_distance_m": 2.8,
"navigation_mode": "autonomous",
"cameras": {{"front": "active", "rear": "recording"}}}}'),
48.0, 'EMEA'
)
""")&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; EVT-001 and EVT-002 are exactly &lt;strong&gt;1 nanosecond apart&lt;/strong&gt; (&lt;code&gt;.123456789&lt;/code&gt; vs &lt;code&gt;.123456790&lt;/code&gt;). Without &lt;code&gt;TIMESTAMP_NTZ(9)&lt;/code&gt;, both would round to the same microsecond and be indistinguishable.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Default values test:&lt;/strong&gt; Event inserted with &lt;code&gt;vehicle_type&lt;/code&gt;, &lt;code&gt;speed_kmh&lt;/code&gt;, and &lt;code&gt;region&lt;/code&gt; omitted:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;spark.sql(f"""
INSERT INTO {TABLE}
(event_id, vehicle_id, event_time, location, service_area, sensor_payload)
VALUES (
'EVT-004', 'UNK-999',
CAST('2026-07-28 10:00:00.000000000' AS TIMESTAMP_NTZ(9)),
ST_SetSrid(ST_GeomFromWKB(X'0101000000000000000000F03F000000000000F03F'), 4326),
ST_SetSrid(ST_GeogFromWKB(X'0101000000000000000000F03F000000000000F03F'), 4326),
PARSE_JSON('{{"status": "initializing"}}')
)
""")&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The omitted columns automatically receive their DEFAULT values: &lt;code&gt;vehicle_type = 'UNKNOWN'&lt;/code&gt;, &lt;code&gt;speed_kmh = 0.0&lt;/code&gt;, &lt;code&gt;region = 'EMEA'&lt;/code&gt;.&lt;/p&gt; 
&lt;h2 id="job-2-query-the-data"&gt;Job 2: Query the data&lt;/h2&gt; 
&lt;p&gt;The query job demonstrates all four data types working together. After the job succeeds, select the run in the AWS Glue console and choose &lt;strong&gt;Output logs&lt;/strong&gt; to see the results.&lt;/p&gt; 
&lt;p&gt;The following sections walk through the key queries from the job and the results of each.&lt;/p&gt; 
&lt;h3 id="geofence-detection-with-st_intersects"&gt;Geofence detection with ST_Intersects&lt;/h3&gt; 
&lt;p&gt;The job defines a polygon and finds all vehicles inside it:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;POLY = "010300...."
SELECT event_id, vehicle_id, vehicle_type, speed_kmh
FROM fleet_monitoring_db.vehicle_telemetry
WHERE ST_Intersects(
location,ST_SetSrid(ST_GeomFromWKB(X'{POLY}'), 4326)
)
ORDER BY event_id&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The polygon covers coordinates (0,0)-(5,0)-(5,2)-(0,2). Three vehicles are inside (ROBOT at (4,1), BIKE at (3,1), UNKNOWN at (1,1)). The VAN at (-0.1278, 51.5074) is outside.&lt;/p&gt; 
&lt;div style="width: 368px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-2.png" alt="Query results listing the ROBOT, BIKE, and UNKNOWN vehicles inside the geofence polygon, with the VAN excluded" width="358"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Geofence query results showing the three vehicles inside the polygon&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="nanosecond-event-sequencing"&gt;Nanosecond event sequencing&lt;/h3&gt; 
&lt;p&gt;Order events by their sub-microsecond timestamps:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT event_id, vehicle_id, CAST(event_time AS STRING) AS precise_time
FROM fleet_monitoring_db.vehicle_telemetry
WHERE event_id IN ('EVT-001', 'EVT-002', 'EVT-003')
ORDER BY event_time ASC&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;EVT-001 and EVT-002 are correctly distinguished and ordered despite being only 1 nanosecond apart. With standard &lt;code&gt;TIMESTAMP_NTZ&lt;/code&gt; (microsecond precision), both would show &lt;code&gt;.123456&lt;/code&gt; and their relative order would be undefined.&lt;/p&gt; 
&lt;div style="width: 447px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-3.png" alt="Query results showing EVT-001 and EVT-002 ordered by nanosecond-precision timestamps one nanosecond apart" width="437"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: Nanosecond-precision ordering distinguishing two events one nanosecond apart&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="variant-extraction-with-variant_get"&gt;Variant extraction with variant_get&lt;/h3&gt; 
&lt;p&gt;Different sensor schemas per vehicle type, all extracted with &lt;code&gt;variant_get&lt;/code&gt;:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT vehicle_id, vehicle_type,
CASE vehicle_type
WHEN 'VAN' THEN variant_get(sensor_payload, '$.fuel_pct', 'DOUBLE')
WHEN 'ROBOT' THEN variant_get(sensor_payload, '$.battery_pct', 'DOUBLE')
WHEN 'BIKE' THEN variant_get(sensor_payload, '$.battery_pct', 'DOUBLE')
ELSE NULL
END AS energy_level,
variant_get(sensor_payload, '$.engine.temp_c', 'DOUBLE') AS engine_temp,
variant_get(sensor_payload, '$.cameras.front', 'STRING') AS front_cam,
variant_get(sensor_payload, '$.deliveries.completed', 'INT') AS deliveries_done
FROM fleet_monitoring_db.vehicle_telemetry
WHERE vehicle_type != 'UNKNOWN'
ORDER BY vehicle_id&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;div style="width: 635px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-4.png" alt="Query results showing variant_get extracting energy level, engine temperature, and camera status for each vehicle type" width="625"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 4: Variant extraction returning typed values from heterogeneous sensor payloads&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;&lt;code&gt;variant_get&lt;/code&gt; takes three arguments: the column, a dot-path expression, and the expected return type. It supports arbitrary nesting depth. &lt;code&gt;$.engine.temp_c&lt;/code&gt; reaches two levels deep, &lt;code&gt;$.deliveries.completed&lt;/code&gt; reaches into a different structure entirely. When a path doesn’t exist in a particular row’s payload, it returns NULL.&lt;/p&gt; 
&lt;h3 id="default-values"&gt;Default values&lt;/h3&gt; 
&lt;p&gt;Confirm that omitted columns received their defaults:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT event_id, vehicle_type, speed_kmh, region
FROM fleet_monitoring_db.vehicle_telemetry
WHERE event_id = 'EVT-004'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;div style="width: 406px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-5.png" alt="Query results showing event EVT-004 with the default values UNKNOWN, 0.0, and EMEA applied" width="396"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 5: Default column values applied to the event inserted with omitted fields&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;EVT-004 was inserted without vehicle_type, speed_kmh, or region. The declared defaults were applied automatically.&lt;/p&gt; 
&lt;h3 id="combined-query-combining-spatial-temporal-and-variant-operations"&gt;Combined query: Combining spatial, temporal, and variant operations&lt;/h3&gt; 
&lt;p&gt;The following query runs a geospatial predicate, nanosecond ordering, and variant extraction in a single &lt;code&gt;SELECT&lt;/code&gt; statement:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT vehicle_id, vehicle_type,
CAST(event_time AS STRING) AS precise_time,
CASE vehicle_type
WHEN 'VAN' THEN variant_get(sensor_payload, '$.fuel_pct', 'DOUBLE')
WHEN 'ROBOT' THEN variant_get(sensor_payload, '$.battery_pct', 'DOUBLE')
WHEN 'BIKE' THEN variant_get(sensor_payload, '$.battery_pct', 'DOUBLE')
ELSE NULL
END AS energy_level,
speed_kmh
FROM fleet_monitoring_db.vehicle_telemetry
WHERE ST_Intersects(location, ST_SetSrid(ST_GeomFromWKB(X'0103000000...'), 4326))
ORDER BY event_time ASC&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;div style="width: 713px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-6.png" alt="Query results combining spatial filtering, nanosecond ordering, and variant extraction in a single query" width="703"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 6: Combined query results over a single Iceberg v3 table&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;This single query combines a spatial predicate, nanosecond ordering, and variant extraction over one table, with no external libraries, pre-processing, or joins to separate geometry or payload tables.&lt;/p&gt; 
&lt;h2 id="clean-up"&gt;Clean up&lt;/h2&gt; 
&lt;p&gt;To avoid ongoing charges from the AWS Glue jobs and Amazon S3 storage, delete the CloudFormation stack when you’re done:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open the AWS &lt;a href="https://console.aws.amazon.com/cloudformation/" target="_blank" rel="noopener"&gt;CloudFormation console&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;Select the stack you deployed earlier and choose &lt;strong&gt;Delete&lt;/strong&gt;.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post, we stored and analyzed geospatial coordinates, nanosecond timestamps, and heterogeneous sensor payloads in a single Iceberg v3 table on &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/migrating-version-60.html" target="_blank" rel="noopener"&gt;AWS Glue 6.0&lt;/a&gt;, with sensible defaults applied automatically, no external libraries, and no schema flattening.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;GEOMETRY&lt;/strong&gt; columns replace latitude/longitude doubles and support native spatial predicates like &lt;code&gt;ST_Intersects&lt;/code&gt; for geofence detection. GEOGRAPHY is stored natively.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;TIMESTAMP_NTZ(9)&lt;/strong&gt; preserves full nanosecond precision for event sequencing where microsecond resolution is insufficient.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;VARIANT&lt;/strong&gt; stores heterogeneous payloads (different schema per vehicle type) in one column with typed extraction through &lt;code&gt;variant_get&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;DEFAULT values&lt;/strong&gt; keep field population consistent across multiple ingestion writers without duplicating logic.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;All capabilities require Iceberg format-version 3. Geospatial requires one additional configuration (&lt;code&gt;spark.sql.geospatial.enabled=true&lt;/code&gt;). Nanosecond timestamps, Variant, and DEFAULT values work with no extra configuration.&lt;/p&gt; 
&lt;p&gt;These capabilities apply wherever schemas vary by source (IoT fleets, multi-tenant software as a service (SaaS), event-driven architectures), timestamps need sub-microsecond precision (trading, sensor fusion, autonomous systems), or spatial operations replace coordinate workarounds (logistics, real estate, delivery networks).&lt;/p&gt; 
&lt;p&gt;For more information, see the &lt;a href="https://aws.amazon.com/blogs/big-data/introducing-aws-glue-6-0-for-apache-spark/" target="_blank" rel="noopener"&gt;AWS launch announcement&lt;/a&gt;, the &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/what-is-glue.html" target="_blank" rel="noopener"&gt;AWS Glue documentation&lt;/a&gt;, and the Apache Iceberg &lt;a href="https://iceberg.apache.org/spec/#version-3" target="_blank" rel="noopener"&gt;v3 specification&lt;/a&gt;. AWS Glue 6.0 includes additional capabilities such as Spark &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/streaming-chapter.html#glue-streaming-real-time-mode" target="_blank" rel="noopener"&gt;Real-Time Mode&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/etl-jobs-section.html#spark-declarative-pipelines" target="_blank" rel="noopener"&gt;Spark Declarative Pipelines&lt;/a&gt;, which we cover in separate posts.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-7.png" alt="Shoukat Ghouse" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Shoukat Ghouse&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/shoukat-ghouse-a6b07919/" target="_blank" rel="noopener"&gt;Shoukat&lt;/a&gt; is a Senior Specialist Solutions Architect for Big Data, Analytics, and Data Governance at Amazon Web Services (AWS). He partners with enterprise and financial services customers across EMEA to design and scale production-grade data lakehouse platforms on Apache Spark, Apache Iceberg, AWS Glue, Amazon EMR, and Amazon SageMaker Unified Studio. His focus spans distributed data processing, fine-grained data governance, and helping organizations build AI-ready data foundations that power analytics and machine learning at scale.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6177-8.png" alt="Shrey Malpani" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Shrey Malpani&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/shreymalpani/" target="_blank" rel="noopener"&gt;Shrey&lt;/a&gt; is a Senior Product Manager Technical at Amazon Web Services (AWS), where he works at the intersection of distributed data processing and data integration. He is focused on building and scaling data integration and data management capabilities across services like AWS Glue, &lt;a href="https://aws.amazon.com/emr/" target="_blank" rel="noopener"&gt;Amazon EMR&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/redshift/" target="_blank" rel="noopener"&gt;Amazon Redshift&lt;/a&gt; that help customers build AI-ready data platforms for their analytics and machine learning workflows.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6096-15.png" alt="Kartik" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Kartik&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/kartik-panjabi/" target="_blank" rel="noopener"&gt;Kartik&lt;/a&gt; is a Software Development Manager on the AWS Glue team. His team builds generative AI features for the Data Integration and distributed system for data integration.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Migrate an OAuth 2.0 authenticated Apache Kafka cluster to Amazon MSK with MSK Replicator</title>
		<link>https://aws.amazon.com/blogs/big-data/migrate-an-oauth-2-0-authenticated-apache-kafka-cluster-to-amazon-msk-with-msk-replicator/</link>
		
		<dc:creator><![CDATA[Subham Rakshit]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 19:11:40 +0000</pubDate>
				<category><![CDATA[Amazon Managed Streaming for Apache Kafka (Amazon MSK)]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Expert (400)]]></category>
		<guid isPermaLink="false">af6f07a36f17924a832546809e32af075cea1b07</guid>

					<description>MSK Replicator now supports OAuth 2.0 (SASL/OAUTHBEARER) authentication to external Apache Kafka clusters. This post walks through the three supported grant types, how to configure Replicator for each, the network and TLS prerequisites that are commonly missed, and how to handle identity providers behind an additional federation layer.</description>
										<content:encoded>&lt;p&gt;In an &lt;a href="https://aws.amazon.com/blogs/big-data/migrate-third-party-and-self-managed-apache-kafka-clusters-to-amazon-msk-express-and-standard-brokers-with-amazon-msk-replicator/" target="_blank" rel="noopener"&gt;earlier post&lt;/a&gt;, we walked through how Amazon Managed Streaming for Apache Kafka (Amazon MSK) Replicator migrates external and self-managed Apache Kafka clusters to Amazon MSK. It replicates your topics and their configurations, keeps topic and consumer-group names intact, and synchronizes consumer-group offsets, so your producers and consumers can cut over on their own schedule instead of all at once. MSK Replicator now supports OAuth 2.0 (SASL/OAUTHBEARER) authentication to the external cluster, and that is what this post covers.&lt;/p&gt; 
&lt;p&gt;If your external Kafka cluster authenticates clients with OAuth, MSK Replicator can connect to it, but “OAuth” isn’t a single thing you switch on. It’s a family of grant types, and each one comes with its own trust model, its own set of inputs you need to supply, and its own configuration on both the Replicator side and your identity provider (IdP) side.&lt;/p&gt; 
&lt;p&gt;In this post, we walk you through the grant types one by one, show you how to configure Replicator for each, call out the network and TLS prerequisites that are commonly missed, and finish with how to handle IdPs that sit behind an additional identity layer. This mechanism works with any OAuth 2.0 (OIDC) identity provider, including Keycloak, Okta, Microsoft Entra ID, PingFederate, and Auth0. OAuth here governs only how Replicator authenticates to your external cluster, so the target can be either Amazon MSK Standard or Express brokers, which always use IAM.&lt;/p&gt; 
&lt;h2 id="how-oauth-authentication-works"&gt;How OAuth authentication works&lt;/h2&gt; 
&lt;p&gt;Before you configure Replicator, it helps to be precise about how the OAuth Kafka handshake works.&lt;/p&gt; 
&lt;h3 id="the-components"&gt;The components&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;The Identity Provider (IdP) –&lt;/strong&gt; Issues &lt;strong&gt;access tokens&lt;/strong&gt; and publishes the &lt;strong&gt;public keys&lt;/strong&gt;. Brokers use these keys to verify the tokens. Examples: Keycloak, Okta, Microsoft Entra ID, PingFederate, Auth0, or a custom OIDC server.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;The client –&lt;/strong&gt; In our case, &lt;strong&gt;MSK Replicator&lt;/strong&gt;, acting as a Kafka consumer/producer against your external cluster.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;The resource server –&lt;/strong&gt; Your &lt;strong&gt;self-managed Kafka broker&lt;/strong&gt;, which must decide whether to admit a connection.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;The access token –&lt;/strong&gt; A JWT (JSON Web Token): a base64url-encoded, three-part string &lt;code&gt;header.payload.signature&lt;/code&gt; that the IdP cryptographically signs.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3 id="the-sasloauthbearer-handshake-step-by-step"&gt;The SASL/OAUTHBEARER handshake, step by step&lt;/h3&gt; 
&lt;p&gt;The following sequence diagram shows the full exchange, from Replicator requesting a token to the broker accepting the connection:&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-1.jpg" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-1.jpg" alt="Sequence diagram of the SASL/OAUTHBEARER handshake: Replicator requests a token from the IdP, receives a signed JWT, presents it to the Kafka broker, and the broker verifies the JWT against cached JWKS keys before accepting the connection." width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 1: The SASL/OAUTHBEARER handshake. Replicator gets a signed JWT from the IdP and presents it to the broker, which verifies it against cached JWKS keys before accepting the connection.&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Walking through it:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Request a token –&lt;/strong&gt; Replicator asks the IdP for an access token. The exact request depends on the grant type (covered in the next section).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Receive a signed JWT –&lt;/strong&gt; The IdP returns a signed JWT access token.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Present the token –&lt;/strong&gt; Replicator opens a SASL/OAUTHBEARER connection to the external Kafka brokers and presents the JWT.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Verify locally –&lt;/strong&gt; The broker verifies the JWT signature against the IdP’s cached JWKS public keys, without calling the IdP per message.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Connection accepted –&lt;/strong&gt; The broker admits the connection and derives the Kafka principal from the &lt;code&gt;preferred_username&lt;/code&gt; claim.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Step 4 is worth dwelling on: the broker validates the token &lt;strong&gt;locally&lt;/strong&gt;. It fetches the IdP’s &lt;strong&gt;JWKS&lt;/strong&gt; (JSON Web Key Set, the public half of the IdP’s signing keys, &lt;a href="https://www.rfc-editor.org/info/rfc7517/#section-5" target="_blank" rel="noopener"&gt;RFC 7517&lt;/a&gt;) from an endpoint like &lt;code&gt;https://idp.example.com/realms/kafka/protocol/openid-connect/certs&lt;/code&gt; and caches it, refreshing on a configurable interval (and re-fetching if it sees a key ID it doesn’t recognize). Incoming JWT signatures are then verified against those cached keys. The IdP is not in the hot path of message traffic. It is contacted only to (a) issue tokens to clients, and (b) serve its public keys for the periodic JWKS refresh.&lt;/p&gt; 
&lt;h3 id="what-the-kafka-broker-checks"&gt;What the Kafka broker checks&lt;/h3&gt; 
&lt;p&gt;When Replicator presents a JWT, the broker validates:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Signature –&lt;/strong&gt; Proves the IdP issued the token and no one tampered with it (verified against JWKS).&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;iss&lt;/code&gt; &lt;strong&gt;(issuer) –&lt;/strong&gt; Must match the broker’s configured &lt;code&gt;oauth.valid.issuer.uri&lt;/code&gt;, byte-for-byte, including scheme, host, port, and path. A mismatch is a common configuration error.&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;exp&lt;/code&gt; &lt;strong&gt;(expiry) –&lt;/strong&gt; Expired tokens are rejected. Strimzi’s client callback handler proactively refreshes before expiry, so you shouldn’t see mid-stream failures.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;The principal claim –&lt;/strong&gt; Typically &lt;code&gt;preferred_username&lt;/code&gt;. The broker uses this as the Kafka principal in ACLs (for example, &lt;code&gt;User:service-account-msk-replicator&lt;/code&gt;). This matters: the identity Replicator authenticates as on the external cluster must have ACLs that you configure to grant it the read/describe permissions it needs.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="mapping-your-idp-to-a-replicator-grant-type"&gt;Mapping your IdP to a Replicator grant type&lt;/h2&gt; 
&lt;p&gt;A &lt;strong&gt;grant type&lt;/strong&gt; is the protocol by which the client proves its identity to the IdP and obtains a token. This is the front half of the preceding handshake (steps 1 and 2). MSK Replicator supports three of them. You already know how your Kafka clients authenticate to your IdP today, so start from that.&lt;/p&gt; 
&lt;h3 id="which-grant-to-use"&gt;Which grant to use?&lt;/h3&gt; 
&lt;p&gt;Find the row that matches how your clients get tokens today:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;How your Kafka clients get tokens from the IdP today&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Grant type&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Long-lived secret?&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;What you trust/register on the IdP&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;A &lt;code&gt;client_id&lt;/code&gt; / &lt;code&gt;client_secret&lt;/code&gt; (confidential client)&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;CLIENT_CREDENTIALS&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;Yes (stored on AWS Secrets Manager)&lt;/td&gt; 
   &lt;td&gt;Nothing new: reuse the existing client, or create one for Replicator&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;You want secretless, and your IdP can trust an &lt;strong&gt;external token issuer&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;IAM_JWT_BEARER&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;No&lt;/td&gt; 
   &lt;td&gt;AWS STS as an external token (OIDC) issuer. Trust its JWKS&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;You want secretless, and your IdP models workloads as &lt;strong&gt;signed-JWT clients&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;CLIENT_CREDENTIALS_ASSERTION&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;No&lt;/td&gt; 
   &lt;td&gt;AWS STS as the client’s signing authority (&lt;code&gt;private_key_jwt&lt;/code&gt;). Trust its JWKS&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The simplest mapping is like-for-like: if your clients use a &lt;code&gt;client_id&lt;/code&gt;/&lt;code&gt;client_secret&lt;/code&gt;, point Replicator at the same client with &lt;strong&gt;CLIENT_CREDENTIALS&lt;/strong&gt;. If you’d rather not give Replicator a long-lived secret, the two secretless grants let it authenticate with its AWS identity instead. Choose between them based on how your IdP prefers to trust an external party.&lt;/p&gt; 
&lt;p&gt;The rest of this section explains &lt;em&gt;why&lt;/em&gt; the three grants differ, using an analogy. If your row is clear and you only want the configuration, skip ahead to &lt;a href="#configuring-and-creating-the-replicator"&gt;Configuring and creating the replicator&lt;/a&gt;.&lt;/p&gt; 
&lt;h3 id="a-scenario-checking-in-at-a-secure-office-building"&gt;A scenario: checking in at a secure office building&lt;/h3&gt; 
&lt;p&gt;A visitor needs to get into a secure office building. They can’t walk straight in. First they stop at the &lt;strong&gt;reception desk&lt;/strong&gt; to prove who they are and collect a &lt;strong&gt;temporary access pass&lt;/strong&gt;. Only then can they use that pass at the building’s &lt;strong&gt;turnstile&lt;/strong&gt; to get inside. In OAuth terms: the building is your external Kafka cluster, the reception desk is the IdP, the temporary access pass is the access token (JWT), and the visitor is MSK Replicator. Presenting the pass at the turnstile is the SASL/OAUTHBEARER step, and it works the same way for every grant type. What differs is how the visitor proves who they are at the reception desk before it prints a pass.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Scenario 1: CLIENT_CREDENTIALS (the shared PIN)&lt;/strong&gt;&lt;/p&gt; 
&lt;div style="width: 606px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-2.png" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-2.png" alt="CLIENT_CREDENTIALS scenario shown as a visitor entering a building: the visitor authenticates at reception with a PIN (the client secret), receives a temporary badge (the access token), and uses it to enter the building (the Kafka cluster)." width="596"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 2: CLIENT_CREDENTIALS. The visitor authenticates at reception with a PIN (the client_secret), gets a temporary badge (the access token), and uses it to enter the building (the Kafka cluster).&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;At the reception desk the visitor keys in a PIN the desk already have on file (the &lt;code&gt;client_secret&lt;/code&gt;), collects a temporary access pass in return (the access token), and uses that pass to get into the building. Both sides hold the same secret. In practice (&lt;a href="https://www.rfc-editor.org/info/rfc6749/#section-4.4" target="_blank" rel="noopener"&gt;RFC 6749 §4.4&lt;/a&gt;), Replicator authenticates to the IdP with a &lt;code&gt;client_id&lt;/code&gt;/&lt;code&gt;client_secret&lt;/code&gt; stored on AWS Secrets Manager, receives the access token, and presents it to the external Kafka brokers over SASL/OAUTHBEARER. Use it when your IdP already issues client secrets for machine clients. This is usually a like-for-like move that reuses the client your existing producers and consumers use, or a new one created for Replicator.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Scenario 2: IAM_JWT_BEARER (the badge is the request)&lt;/strong&gt;&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-3.png" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-3.png" alt="IAM_JWT_BEARER scenario: the visitor presents an employer-signed badge (an STS JWT) to reception as the request itself and receives an access token, because reception trusts the employer’s stamp (the STS JWKS)." width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 3: IAM_JWT_BEARER. The visitor shows an employer-signed badge (an STS JWT) to reception as the request itself and gets an access token. Reception accepts it because it trusts the employer’s stamp (the STS JWKS).&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;First, the visitor collects an employer-signed badge: Replicator calls STS &lt;code&gt;GetWebIdentityToken&lt;/code&gt; to mint an STS JWT. At the reception desk the badge itself is the request. The visitor shows it to ask for a pass. Reception trusts the employer’s tamper-proof stamp (STS JWKS), so it accepts the badge and prints a temporary access pass. In practice (&lt;a href="https://www.rfc-editor.org/rfc/rfc7523#section-2.1" target="_blank" rel="noopener"&gt;RFC 7523 §2.1&lt;/a&gt;), the STS JWT is sent as the authorization grant (&lt;code&gt;assertion&lt;/code&gt;), and the IdP trusts AWS STS as an external token issuer. Use it when you want secretless authentication, and your IdP can trust an external issuer’s JWTs.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Scenario 3: CLIENT_CREDENTIALS_ASSERTION (the same badge, used as ID on the form)&lt;/strong&gt;&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-4.png" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-4.png" alt="CLIENT_CREDENTIALS_ASSERTION scenario: the visitor fills out reception’s standard request form and attaches the same STS JWT as identification to receive an access token, which reception grants by trusting the employer’s stamp (the STS JWKS)." width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 4: CLIENT_CREDENTIALS_ASSERTION. The visitor fills out reception’s standard request form and attaches the same STS JWT as ID, getting an access token. Reception trusts the employer’s stamp (the STS JWKS).&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The visitor again collects the same employer-signed badge (STS JWT). This time they fill out the reception desk’s standard access request form (the &lt;code&gt;client_credentials&lt;/code&gt; grant) and attach the badge to it as identification, all in one submission. Reception trusts the same employer stamp (STS JWKS) and prints a temporary access pass. In practice (&lt;a href="https://www.rfc-editor.org/rfc/rfc7521" target="_blank" rel="noopener"&gt;RFC 7521&lt;/a&gt;/&lt;a href="https://datatracker.ietf.org/doc/html/rfc7523#section-2.2" target="_blank" rel="noopener"&gt;RFC 7523 §2.2&lt;/a&gt;), the same STS JWT is sent as the &lt;code&gt;client_assertion&lt;/code&gt; on the &lt;code&gt;client_credentials&lt;/code&gt; grant, with the IdP trusting STS as the client’s signing authority (&lt;code&gt;private_key_jwt&lt;/code&gt;). Use it when you want secretless authentication and your IdP models external workloads as signed-JWT clients.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Scenarios 2 and 3 in one sentence.&lt;/strong&gt; Both mint the &lt;em&gt;same&lt;/em&gt; STS JWT and share the same benefit: nothing shared can leak, because there is no secret. They differ only in &lt;em&gt;where the STS JWT sits in the token request&lt;/em&gt;. &lt;code&gt;IAM_JWT_BEARER&lt;/code&gt; sends it as the &lt;code&gt;assertion&lt;/code&gt; (the badge &lt;em&gt;is&lt;/em&gt; the request), while &lt;code&gt;CLIENT_CREDENTIALS_ASSERTION&lt;/code&gt; sends it as the &lt;code&gt;client_assertion&lt;/code&gt; on a standard &lt;code&gt;client_credentials&lt;/code&gt; request (the badge is ID on the form). That single difference is what you register on the IdP: AWS STS as an external token issuer, or as the client’s signing authority.&lt;/p&gt; 
&lt;h2 id="solution-overview"&gt;Solution overview&lt;/h2&gt; 
&lt;p&gt;Now that you can map your setup to a grant type, the next question is where these pieces actually run. MSK Replicator runs on AWS managed infrastructure but attaches elastic network interfaces (ENIs) into the subnets of the target Amazon MSK cluster’s virtual private cloud (VPC) and initiates every connection from there under a Service Execution Role (SER). Those ENIs sit in private subnets that typically have no NAT or internet gateway, so each external dependency needs an explicit network path. The following diagram shows the full topology for an OAuth migration, including the two pieces that are commonly missed: &lt;strong&gt;STS Outbound Web Identity Federation&lt;/strong&gt; (for the secretless grants) and the &lt;strong&gt;interface VPC endpoints&lt;/strong&gt; for STS and Secrets Manager.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-5.jpg" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-5.jpg" alt="Deployment architecture: the source environment holds the IdP and Kafka brokers; the AWS account holds STS, Secrets Manager, and the Amazon MSK VPC, whose private subnets contain the Replicator ENIs and target cluster, reached through interface VPC endpoints." width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 5: Deployment architecture. The source environment holds the IdP and Kafka brokers. The AWS account holds STS, Secrets Manager, and the Amazon MSK VPC, whose private subnets contain the Replicator ENIs and target cluster, reached through interface VPC endpoints.&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The &lt;strong&gt;source environment&lt;/strong&gt; (on the left, shown as on-premises here, but it can equally be another cloud or a self-managed cluster on AWS) holds two components: the &lt;strong&gt;IdP token endpoint and JWKS&lt;/strong&gt; (Keycloak, Okta, Entra ID) and the &lt;strong&gt;external Kafka brokers&lt;/strong&gt; on a SASL_SSL / OAUTHBEARER listener. Everything else runs in your AWS account.&lt;/p&gt; 
&lt;p&gt;The two dotted lines are &lt;strong&gt;trust relationships you configure ahead of time&lt;/strong&gt;, not runtime calls:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;External Kafka validates token by using IdP JWKS –&lt;/strong&gt; The broker checks every presented access token against the IdP’s published public keys. This applies to all grants.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;IdP trusts STS issuer through JWKS –&lt;/strong&gt; For the secretless grants only, the IdP is configured to trust your account’s STS issuer and validate the STS-signed JWT against STS’s JWKS. When STS Outbound Web Identity Federation is enabled, AWS provisions a per-account issuer URL (&lt;code&gt;https://&amp;lt;id&amp;gt;.tokens.sts.global.api.aws&lt;/code&gt;) whose JWKS the IdP trusts. This trust is not used by CLIENT_CREDENTIALS.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The numbered arrows are the runtime flow, all originating from the Replicator ENIs:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Step 1: Fetch client credentials and the CA certificate&lt;/strong&gt; from AWS Secrets Manager, through its VPC endpoint. For CLIENT_CREDENTIALS this includes the &lt;code&gt;client_id&lt;/code&gt;/&lt;code&gt;client_secret&lt;/code&gt;. For the secretless grants it is only the CA certificate(s).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Step 1a (optional): Call GetWebIdentityToken&lt;/strong&gt; on AWS STS, through the STS VPC endpoint, to mint a JWT of Replicator’s AWS identity. Required only for IAM_JWT_BEARER and CLIENT_CREDENTIALS_ASSERTION.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Step 2: Get a signed JWT access token&lt;/strong&gt; from the IdP token endpoint, exchanging either the client secret or the STS JWT depending on the grant.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Step 3: Present the token to the external Kafka&lt;/strong&gt; brokers over SASL/OAUTHBEARER.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Step 4: Replicate to the target&lt;/strong&gt; Amazon MSK cluster using IAM authentication.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The two supporting pieces inside the VPC, the &lt;strong&gt;Secrets Manager&lt;/strong&gt; and &lt;strong&gt;STS interface VPC endpoints&lt;/strong&gt;, are commonly overlooked precisely because the private subnets have no NAT or internet gateway. We cover exactly why they’re needed, and when, in the following section, &lt;a href="#cross-cutting-requirements"&gt;Cross-cutting requirements&lt;/a&gt;.&lt;/p&gt; 
&lt;h2 id="configuring-and-creating-the-replicator"&gt;Configuring and creating the replicator&lt;/h2&gt; 
&lt;p&gt;With the architecture in mind, you can now configure Replicator itself. MSK Replicator models OAuth through a &lt;code&gt;saslOAuthBearer&lt;/code&gt; structure on the external cluster’s &lt;code&gt;clientAuthentication&lt;/code&gt;. Exactly &lt;strong&gt;one&lt;/strong&gt; of three mechanism members must be present: &lt;code&gt;clientCredentials&lt;/code&gt;, &lt;code&gt;iamJwtBearer&lt;/code&gt;, or &lt;code&gt;clientCredentialsAssertion&lt;/code&gt;. The control plane enforces this mutual exclusivity. Fields shared across all three (&lt;code&gt;tokenEndpointUrl&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;tokenEndpointAuthenticationMethod&lt;/code&gt;, &lt;code&gt;tokenEndpointTlsCertificateArn&lt;/code&gt;, and &lt;code&gt;saslExtensions&lt;/code&gt;) live at the &lt;code&gt;saslOAuthBearer&lt;/code&gt; level.&lt;/p&gt; 
&lt;p&gt;Before the per-grant details, here are the requirements that apply to every OAuth migration, whichever grant you choose. Most OAuth setup failures trace back to one of these, so review them first.&lt;/p&gt; 
&lt;h3 id="cross-cutting-requirements"&gt;Cross-cutting requirements&lt;/h3&gt; 
&lt;p&gt;Here are the five items that apply to every grant: TLS trust, secret format, network reachability, the Service Execution Role, and STS federation.&lt;/p&gt; 
&lt;h4 id="a-tls-everywhere-and-two-separate-trust-settings"&gt;a) TLS everywhere, and two separate trust settings&lt;/h4&gt; 
&lt;p&gt;Replicator connects to two TLS endpoints, and they are configured &lt;strong&gt;independently&lt;/strong&gt;:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code&gt;encryptionInTransit.rootCaCertificate&lt;/code&gt;: the CA that signed your &lt;strong&gt;Kafka brokers’&lt;/strong&gt; TLS certificates (the SASL_SSL listener – &lt;code&gt;:9096&lt;/code&gt;).&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;tokenEndpointTlsCertificateArn&lt;/code&gt;: the CA that signed your &lt;strong&gt;IdP’s token endpoint&lt;/strong&gt; TLS certificate (for example – Keycloak on &lt;code&gt;:8443&lt;/code&gt;).&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;If your broker and IdP are signed by the same private CA, you still must supply the CA &lt;strong&gt;in both fields&lt;/strong&gt;. Omitting &lt;code&gt;tokenEndpointTlsCertificateArn&lt;/code&gt; when the IdP uses a private or self-signed cert produces a &lt;code&gt;PKIX path building failed&lt;/code&gt; error during token acquisition. Because that fails before workers stabilize, you’ll see a generic failure with no worker logs. If your IdP uses a publicly-trusted certificate (for example, it sits behind a public endpoint), you can omit &lt;code&gt;tokenEndpointTlsCertificateArn&lt;/code&gt; entirely.&lt;/p&gt; 
&lt;h4 id="b-secret-format-store-keyvalue-pairs-not-raw-values"&gt;b) Secret format: store key/value pairs, not raw values&lt;/h4&gt; 
&lt;p&gt;Every secret Replicator reads (client credentials, CA certificate) is parsed by the config provider as a set of &lt;strong&gt;key/value pairs&lt;/strong&gt;. Use the Secrets Manager console’s &lt;strong&gt;Key/value&lt;/strong&gt; editor rather than pasting raw text, and it will serialize and escape the values for you.&lt;/p&gt; 
&lt;p&gt;The keys the provider expects:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Key&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Used for&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;code&gt;certificate&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;the CA in PEM (newlines escaped as &lt;code&gt;\n&lt;/code&gt;)&lt;/td&gt; 
   &lt;td&gt;CA-certificate secrets (&lt;code&gt;rootCaCertificate&lt;/code&gt;, &lt;code&gt;tokenEndpointTlsCertificateArn&lt;/code&gt;)&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;code&gt;client_id&lt;/code&gt;, &lt;code&gt;client_secret&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;your OAuth client credentials&lt;/td&gt; 
   &lt;td&gt;the &lt;code&gt;CLIENT_CREDENTIALS&lt;/code&gt; token-request secret&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;&lt;strong&gt;Custom parameters, headers, and SASL extensions.&lt;/strong&gt; Some IdPs require extra data on the token request, and some brokers require SASL/OAUTHBEARER extensions. The config provider supports both through reserved key prefixes in the same secret:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Prefix&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Effect&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Example key&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Example value&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;code&gt;custom_param.&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;adds a parameter to the token request sent to the IdP&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;custom_param.tenant_token&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;myTenantToken&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;code&gt;custom_header.&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;adds an HTTP header to the IdP token request&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;custom_header.X-Tenant-Id&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;acme&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;code&gt;extension.&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;adds a SASL/OAUTHBEARER extension presented to the broker (for example, Confluent Cloud’s &lt;code&gt;logicalCluster&lt;/code&gt;)&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;extension.logicalCluster&lt;/code&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;myLogicalClusterId&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;For example, an IdP that expects a tenant token as a request parameter and a Confluent Cloud broker that requires a logical-cluster extension would add &lt;code&gt;custom_param.tenant_token&lt;/code&gt; and &lt;code&gt;extension.logicalCluster&lt;/code&gt; as extra key/value pairs alongside &lt;code&gt;client_id&lt;/code&gt;/&lt;code&gt;client_secret&lt;/code&gt; in the same secret.&lt;/p&gt; 
&lt;h4 id="c-network-reachability-from-replicators-enis"&gt;c) Network reachability from Replicator’s ENIs&lt;/h4&gt; 
&lt;p&gt;Replicator attaches ENIs into the subnets you specify (through the target &lt;code&gt;amazonMskCluster&lt;/code&gt; cluster’s &lt;code&gt;vpcConfig&lt;/code&gt;) and initiates &lt;em&gt;all&lt;/em&gt; connections from there. Those ENIs must be able to reach:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Your external brokers&lt;/strong&gt;, over VPC peering, AWS Transit Gateway, AWS Direct Connect, or VPN, with security groups permitting the SASL_SSL port.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Your IdP’s token endpoint&lt;/strong&gt;, over the same networking. The endpoint hostname must resolve from those subnets.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Secrets Manager&lt;/strong&gt;, to fetch credentials/CA. If the subnets have no NAT/internet gateway, add an &lt;strong&gt;interface VPC endpoint&lt;/strong&gt; for &lt;code&gt;com.amazonaws.&amp;lt;region&amp;gt;.secretsmanager&lt;/code&gt; with private DNS.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS STS&lt;/strong&gt; (only for &lt;code&gt;IAM_JWT_BEARER&lt;/code&gt; and &lt;code&gt;CLIENT_CREDENTIALS_ASSERTION&lt;/code&gt;), to call &lt;code&gt;GetWebIdentityToken&lt;/code&gt;. In no-egress subnets this &lt;strong&gt;will time out&lt;/strong&gt; (&lt;code&gt;STS GetWebIdentityToken call failed: Connect timed out&lt;/code&gt;) unless you add an interface VPC endpoint for &lt;code&gt;com.amazonaws.&amp;lt;region&amp;gt;.sts&lt;/code&gt; with private DNS. This is the most common oversight for the secretless grants.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Both endpoints use private DNS, so the standard &lt;code&gt;secretsmanager.&amp;lt;region&amp;gt;.amazonaws.com&lt;/code&gt; and &lt;code&gt;sts.&amp;lt;region&amp;gt;.amazonaws.com&lt;/code&gt; hostnames resolve to the endpoint inside the VPC, with no client change needed.&lt;/p&gt; 
&lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;A note on&lt;/strong&gt; &lt;code&gt;vpcConfig&lt;/code&gt; &lt;strong&gt;placement.&lt;/strong&gt; For an external Apache Kafka cluster, &lt;code&gt;vpcConfig&lt;/code&gt; is specified on the &lt;strong&gt;target&lt;/strong&gt; &lt;code&gt;amazonMskCluster&lt;/code&gt; entry, not the external &lt;code&gt;apacheKafkaCluster&lt;/code&gt; entry. The API rejects a &lt;code&gt;vpcConfig&lt;/code&gt; on the external cluster. The ENIs it creates are what reach &lt;em&gt;both&lt;/em&gt; clusters and all AWS endpoints.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;h4 id="d-the-service-execution-role-ser"&gt;d) The Service Execution Role (SER)&lt;/h4&gt; 
&lt;p&gt;Replicator assumes an IAM role to do its work. Two parts matter:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Trust policy –&lt;/strong&gt; Must allow the Replicator service to assume it. &lt;code&gt;kafka.amazonaws.com&lt;/code&gt; needs to be trusted. A trust policy that is too narrow fails with &lt;code&gt;AccessDenied.ServiceExecutionRoleUnassumable&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Permissions –&lt;/strong&gt; The replication permissions are extensive and depend on which features you enable, so follow the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/msk-replicator-permissions-reference.html" target="_blank" rel="noopener"&gt;service execution role&lt;/a&gt; permissions reference to build a least-privilege policy.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h4 id="e-enabling-sts-outbound-web-identity-federation-secretless-grants-only"&gt;e) Enabling STS Outbound Web Identity Federation (secretless grants only)&lt;/h4&gt; 
&lt;p&gt;For &lt;code&gt;IAM_JWT_BEARER&lt;/code&gt; and &lt;code&gt;CLIENT_CREDENTIALS_ASSERTION&lt;/code&gt;, &lt;code&gt;sts:GetWebIdentityToken&lt;/code&gt; must be enabled for your account/role. When enabled, AWS provisions a dedicated &lt;strong&gt;issuer URL&lt;/strong&gt; of the form &lt;code&gt;https://&amp;lt;uuid&amp;gt;.tokens.sts.global.api.aws&lt;/code&gt;. Every JWT STS mints for your account carries this as its &lt;code&gt;iss&lt;/code&gt; claim, and its public keys are published under this issuer’s JWKS. &lt;strong&gt;You configure your IdP to trust this issuer.&lt;/strong&gt; Granting the &lt;code&gt;sts:GetWebIdentityToken&lt;/code&gt; IAM action is necessary but not sufficient. The account-level federation feature must also be turned on.&lt;/p&gt; 
&lt;h3 id="create-the-replicator"&gt;Create the replicator&lt;/h3&gt; 
&lt;p&gt;A repeatable way to create the replicator is with a request file and &lt;code&gt;--cli-input-json&lt;/code&gt;, so you can keep the full configuration under version control. The following example is a complete &lt;code&gt;CLIENT_CREDENTIALS&lt;/code&gt; request. The two secretless variants change only the &lt;code&gt;saslOAuthBearer&lt;/code&gt; block (shown after).&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws kafka create-replicator \
  --region &amp;lt;region&amp;gt; \
  --cli-input-json file://create-replicator.json&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;code&gt;create-replicator.json&lt;/code&gt;:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "replicatorName": "oauth-migration-replicator",
  "serviceExecutionRoleArn": "arn:aws:iam::&amp;lt;acct&amp;gt;:role/msk-replicator-execution-role",
  "kafkaClusters": [
    {
      "apacheKafkaCluster": {
        "apacheKafkaClusterId": "&amp;lt;source-cluster-id&amp;gt;",
        "bootstrapBrokerString": "b-1.ext-kafka.example.com:9096,b-2.ext-kafka.example.com:9096"
      },
      "clientAuthentication": {
        "saslOAuthBearer": {
          "tokenEndpointUrl": "https://idp.example.com/realms/kafka/protocol/openid-connect/token",
          "clientCredentials": {
            "tokenRequestSecretArn": "arn:aws:secretsmanager:&amp;lt;region&amp;gt;:&amp;lt;acct&amp;gt;:secret:&amp;lt;oauth-creds&amp;gt;"
          },
          "tokenEndpointAuthenticationMethod": "POST",
          "tokenEndpointTlsCertificateArn": "arn:aws:secretsmanager:&amp;lt;region&amp;gt;:&amp;lt;acct&amp;gt;:secret:&amp;lt;idp-ca&amp;gt;"
        }
      },
      "encryptionInTransit": {
        "encryptionType": "TLS",
        "rootCaCertificate": "arn:aws:secretsmanager:&amp;lt;region&amp;gt;:&amp;lt;acct&amp;gt;:secret:&amp;lt;broker-ca&amp;gt;"
      }
    },
    {
      "amazonMskCluster": {
        "mskClusterArn": "arn:aws:kafka:&amp;lt;region&amp;gt;:&amp;lt;acct&amp;gt;:cluster/target-msk/&amp;lt;uuid&amp;gt;"
      },
      "vpcConfig": {
        "subnetIds": [
          "subnet-aaaa",
          "subnet-bbbb",
          "subnet-cccc"
        ],
        "securityGroupIds": [
          "sg-xxxxxxxx"
        ]
      }
    }
  ],
  "replicationInfoList": [
    {
      "sourceKafkaClusterId": "&amp;lt;source-cluster-id&amp;gt;",
      "targetKafkaClusterArn": "arn:aws:kafka:&amp;lt;region&amp;gt;:&amp;lt;acct&amp;gt;:cluster/target-msk/&amp;lt;uuid&amp;gt;",
      "targetCompressionType": "NONE",
      "topicReplication": {
        "topicsToReplicate": [
          ".*"
        ],
        "detectAndCopyNewTopics": true,
        "copyTopicConfigurations": true
      },
      "consumerGroupReplication": {
        "consumerGroupsToReplicate": [
          ".*"
        ],
        "detectAndCopyNewConsumerGroups": true,
        "synchroniseConsumerGroupOffsets": true
      }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Field names and exact nesting follow the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/msk-replicator.html" target="_blank" rel="noopener"&gt;create-replicator API reference&lt;/a&gt;. Check it for the full schema and any Region-specific values.&lt;/p&gt; 
&lt;p&gt;The example above uses &lt;code&gt;CLIENT_CREDENTIALS&lt;/code&gt;. For the full schema, any Region-specific values, and detailed examples for the other grant types, check the &lt;span&gt;&lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/msk-replicator-external-api-examples.html" target="_blank" rel="noopener noreferrer"&gt;MSK documentation&lt;/a&gt;&lt;/span&gt;.&lt;/p&gt; 
&lt;h3 id="recommended-order-of-operations"&gt;Recommended order of operations&lt;/h3&gt; 
&lt;p&gt;With the requirements and configuration in hand, here is the order to put them in:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Pick your grant type&lt;/strong&gt; using the preceding &lt;a href="#which-grant-to-use"&gt;decision table&lt;/a&gt;. &lt;code&gt;CLIENT_CREDENTIALS&lt;/code&gt; is the fastest path if you already manage a client secret. Otherwise choose a secretless grant based on how your IdP models external workloads. For a multi-hop internal chain, use &lt;code&gt;IAM_JWT_BEARER&lt;/code&gt; against the proxy pattern described in the next section.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Prepare the IdP:&lt;/strong&gt; create the client (or the STS-trust configuration), and note the exact token endpoint URL and issuer.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Stage secrets in Secrets Manager&lt;/strong&gt;, as JSON (requirement b): client credentials (if any) and the CA certificate(s).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Wire the network&lt;/strong&gt; (requirement c): connectivity from Replicator’s subnets to your brokers and IdP, plus interface VPC endpoints for Secrets Manager and (secretless grants only) STS, both with private DNS.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;[Optional but recommended]: Smoke-test the path from inside the VPC –&lt;/strong&gt; IdP setup is often the part that takes the most iterations, and Replicator provisioning is a slow way to discover a misconfigured token endpoint or a missing TLS trust. Spin up a small EC2 instance in Replicator’s subnets, install a Kafka client, and run an end-to-end produce/consume against the external brokers using SASL/OAUTHBEARER (a &lt;code&gt;client_credentials&lt;/code&gt; flow is simplest). This validates the three things most likely to be wrong (network reachability to the IdP and brokers, both TLS trusts for the broker CA and IdP CA, and token vending) while you can still fix them in seconds. Tear the instance down once the round trip works.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Enable STS Outbound Web Identity Federation&lt;/strong&gt; (requirement e. Secretless grants only) and configure your IdP to trust the resulting issuer.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Build the SER&lt;/strong&gt; (requirement d) with a trust policy the Replicator service can assume and the required permissions.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Create the replicator&lt;/strong&gt; with the &lt;a href="#create-the-replicator"&gt;create-replicator request&lt;/a&gt; for your grant. Remember both TLS trust fields for a private-CA IdP (requirement a), and &lt;code&gt;vpcConfig&lt;/code&gt; on the target entry only.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Verify –&lt;/strong&gt; Produce to a topic on the external cluster and confirm the records land on the target (consume with IAM auth on the Amazon MSK side). Then watch the health signals: 
  &lt;ul&gt; 
   &lt;li&gt;In the Amazon MSK console, the replicator should reach the &lt;code&gt;RUNNING&lt;/code&gt; state.&lt;/li&gt; 
   &lt;li&gt;In Amazon CloudWatch, under the AWS/Kafka namespace, watch the replicator’s ReplicationLatency and MessageLag metrics. Both should be low and stable, and MessageLag should trend toward zero as it catches up.&lt;/li&gt; 
   &lt;li&gt;A healthy replicator commits offsets continuously. A steady “1 message per batch” with no producer activity is only the internal heartbeat topic, not a stall.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="handling-an-additional-identity-layer-the-federation-proxy-pattern"&gt;Handling an additional identity layer: the federation-proxy pattern&lt;/h2&gt; 
&lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;Who owns what –&lt;/strong&gt; Before the details, the ownership line is simple and worth stating up front:&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;What Replicator guarantees:&lt;/strong&gt; it calls the configured &lt;code&gt;tokenEndpointUrl&lt;/code&gt; with the configured grant, includes the STS JWT, expects a standard &lt;code&gt;{access_token, token_type, expires_in}&lt;/code&gt; response, and refreshes before expiry.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;What you own:&lt;/strong&gt; everything at and behind the proxy, including validating the STS JWT, the downstream token exchanges, claim mapping, and the availability and latency of the endpoint. The proxy runs in your VPC and is owned entirely by you.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;So far we have assumed you can point Replicator at a single token endpoint. Some organizations can’t. Instead, they have an &lt;strong&gt;internal identity chain&lt;/strong&gt;: several hops of token exchange and federation that a workload must traverse before it holds a token the Kafka brokers accept.&lt;/p&gt; 
&lt;p&gt;A representative example is a large financial institution whose chain has several hops: an AWS workload’s identity (a signed &lt;code&gt;GetCallerIdentity&lt;/code&gt; request) is exchanged at an internal &lt;strong&gt;Token Exchange service&lt;/strong&gt; for an intermediate JWT, which an &lt;strong&gt;internal IdP&lt;/strong&gt; then consumes as a &lt;code&gt;client_assertion&lt;/code&gt; to issue the final Bearer token the Kafka brokers accept.&lt;/p&gt; 
&lt;p&gt;Replicator connects to a single HTTPS token endpoint using one of the three grant types and expects a standard token response. When the identity flow spans multiple hops like this, you place a proxy in front of that chain so Replicator still sees a single endpoint.&lt;/p&gt; 
&lt;h3 id="the-solution-a-customer-owned-proxy"&gt;The solution: a customer-owned proxy&lt;/h3&gt; 
&lt;p&gt;You deploy a small proxy in your own VPC that collapses the chain behind a single endpoint. From Replicator’s perspective, this is an ordinary OAuth flow against one token endpoint. Everything behind that endpoint is opaque to Replicator and owned entirely by you.&lt;/p&gt; 
&lt;p&gt;The grant Replicator uses to reach the proxy is a separate choice from the exchanges happening behind it. We recommend a secretless grant (IAM_JWT_BEARER or CLIENT_CREDENTIALS_ASSERTION) so there is no long-lived secret between Replicator and the proxy. CLIENT_CREDENTIALS is also valid if you would rather the proxy authenticate Replicator with a client secret. The following walkthrough uses IAM_JWT_BEARER, where the proxy validates the STS JWT that Replicator presents.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;How it works, end to end.&lt;/strong&gt; The following sequence diagram traces the full token exchange, from Replicator’s request to the Bearer it finally presents to the external Kafka brokers.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-6.jpg" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-6.jpg" alt="Federation-proxy token flow: the proxy validates Replicator’s STS JWT, exchanges its own AWS identity at the Token Exchange service for an intermediate JWT, presents that to the internal IdP, and returns the resulting Bearer token to Replicator." width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 6: Federation-proxy token flow. The proxy validates Replicator’s STS JWT, exchanges its own AWS identity at the Token Exchange service for an intermediate JWT, presents that to the internal IdP, and returns the resulting Bearer to Replicator.&lt;/p&gt;
&lt;/div&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Replicator to proxy –&lt;/strong&gt; Replicator POSTs its STS JWT as &lt;code&gt;assertion&lt;/code&gt; to the proxy’s token endpoint, a plain &lt;code&gt;IAM_JWT_BEARER&lt;/code&gt; request (&lt;code&gt;grant_type=jwt-bearer&lt;/code&gt;). Because the endpoint is private, Replicator reaches it through an &lt;code&gt;execute-api&lt;/code&gt; &lt;strong&gt;interface VPC endpoint&lt;/strong&gt;, the same private-connectivity approach used for Secrets Manager and STS. (Replicator first obtains the STS JWT by calling STS &lt;code&gt;GetWebIdentityToken&lt;/code&gt; through the STS VPC endpoint.)&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Proxy validates the STS JWT&lt;/strong&gt; (signature against STS’s JWKS, plus &lt;code&gt;iss&lt;/code&gt;/&lt;code&gt;aud&lt;/code&gt;/&lt;code&gt;exp&lt;/code&gt;/&lt;code&gt;sub&lt;/code&gt; checks. The &lt;code&gt;sub&lt;/code&gt; is the caller’s AWS ARN).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Proxy to Token Exchange service –&lt;/strong&gt; The proxy exchanges its own AWS identity, presented as a signed &lt;code&gt;GetCallerIdentity&lt;/code&gt; request, at the internal Token Exchange service.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Token Exchange service → proxy –&lt;/strong&gt; It returns a signed intermediate JWT.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Proxy to internal IdP –&lt;/strong&gt; The proxy makes a &lt;code&gt;client_credentials&lt;/code&gt; request that carries the intermediate JWT as the &lt;code&gt;client_assertion&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Internal IdP to proxy –&lt;/strong&gt; The IdP issues the final Bearer access token.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Proxy to Replicator –&lt;/strong&gt; The proxy returns the Bearer, and Replicator presents it to the external brokers over SASL/OAUTHBEARER. The brokers validate it against the &lt;strong&gt;final IdP’s&lt;/strong&gt; JWKS, a completely ordinary OAuth handshake from their point of view.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="reference-architecture"&gt;Reference architecture&lt;/h3&gt; 
&lt;p&gt;Here is the reference architecture for the end-to-end solution.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-7.jpg" target="_blank" rel="noopener"&gt;&lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-7.jpg" alt="Federation-proxy reference architecture: Replicator ENIs in a private subnet call a customer-owned proxy (a Lambda function behind a private API Gateway) that runs the on-premises identity chain over Direct Connect before Replicator replicates into the target Amazon MSK cluster." width="800"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 7: Federation-proxy reference architecture. Replicator ENIs in a private subnet call a customer-owned proxy (a Lambda behind a private API Gateway), which runs the on-premises identity chain over Direct Connect before Replicator replicates into the target Amazon MSK cluster.&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Everything on the Replicator side runs in your VPC’s private subnets: the Replicator ENIs, the customer-owned proxy, and the target Amazon MSK cluster. The proxy here is an AWS Lambda function behind a private Amazon API Gateway, but it can run on any compute you prefer (EC2, ECS, or EKS) as long as it exposes a single private HTTPS token endpoint. Connectivity to the on-premises Token Exchange service, internal IdP, and Kafka brokers runs over AWS Direct Connect (a VPN or VPC peering works too).&lt;/p&gt; 
&lt;p&gt;The outer legs of this flow are exactly the base migration from &lt;a href="#solution-overview"&gt;Solution overview&lt;/a&gt;: step &lt;strong&gt;1&lt;/strong&gt; (fetch the broker CA from Secrets Manager), step &lt;strong&gt;1a&lt;/strong&gt; (mint the STS JWT through STS), step &lt;strong&gt;3&lt;/strong&gt; (present the Bearer to the brokers), and step &lt;strong&gt;4&lt;/strong&gt; (replicate to the target with IAM). What’s &lt;strong&gt;new here&lt;/strong&gt; is the proxy hop in the middle, which replaces the single “step 2” call to a token endpoint:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;2. POST /token –&lt;/strong&gt; Replicator sends the STS JWT as the &lt;code&gt;assertion&lt;/code&gt; to the proxy’s private token endpoint, reached through the &lt;code&gt;execute-api&lt;/code&gt; interface VPC endpoint. The proxy validates it against STS’s JWKS.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;2a. Exchange AWS identity –&lt;/strong&gt; The proxy presents its own AWS identity (a signed &lt;code&gt;GetCallerIdentity&lt;/code&gt; request) to the internal Token Exchange service and gets back a signed intermediate JWT.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;2b. Present as&lt;/strong&gt; &lt;code&gt;client_assertion&lt;/code&gt; &lt;strong&gt;–&lt;/strong&gt; The proxy sends a &lt;code&gt;client_credentials&lt;/code&gt; request to the internal IdP with the intermediate JWT as the &lt;code&gt;client_assertion&lt;/code&gt;, and receives the final Bearer.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;2c. Final Bearer token –&lt;/strong&gt; The proxy returns the Bearer to Replicator, which then continues at step 3.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;As in the base architecture, the dotted lines are prerequisite trust relationships, not runtime calls: the &lt;strong&gt;proxy trusts AWS STS&lt;/strong&gt; as an issuer (validating the STS JWT against STS’s JWKS), and the &lt;strong&gt;Kafka brokers validate the final Bearer&lt;/strong&gt; against the internal IdP’s JWKS.&lt;/p&gt; 
&lt;p&gt;One subtlety worth calling out is the &lt;strong&gt;split of TLS trust&lt;/strong&gt;. Replicator connects directly only to the private API Gateway (which uses a publicly trusted certificate) and to the Kafka brokers, so the only certificate it fetches from Secrets Manager is the &lt;strong&gt;broker CA&lt;/strong&gt;. The &lt;strong&gt;internal IdP’s CA is the proxy’s concern&lt;/strong&gt;: the proxy terminates TLS to the Token Exchange service and internal IdP, so it carries their CA material, not Replicator.&lt;/p&gt; 
&lt;p&gt;The same single-endpoint pattern handles other “extra layer” scenarios without any Replicator change: claim enrichment (the proxy intercepts and augments), rate-limited IdPs (the proxy caches tokens), IdPs requiring mTLS (the proxy terminates Replicator’s HTTPS and initiates mTLS onward), and IdP migrations (swap the proxy’s target without touching Replicator config).&lt;/p&gt; 
&lt;p&gt;A working reference implementation of this customer-owned proxy is available at &lt;a href="https://github.com/aws-samples/sample-federation-proxy-oauth" target="_blank" rel="noopener"&gt;GitHub&lt;/a&gt;.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post, we walked through how to migrate a self-managed, OAuth-authenticated Apache Kafka cluster to Amazon MSK using MSK Replicator: how the SASL/OAUTHBEARER handshake works, how to map your identity provider to one of the three supported grant types, the deployment architecture and prerequisites that the connection depends on, and how to handle identity providers that sit behind an additional federation layer. To get started, see the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/what-is-msk.html" target="_blank" rel="noopener"&gt;Amazon MSK Developer Guide&lt;/a&gt; and the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/msk-replicator.html" target="_blank" rel="noopener"&gt;Amazon MSK Replicator documentation&lt;/a&gt;. For the federation-proxy example, see the &lt;a href="https://github.com/aws-samples/sample-federation-proxy-oauth" target="_blank" rel="noopener"&gt;sample implementation on GitHub&lt;/a&gt;.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the author&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6223-8.jpeg" alt="Subham Rakshit" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Subham Rakshit&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/subhamrakshit/" target="_blank" rel="noopener"&gt;Subham&lt;/a&gt; is a Streaming Solutions Architect for Analytics at AWS based in the UK. He works with customers to design and build search and streaming data platforms that help them achieve their business objective. Outside of work, he enjoys spending time solving jigsaw puzzles with his daughters.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Announcing in-place ZooKeeper-to-KRaft cluster upgrades for Amazon MSK</title>
		<link>https://aws.amazon.com/blogs/big-data/announcing-in-place-zookeeper-to-kraft-cluster-upgrades-for-amazon-msk/</link>
		
		<dc:creator><![CDATA[Austin Groeneveld]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 18:59:47 +0000</pubDate>
				<category><![CDATA[Amazon Managed Streaming for Apache Kafka (Amazon MSK)]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Foundational (100)]]></category>
		<guid isPermaLink="false">7c500ad253406bc206d814f987095098940c8433</guid>

					<description>Amazon MSK now supports in-place upgrades from ZooKeeper to KRaft metadata mode. You can modernize your existing cluster's metadata management through the familiar version upgrade workflow, with no new cluster to provision and no data migration. This post covers the prerequisites and the step-by-step upgrade process.</description>
										<content:encoded>&lt;p&gt;Apache Kafka 4.0 officially removes ZooKeeper. If your &lt;a href="https://aws.amazon.com/msk/" target="_blank" rel="noopener"&gt;Amazon Managed Streaming for Apache Kafka&lt;/a&gt; (Amazon MSK) Provisioned clusters still run in ZooKeeper metadata mode, now is the time to plan your migration. Amazon MSK now supports in-place upgrades from ZooKeeper to KRaft metadata mode, so you can modernize your existing cluster’s metadata management through the familiar version upgrade workflow.&lt;/p&gt; 
&lt;p&gt;For more than a decade, Apache ZooKeeper provided dependable metadata management for Kafka, including controller election, partition state, broker registration, and topic configuration. With &lt;a href="https://kafka.apache.org/blog/2025/03/18/apache-kafka-4.0.0-release-announcement/" target="_blank" rel="noopener"&gt;Apache Kafka 4.0&lt;/a&gt;, ZooKeeper is officially removed in favor of KRaft, an embedded Raft-based consensus protocol that handles metadata management internally. It brings those responsibilities into Apache Kafka itself, creating a more streamlined foundation for the continued evolution of Kafka. &lt;a href="https://aws.amazon.com/blogs/big-data/introducing-support-for-apache-kafka-on-raft-mode-kraft-with-amazon-msk-clusters/" target="_blank" rel="noopener"&gt;Amazon MSK has supported KRaft-mode clusters since May 2024&lt;/a&gt;, and all Kafka 4.x versions on Amazon MSK use KRaft.&lt;/p&gt; 
&lt;p&gt;With the in-place upgrade, you can retain your cluster data and metadata while Amazon MSK manages the control-plane transition. Your cluster remains available for produce and consume traffic throughout the process, with no expected downtime if you’re following &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/version-upgrades-best-practices.html" target="_blank" rel="noopener"&gt;best practices&lt;/a&gt;. By using the existing version upgrade workflow, the move to KRaft becomes a natural step in your cluster’s lifecycle. This prepares your cluster for Kafka 4.x and future Kafka releases.&lt;/p&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;p&gt;Before initiating the upgrade, review the following requirements to confirm your cluster is ready for the transition.&lt;/p&gt; 
&lt;h3 id="supported-source-versions"&gt;Supported source versions&lt;/h3&gt; 
&lt;p&gt;Clusters must be running Kafka 3.9.x in ZooKeeper mode to use the in-place upgrade. If your cluster is running an earlier version, such as 3.6.0, 3.7.x, or 3.8.x, first complete a standard in-place version upgrade to 3.9.x. You can then initiate the upgrade to &lt;code&gt;3.9.x.kraft&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;Kafka 3.9 is the bridge release for this transition because it supports both ZooKeeper and KRaft modes. To support customers through this migration process, Amazon MSK provides extended support for 3.9.x for a minimum of 2 years from its April 2025 release.&lt;/p&gt; 
&lt;h3 id="client-compatibility"&gt;Client compatibility&lt;/h3&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Requirement&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Detail&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Minimum client library&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;Apache Kafka client v3.0+&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Recommended client version&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;v3.9 or above&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Connection strings&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;Must use &lt;code&gt;bootstrap.servers&lt;/code&gt; only. Any ZooKeeper connection strings (the &lt;code&gt;--zookeeper&lt;/code&gt; flag) must be removed before upgrade.&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The &lt;code&gt;--zookeeper&lt;/code&gt; admin flag was deprecated in Kafka 2.5 and removed in 3.0. Before upgrading, update any remaining applications or tools that connect directly to ZooKeeper.&lt;/p&gt; 
&lt;h3 id="pre-upgrade-checklist"&gt;Pre-upgrade checklist&lt;/h3&gt; 
&lt;p&gt;Before beginning the upgrade, confirm the following:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;For Standard brokers, the cluster must be deployed across three &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html" target="_blank" rel="noopener"&gt;Availability Zones&lt;/a&gt;. Express brokers provide this by default.&lt;/li&gt; 
 &lt;li&gt;The cluster is running Kafka 3.9.x in ZooKeeper mode.&lt;/li&gt; 
 &lt;li&gt;Standard brokers expose direct ZooKeeper access on ports 2181 (plaintext) and 2182 (TLS). Before upgrading, validate that you’ve disabled ZooKeeper access on the cluster and none of your applications rely on these connections.&lt;/li&gt; 
 &lt;li&gt;Solutions using &lt;a href="https://kafka.apache.org/43/getting-started/zk2kraft/" target="_blank" rel="noopener"&gt;dynamic Kafka configurations&lt;/a&gt; that relied on ZooKeeper have been removed before attempting the upgrade operation. 
  &lt;ul&gt; 
   &lt;li&gt;If you previously configured custom domain names on a ZooKeeper-based deployment using the dynamic override (&lt;code&gt;kafka-configs.sh --alter&lt;/code&gt; on &lt;code&gt;advertised.listeners&lt;/code&gt;), be aware that KRaft does not support this dynamic configuration. If you attempt to upgrade your MSK cluster to KRaft with altered &lt;code&gt;advertised.listeners&lt;/code&gt;, the upgrade operation fails.&lt;/li&gt; 
   &lt;li&gt;If you’re implementing your custom domain name solution on MSK moving forward with KRaft, we recommend our coinciding MSK release for &lt;a href="https://aws.amazon.com/blogs/big-data/amazon-msk-simplifies-configuring-custom-domain-names/" target="_blank" rel="noopener"&gt;custom domain name support&lt;/a&gt; by statically configuring the &lt;code&gt;custom.advertised.listeners&lt;/code&gt; property through the &lt;code&gt;UpdateClusterConfiguration&lt;/code&gt; API.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;The cluster has no under-replicated partitions.&lt;/li&gt; 
 &lt;li&gt;The cluster is running within per-broker partition limits for standard or express broker clusters.&lt;/li&gt; 
 &lt;li&gt;For clusters running above the KRaft brokers-per-cluster &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/limits.html" target="_blank" rel="noopener"&gt;limit&lt;/a&gt;, you might need an additional quota increase. If you previously raised a quota increase for your ZooKeeper brokers-per-cluster, submit another quota increase for the KRaft limit before attempting the upgrade.&lt;/li&gt; 
 &lt;li&gt;The cluster has enough reserve capacity to support rolling broker restarts while serving client traffic.&lt;/li&gt; 
 &lt;li&gt;As a best practice, verify that monitoring is ready for the transition from ZooKeeper-specific metrics to KRaft controller metrics. 
  &lt;ul&gt; 
   &lt;li&gt;After the migration, ZooKeeper-specific &lt;a href="https://aws.amazon.com/cloudwatch/" target="_blank" rel="noopener"&gt;Amazon CloudWatch&lt;/a&gt; metrics such as &lt;code&gt;ZookeeperRequestLatencyMsMean&lt;/code&gt; and &lt;code&gt;ZookeeperSessionState&lt;/code&gt; are no longer available.&lt;/li&gt; 
   &lt;li&gt;If you use Open Monitoring, Kafka also stops publishing ZooKeeper metrics. Plan to update or retire related alerts and dashboards as part of your migration preparation.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="how-the-upgrade-works"&gt;How the upgrade works&lt;/h2&gt; 
&lt;p&gt;When you initiate the upgrade, Amazon MSK performs a managed, multi-phase migration:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Controller quorum bootstrap:&lt;/strong&gt; Amazon MSK provisions KRaft controller nodes alongside the existing ZooKeeper infrastructure. Both systems operate in parallel during this phase.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Metadata migration:&lt;/strong&gt; The KRaft controller reads the cluster state from ZooKeeper and writes it to the internal KRaft metadata log.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Broker transition:&lt;/strong&gt; Amazon MSK performs a rolling update and registers with the KRaft controller quorum. Data plane operations remain available during the transition.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Validation and bake period:&lt;/strong&gt; Amazon MSK verifies cluster health under KRaft, including partition leadership, replication state, and controller responsiveness.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;ZooKeeper decommissioning:&lt;/strong&gt; After validation succeeds, Amazon MSK removes the ZooKeeper infrastructure and the cluster operates entirely in KRaft mode.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;During the upgrade, the cluster enters &lt;code&gt;UPDATING&lt;/code&gt; state. You can continue producing and consuming data, while Amazon MSK administrative API operations are temporarily unavailable until the cluster returns to &lt;code&gt;ACTIVE&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;Amazon MSK maintains a high bar for durability during the transition. It uses rigorous safety checks at each phase of the migration to protect customer metadata in both roll-forward and rollback scenarios.&lt;/p&gt; 
&lt;h3 id="built-in-recovery"&gt;Built-in recovery&lt;/h3&gt; 
&lt;p&gt;Amazon MSK monitors cluster health throughout the upgrade. If it detects a condition that prevents the migration from completing, it automatically returns the cluster to its pre-migration state. No customer action is required during recovery.&lt;/p&gt; 
&lt;p&gt;The operation status changes to &lt;strong&gt;Reverting to pre-migration state&lt;/strong&gt; while Amazon MSK restores the original Kafka version and reconnects ZooKeeper. After the cluster returns to &lt;code&gt;ACTIVE&lt;/code&gt;, the &lt;code&gt;describe-cluster-operation&lt;/code&gt; API provides error codes, failure reasons, and recommended remediation steps. You can use these to address the issue before starting the upgrade again.&lt;/p&gt; 
&lt;h2 id="how-to-perform-the-upgrade"&gt;How to perform the upgrade&lt;/h2&gt; 
&lt;p&gt;The following steps walk you through the upgrade process using the Amazon MSK console. You can also perform these steps programmatically using the AWS Command Line Interface (AWS CLI) or SDK.&lt;/p&gt; 
&lt;h3 id="step-1-disable-zookeeper-access-standard-brokers-only"&gt;Step 1: Disable ZooKeeper access (standard brokers only)&lt;/h3&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This step applies only to Standard broker clusters. Express broker clusters don’t expose direct ZooKeeper access and can skip directly to Step 2.&lt;/p&gt; 
&lt;p&gt;Standard brokers expose direct ZooKeeper access on ports 2181 (plaintext) and 2182 (TLS). Before upgrading, validate that none of your applications rely on these connections.&lt;/p&gt; 
&lt;p&gt;Navigate to your cluster’s &lt;strong&gt;Properties&lt;/strong&gt; tab, choose &lt;strong&gt;Network settings&lt;/strong&gt;, and then choose &lt;strong&gt;Edit ZooKeeper access&lt;/strong&gt;.&lt;/p&gt; 
&lt;div id="attachment_93890" style="width: 1412px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93890" loading="lazy" class="size-full wp-image-93890" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/image-33.png" alt="Figure 1: Editing ZooKeeper access from the cluster network settings" width="1402" height="636"&gt;
 &lt;p id="caption-attachment-93890" class="wp-caption-text"&gt;Figure 1: Editing ZooKeeper access from the cluster network settings&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;In the pop-up window, verify that &lt;strong&gt;ZooKeeper access&lt;/strong&gt; is set to &lt;strong&gt;Disabled&lt;/strong&gt;, and then choose &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6145-2.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6145-2.png" alt="Edit ZooKeeper access dialog with access set to Disabled and the Save button" width="800" height="582"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;Figure 2: Confirming ZooKeeper access is disabled&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Confirm that producers, consumers, and admin tooling continue operating normally without ZooKeeper connectivity. This step is fully reversible. Re-enable ZooKeeper access immediately if anything breaks.&lt;/p&gt; 
&lt;div id="attachment_93894" style="width: 1411px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93894" loading="lazy" class="size-full wp-image-93894" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/image-37.png" alt="" width="1401" height="597"&gt;
 &lt;p id="caption-attachment-93894" class="wp-caption-text"&gt;Figure 3: Verifying client traffic continues without ZooKeeper access&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="step-2-initiate-the-version-upgrade"&gt;Step 2: Initiate the version upgrade&lt;/h3&gt; 
&lt;p&gt;In the Amazon MSK console, under &lt;strong&gt;Properties&lt;/strong&gt;, choose &lt;strong&gt;Upgrade&lt;/strong&gt; in the &lt;strong&gt;Apache Kafka version&lt;/strong&gt; section.&lt;/p&gt; 
&lt;div id="attachment_93895" style="width: 1412px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93895" loading="lazy" class="size-full wp-image-93895" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/image-38-1.png" alt="" width="1402" height="582"&gt;
 &lt;p id="caption-attachment-93895" class="wp-caption-text"&gt;Figure 4: Starting a version upgrade from the Apache Kafka version section&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Select your cluster and start a version upgrade to &lt;code&gt;3.9.x&lt;/code&gt; with &lt;strong&gt;Target metadata mode&lt;/strong&gt; set to &lt;code&gt;KRaft&lt;/code&gt;. Choose &lt;strong&gt;Upgrade&lt;/strong&gt;.&lt;/p&gt; 
&lt;div id="attachment_93896" style="width: 1414px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93896" loading="lazy" class="size-full wp-image-93896" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/image-39.png" alt="" width="1404" height="646"&gt;
 &lt;p id="caption-attachment-93896" class="wp-caption-text"&gt;Figure 5: Selecting KRaft as the target metadata mode&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;You can monitor your upgrade progress on the cluster properties page.&lt;/p&gt; 
&lt;div id="attachment_93897" style="width: 1408px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93897" loading="lazy" class="size-full wp-image-93897" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/image-41.png" alt="" width="1398" height="579"&gt;
 &lt;p id="caption-attachment-93897" class="wp-caption-text"&gt;Figure 6: Monitoring upgrade progress on the cluster properties page&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="step-3-monitor-upgrade-progress"&gt;Step 3: Monitor upgrade progress&lt;/h3&gt; 
&lt;p&gt;Track progress on the &lt;strong&gt;Cluster operations&lt;/strong&gt; tab in the Amazon MSK console or with the &lt;code&gt;describe-cluster-operation&lt;/code&gt; API.&lt;/p&gt; 
&lt;div id="attachment_93898" style="width: 1409px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93898" loading="lazy" class="size-full wp-image-93898" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/image-42.png" alt="" width="1399" height="496"&gt;
 &lt;p id="caption-attachment-93898" class="wp-caption-text"&gt;Figure 7: Tracking the upgrade on the Cluster operations tab&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="step-4-validate-the-kraft-cluster"&gt;Step 4: Validate the KRaft cluster&lt;/h3&gt; 
&lt;p&gt;After the cluster returns to &lt;code&gt;ACTIVE&lt;/code&gt; state in KRaft mode:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Verify that topics, partitions, and consumer groups are present.&lt;/li&gt; 
 &lt;li&gt;Confirm producer and consumer throughput aligns with pre-migration baselines.&lt;/li&gt; 
 &lt;li&gt;Update or disable any ZooKeeper-specific monitoring alerts.&lt;/li&gt; 
 &lt;li&gt;Update operational documentation and runbooks to reflect KRaft mode.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;div id="attachment_93899" style="width: 1409px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93899" loading="lazy" class="size-full wp-image-93899" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/image-43.png" alt="" width="1399" height="637"&gt;
 &lt;p id="caption-attachment-93899" class="wp-caption-text"&gt;Figure 8: Cluster running in KRaft mode after the upgrade&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;After the upgrade completes, your cluster appears in an Active state with KRaft enabled as the metadata mode.&lt;/p&gt; 
&lt;h2 id="get-ready-for-the-next-generation-of-kafka-on-amazon-msk"&gt;Get ready for the next generation of Kafka on Amazon MSK&lt;/h2&gt; 
&lt;p&gt;The in-place ZooKeeper-to-KRaft mode upgrade makes it straightforward to prepare existing Amazon MSK clusters for the future of Apache Kafka. Beyond removing external metadata dependencies, KRaft delivers faster failover times and higher partition limits per cluster. Amazon MSK handles the entire metadata transition, rolling broker updates, validation, and recovery workflow for you. With the new in-place experience, you have a clear, streamlined path to upgrade on your schedule and unlock enhanced scalability and resilience.&lt;/p&gt; 
&lt;p&gt;For more details, see the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/" target="_blank" rel="noopener"&gt;Amazon MSK Developer Guide&lt;/a&gt; and the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/supported-kafka-versions.html" target="_blank" rel="noopener"&gt;supported Kafka versions documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6145-9.png" alt="Austin Groeneveld" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Austin Groeneveld&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/austin-groeneveld-2b50891b9/" target="_blank" rel="noopener"&gt;Austin&lt;/a&gt; is a Streaming Specialist Solutions Architect at Amazon Web Services (AWS), based in the San Francisco Bay Area. In this role, Austin is passionate about helping customers accelerate insights from their data using the AWS platform. He is particularly fascinated by the growing role that data streaming plays in driving innovation in the data analytics space. Outside of his work at AWS, Austin enjoys watching and playing soccer, traveling, and spending quality time with his family.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/BDB-6145-10.png" alt="Ashley Millette" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ashley Millette&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/ashleymillette/" target="_blank" rel="noopener"&gt;Ashley&lt;/a&gt; is a Specialist Solutions Architect for Streaming and Analytics at AWS. She partners with customers to design and implement real-time data streaming architectures using services like Amazon MSK, helping them build scalable, cost-effective pipelines that turn data in motion into actionable insights. She is passionate about simplifying complex streaming workloads and enabling customers to modernize their data infrastructure with confidence.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Amazon MSK Service 101: How many partitions does an Amazon MSK topic need?</title>
		<link>https://aws.amazon.com/blogs/big-data/amazon-msk-service-101-how-many-partitions-does-an-amazon-msk-topic-need/</link>
		
		<dc:creator><![CDATA[Yashika Jain]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 15:56:42 +0000</pubDate>
				<category><![CDATA[Amazon Managed Streaming for Apache Kafka (Amazon MSK)]]></category>
		<category><![CDATA[Foundational (100)]]></category>
		<category><![CDATA[Thought Leadership]]></category>
		<guid isPermaLink="false">f3f1f10347f6d66305a63a135d2e5ca5d5c37877</guid>

					<description>How many partitions does your Amazon MSK topic need? Choosing the right partition count affects throughput, scalability, and operational complexity. This post provides practical guidance for sizing partitions, covering per-partition throughput, consumer parallelism, partition keys, and Amazon MSK partition-per-broker guidelines.</description>
										<content:encoded>&lt;p&gt;Customers new to &lt;a href="https://aws.amazon.com/msk/" target="_blank" rel="noopener"&gt;Amazon Managed Streaming for Apache Kafka&lt;/a&gt; (Amazon MSK) often ask how many partitions their topics need. Choosing the right partition count is one of the most impactful architectural decisions you make, because it directly affects throughput, scalability, and operational complexity.&lt;/p&gt; 
&lt;p&gt;In &lt;a href="https://kafka.apache.org/41/getting-started/introduction/" target="_blank" rel="noopener"&gt;Apache Kafka&lt;/a&gt;, a topic is the fundamental unit for categorizing data streams, but to achieve high scalability and performance, Kafka divides topics into smaller, independent units called partitions.&lt;/p&gt; 
&lt;p&gt;In this post, we provide practical guidance for determining the ideal partition count for your use case.&lt;/p&gt; 
&lt;h2 id="understanding-kafka-partitions"&gt;Understanding Kafka partitions&lt;/h2&gt; 
&lt;div&gt;
 In 
 &lt;a href="https://kafka.apache.org/41/getting-started/introduction/" target="_blank" rel="noopener noreferrer"&gt;Apache Kafka&lt;/a&gt;, a partition is the unit of storage and parallelism. Each partition is an ordered, immutable log that can store records as they are produced to a topic. When you create a topic, Kafka distributes its partitions across the brokers in the cluster. Partitions allow Kafka to scale in three key ways:
&lt;/div&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;b&gt;Parallelism&lt;/b&gt; – Within a consumer group, each partition can be read by only one consumer at a time. Each partition maps to a dedicated log file in storage on the broker, and Kafka manages these logs through separate processing threads. This architecture allows more partitions to support more consumers processing data in parallel, with each partition’s log being independently managed for read and write operations.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;div&gt;
 The following diagram shows how Kafka distributes partition replicas across a three-broker cluster, with each broker serving as a leader for some partitions and a follower for others.
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5728-1.png" alt="Partitions 0, 1, and 2 replicated across three brokers, each a leader for some partitions and a follower for others" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Partition replicas distributed across a three-broker cluster&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The following diagram illustrates how producers append new records to the end of a partition log, while consumers read sequentially from their current offset position.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5728-2.png" alt="Producers append records to the tail of partition logs while consumers read sequentially from their offset position" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Producer writes and consumer offset positions in two partition logs&lt;/p&gt;
&lt;/div&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Throughput&lt;/strong&gt; – Producers and consumers can read and write data in parallel across partitions, increasing overall throughput.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt; – Partitions allow Kafka to spread data and load across multiple brokers instead of concentrating it on a single node.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;However, increasing partitions comes with trade-offs. Each partition adds metadata overhead, consumes memory, and requires file handles on the broker. While more partitions improve throughput and parallelism, they also increase the operational burden on the cluster. Too many partitions can lead to longer leader election times during broker failures, increased end-to-end latency, and higher memory consumption for both producers and consumers managing connections to multiple partitions.&lt;/p&gt; 
&lt;h2 id="trade-offs-when-choosing-partition-count"&gt;Trade-offs when choosing partition count&lt;/h2&gt; 
&lt;p&gt;Choosing a partition count is a balancing act between parallelism and resource utilization.&lt;/p&gt; 
&lt;h3 id="benefits-of-more-partitions"&gt;Benefits of more partitions&lt;/h3&gt; 
&lt;p&gt;Using more partitions can significantly improve throughput by allowing Kafka to distribute read and write traffic across more brokers. This is particularly useful for high-volume ingestion pipelines and real-time analytics workloads. More partitions also allow consumer groups to scale horizontally, because the maximum number of active consumers in a group is limited by the number of partitions. In addition, choosing a partition count that is evenly divisible by the number of brokers helps provide balanced leadership and replica distribution, reducing the risk of uneven load.&lt;/p&gt; 
&lt;h3 id="operational-costs-of-more-partitions"&gt;Operational costs of more partitions&lt;/h3&gt; 
&lt;p&gt;However, higher partition counts also come with costs. When a broker fails or undergoes maintenance, Kafka must perform recovery operations for each affected partition. During recovery, Kafka elects new leaders for partitions that were hosted on the unavailable broker and replicates data from the remaining in-sync replicas to newly assigned brokers. This process involves copying partition data across the network to restore the replication factor, which can be resource intensive. As the number of partitions increases, these recovery operations take longer because each partition requires its own leader election and data replication cycle.&lt;/p&gt; 
&lt;p&gt;You might encounter clusters with very high partition counts that experience extended recovery times during rolling upgrades, even when overall traffic volumes are modest. Amazon MSK &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/msk-broker-types-express.html" target="_blank" rel="noopener"&gt;Express brokers&lt;/a&gt; address this challenge by recovering 90x faster and providing 180x faster elasticity when scaling out clusters. This significantly reduces the operational impact of high partition counts during maintenance windows and failure scenarios.&lt;/p&gt; 
&lt;h3 id="infrastructure-cost-implications"&gt;Infrastructure cost implications&lt;/h3&gt; 
&lt;p&gt;Beyond operational complexity, more partitions can directly increase infrastructure costs. Amazon MSK publishes &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices.html#standard-server-side-considerations" target="_blank" rel="noopener"&gt;partition-per-broker limits&lt;/a&gt; that vary by instance type. When the total partition count (including replicas) exceeds what the current broker fleet can support, you must add brokers to stay within &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices.html#standard-server-side-considerations" target="_blank" rel="noopener"&gt;recommended limits&lt;/a&gt;, even if throughput alone does not warrant the additional capacity.&lt;/p&gt; 
&lt;h2 id="amazon-msk-partition-per-broker-guidelines"&gt;Amazon MSK partition-per-broker guidelines&lt;/h2&gt; 
&lt;p&gt;Amazon MSK publishes recommended partition-per-broker guidelines to help you operate clusters reliably. These values are strict limits. Exceeding them can lead to operational challenges, particularly during broker replacement or rolling upgrades, and can block cluster operations such as configuration updates or scaling down.&lt;/p&gt; 
&lt;p&gt;Express brokers support up to 5x more partitions per broker compared to Standard brokers. For example, the largest Standard broker (kafka.m7g.16xlarge) supports a recommended maximum of 4,000 partitions per broker. The equivalent Express broker (express.m7g.16xlarge) supports up to 20,000 &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/limits.html#msk-express-broker-partition-quota" target="_blank" rel="noopener"&gt;recommended partitions per broker&lt;/a&gt;. This higher partition density means partition-bound workloads can be hosted on fewer brokers, improving &lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/07/amazon-msk-partitions-express-broker/" target="_blank" rel="noopener"&gt;price-performance by up to 50%&lt;/a&gt; for such workloads.&lt;/p&gt; 
&lt;p&gt;We recommend setting &lt;a href="https://github.com/aws-samples/sample-recommended-msk-cloudwatch-alarms" target="_blank" rel="noopener"&gt;Amazon CloudWatch alarms&lt;/a&gt; on &lt;code&gt;PartitionCount&lt;/code&gt; per-broker metrics to proactively monitor your partition distribution. When an alarm triggers, evaluate your partition strategy and consider rebalancing partitions across brokers, consolidating topics, or scaling out your cluster to stay within recommended limits. For detailed guidance, see &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices.html#standard-server-side-considerations" target="_blank" rel="noopener"&gt;Right-size your cluster: Number of partitions per Standard broker&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/limits.html#msk-express-broker-partition-quota" target="_blank" rel="noopener"&gt;Express broker partition quota&lt;/a&gt;.&lt;/p&gt; 
&lt;h2 id="practical-guidance-for-choosing-a-partition-count"&gt;Practical guidance for choosing a partition count&lt;/h2&gt; 
&lt;p&gt;There is no single formula that works for every Kafka workload. In practice, you typically combine several considerations when sizing partitions.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Start with throughput requirements&lt;/strong&gt; – The first step is to determine your per-partition throughput capacity, which then informs how many partitions you need.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;blockquote&gt;
 &lt;p&gt;For Express brokers, use the per-broker throughput capacity as the primary means for sizing your cluster. Express brokers feature a fully managed storage layer, so you do not need to separately account for storage I/O constraints. &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices-express.html" target="_blank" rel="noopener"&gt;The published per-broker limits&lt;/a&gt; represent the effective capacity available to your workload.&lt;/p&gt; 
 &lt;p&gt;For Standard brokers, the achievable throughput depends on additional factors beyond the broker instance size. These factors include provisioned EBS storage throughput, the number of consumer groups reading from the broker, and how much data is served from memory versus disk. Storage I/O is consumed when producers write, when data replicates between brokers, and when consumers read data that is not in memory. For this reason, validate the effective per-partition throughput for Standard brokers through load testing in your environment.&lt;/p&gt; 
 &lt;p&gt;Once you know your per-partition throughput, calculate the required number of partitions: &lt;em&gt;Number of partitions = Peak throughput of the topic ÷ Throughput per partition&lt;/em&gt;&lt;/p&gt; 
 &lt;p&gt;For example, if a topic must handle 40 MB/sec at peak and your testing shows each partition can sustain 5 MB/sec, you would need: 40 ÷ 5 = 8 partitions. Always validate these assumptions with load testing, as actual throughput varies based on your workload characteristics. For initial sizing estimates, refer to the Amazon &lt;a href="https://view.officeapps.live.com/op/view.aspx?src=https%3A%2F%2Fdy7oqpxkwhskb.cloudfront.net%2FMSK_Sizing_Pricing.xlsx&amp;amp;wdOrigin=BROWSELINK" target="_blank" rel="noopener"&gt;MSK Sizing and Pricing worksheet&lt;/a&gt; and the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices.html" target="_blank" rel="noopener"&gt;Amazon MSK Best Practices documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Consider your consumer parallelism needs&lt;/strong&gt; – If you know the number of consumers required during peak processing times, use that as your partition count. We don’t recommend having more active consumers in a consumer group than partitions. For example, if you have 5 partitions, only 5 consumers can actively process data. Additional consumers remain idle. These idle consumers still maintain active TCP connections to the brokers, sending frequent heartbeats and group coordination requests. This might result in unnecessary overhead on broker resources and contribute to high CPU usage despite low egress traffic.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5728-3.png" alt="Consumer group with more consumers than partitions, leaving the extra consumers idle" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: Idle consumers when a consumer group has more consumers than partitions&lt;/p&gt;
&lt;/div&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Producer throughput and partition keys&lt;/strong&gt; – When sizing partitions, consider producer-side throughput in addition to consumer parallelism. If producers generate data faster than a single partition can handle, additional partitions can help distribute write traffic across brokers. Partition keys also play a critical role. Poorly distributed or low-cardinality keys can create hot partitions and limit throughput. In such cases, increasing the number of partitions alone does not improve throughput unless records are evenly distributed.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Plan for even distribution and future growth&lt;/strong&gt; – Kafka works best when partitions can be spread evenly across brokers. Instead of focusing on specific numbers, aim for partition counts that divide reasonably well across your expected broker count. This reduces reassignment churn when brokers are added or replaced. But avoid excessive over-partitioning. It’s reasonable to leave some headroom for future growth. However, creating thousands of partitions “just in case” often causes more harm than good. Increasing partitions later is supported, but it can affect ordering guarantees and may require consumer changes. Start with a conservative number, monitor real traffic patterns, and scale gradually.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;From an operational perspective, Amazon MSK provides recommended &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices.html#standard-server-side-considerations" target="_blank" rel="noopener"&gt;partition-per-broker&lt;/a&gt; guidelines based on broker instance type. Exceeding these guidelines increases operational risk and can block cluster operations such as version upgrades, scaling, or configuration changes. Large partition counts can also increase consumer group rebalance duration, temporarily pausing message processing and increasing end-to-end latency.&lt;/p&gt; 
&lt;p&gt;Keep in mind that partitioning improves scalability, but it does not address application-level bottlenecks such as slow consumers, inefficient processing logic, or downstream system constraints.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Determining the right number of partitions for an Amazon MSK topic is a foundational design decision. It affects throughput, scalability, failure recovery, and day-to-day operability of your Kafka cluster. Start by understanding your throughput and consumer parallelism needs, respect Amazon MSK partition-per-broker guidelines, avoid excessive over-partitioning, and validate assumptions through load testing. Most importantly, there is no universal “correct” number, only a number that fits your workload, operational goals, and cost.&lt;/p&gt; 
&lt;p&gt;For more information, see the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/what-is-msk.html" target="_blank" rel="noopener"&gt;Amazon MSK Developer Guide&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices.html" target="_blank" rel="noopener"&gt;Recommended best practices for Amazon MSK&lt;/a&gt;.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/artifacts/DBSBlogs/BDB-5728/BDB-5728-4.png" alt="Yashika Jain" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Yashika Jain&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/jain-yashika/" target="_blank" rel="noopener"&gt;Yashika&lt;/a&gt; is a Senior Cloud Analytics Engineer at AWS, specializing in real-time analytics and event-driven architectures. She is committed to helping customers by providing deep technical guidance, driving best practices across real-time data platforms and solving complex issues related to their streaming data architectures.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5728-5.png" alt="Ali Alemi" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ali Alemi&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/ali-alemi-11869b53/" target="_blank" rel="noopener"&gt;Ali&lt;/a&gt; is a Principal Streaming Solutions Architect at AWS. Ali advises AWS customers with architectural best practices and helps them design real-time analytics data systems which are reliable, secure, efficient, and cost-effective. Prior to joining AWS, Ali supported several public sector customers and AWS consulting partners in their application modernization journey and migration to the Cloud.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>AWS and DuckLabs: Building the future of analytics together</title>
		<link>https://aws.amazon.com/blogs/big-data/aws-and-ducklabs-building-the-future-of-analytics-together/</link>
					
		
		<dc:creator><![CDATA[Mai-Lan Tomsen Bukovec]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 13:28:34 +0000</pubDate>
				<category><![CDATA[Analytics]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Database]]></category>
		<guid isPermaLink="false">a72353e29fb62fc56273604ca91af83bfa9a1a94</guid>

					<description>Today we are announcing that Amazon has signed a definitive agreement to acquire DuckLabs, the Amsterdam-based company behind the open-source analytical database DuckDB. We expect the transaction to close shortly, subject to customary closing conditions. Hannes Mühleisen and Mark Raasveldt, who created DuckDB and co-founded DuckLabs, will continue leading the team and the open-source project's technical direction as part of AWS. The DuckDB open-source project will also continue to be driven by the DuckLabs team, remain open source under the independent Foundation (the non-profit that oversees DuckDB), and available under the MIT license as it does today.</description>
										<content:encoded>&lt;p&gt;Today we are announcing that &lt;a href="https://www.aboutamazon.com/news/company-news/aws-ducklabs" target="_blank" rel="noopener"&gt;Amazon has signed a definitive agreement to acquire DuckLabs&lt;/a&gt;, the Amsterdam-based company behind the open-source analytical database DuckDB. We expect the transaction to close shortly, subject to customary closing conditions. Hannes Mühleisen and Mark Raasveldt, who created DuckDB and co-founded DuckLabs, will continue leading the team and the open-source project’s technical direction as part of AWS. The DuckDB open-source project will also continue to be driven by the DuckLabs team, remain open source under the independent Foundation (the non-profit that oversees DuckDB), and available under the MIT license as it does today (see &lt;a href="https://ducklabs.com/ducklabs-to-join-aws" target="_blank" rel="noopener"&gt;DuckLabs blog&lt;/a&gt;).&lt;/p&gt; 
&lt;p&gt;Data has always been a core asset and differentiator for companies. That is true now more than ever, as organizations use their data to customize inference and build AI agents. For 20 years AWS has driven the frontier of data, starting with the launch of Amazon S3 to create data lakes for every business, the first cloud analytics service in Amazon EMR, the first cloud data warehouse with Amazon Redshift and the many capabilities that we have introduced with Athena, Glue ETL, etc. We continue innovating for AWS customers on the data frontier including providing Apache Iceberg capabilities directly in S3 Tables, vector storage in the data lake and our new optimized Graviton-based Redshift clusters.&lt;/p&gt; 
&lt;p&gt;DuckDB has also been at the forefront of changing how the world works with data. Hannes and Mark started DuckDB while at Centrum Wiskunde &amp;amp; Informatica (CWI), the national research institute in the Netherlands&amp;nbsp;that also invented Python. The founders of DuckDB realized that older databases and analytics engines like Spark focused on performance for very large data processing but didn’t have an effective way to “scale down” to smaller size data queries that form the backbone of what most customers do with SQL analytics.&lt;/p&gt; 
&lt;p&gt;DuckDB set out to solve the problem of blazingly fast performance for the 90%+ of data queries in the world today, that often runs 1 terabyte of data or less as part of analysis and dashboarding. DuckDB’s architecture is based on that core premise of “make the everyday SQL query super fast” so DuckDB runs in-process to other applications which simplifies and speeds up data exchange with the application. DuckDB gets big performance gains from its vectorized execution because it does not require a heavy compiler to run simple statements like &lt;code&gt;SELECT * FROM table&lt;/code&gt;. And what works for everyday queries also (unsurprisingly) works very well for agents because agents behave a lot like people when interacting with data. They poke. They experiment. They run exploratory analysis on small data sets before figuring out what they really want to do. DuckDB ends up being naturally optimized for AI agents to use. What started as an academic project is now widely adopted across data engineering, data science, analytics, and now AI agents, for its simplicity of use and raw performance. We plan to combine the superpower of DuckDB at everyday queries of a terabyte or less with the proven exabyte-plus enterprise scale of S3 and our AWS analytics services of Redshift, Athena, EMR, Glue-ETL, and SageMaker platform which power analytics across hundreds of terabytes to petabytes of data. Andy Warfield, Distinguished Engineer at AWS, talks about &lt;a href="https://www.allthingsdistributed.com/2026/08/duckdb-and-the-changing-physics-of-analytics.html" target="_blank" rel="noopener"&gt;&lt;strong&gt;DuckDB and the Changing Physics of Analytics&lt;/strong&gt;&lt;/a&gt; in Werner Vogel’s All Things Distributed blog.&lt;/p&gt; 
&lt;p&gt;Our customers use DuckDB today with AWS services and tell us how much they love it for its speed and simplicity. For example, DuckDB today executes SQL directly against external files, such as Parquet, CSV, and JSON, stored locally or on cloud storage like S3 for unparalleled performance and significantly lower cost.&lt;/p&gt; 
&lt;p&gt;David Feng, Executive Director, Scientific Computing at Allen Institute, said&lt;span style="font-weight: normal !msorm"&gt; “The Allen Institute accelerates science for a healthier world by tackling the biggest questions in biology at a large scale, and that involves extensive analysis of large, multimodal data. We started using DuckDB to analyze terabytes of scientific data in 2025 and love it. We are storing data in S3 for realtime quality control and analysis of neurophysiology and behavior data, critical to driving the next data acquisition. Queries that took minutes now come back in less than a second, enabling completely new ways of interacting with data.” DuckDB can also run in-process to AWS Lambda functions.&lt;/span&gt;&lt;/p&gt; 
&lt;p&gt;We are excited to make DuckDB applications run best on AWS, and will continue to invest in deep integration between DuckDB and our building block services.&lt;/p&gt; 
&lt;p&gt;We are also using DuckDB in our own AWS infrastructure. When Amazon Quick wanted to augment the performance of their custom dashboarding engine, they picked DuckDB to query data in S3 Tables. The Quick team found that the DuckDB engine scales effortlessly with the number of CPUs, and its single library can easily plug into the internal Quick control plane subsystems. Since we launched Quick in October 2025, we have processed over 2.5B queries using our custom Quick query engine with the DuckDB integrations and optimizations. These DuckDB integrations and optimizations helped Amazon Quick reduce average query latency by 30%. We are going to look at how we can integrate DuckDB’s performance and simplicity in our other AWS services across data and analytics.&lt;/p&gt; 
&lt;p&gt;Stay tuned for more about how DuckLabs and AWS will reinvent the frontier of data together for applications, data engineers, and AI, meeting customers where they are today and giving them the benefits of DuckDB’s innovation within AWS.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the author&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="alignleft size-full wp-image-93882" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/26/Screenshot-2026-08-26-at-8.09.11 AM-1.png" alt="" width="120" height="137"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Mai-Lan Tomsen Bukovec&lt;/h3&gt; 
  &lt;p&gt;Mai-Lan Tomsen Bukovec, Technology Vice President at AWS, leads the Amazon cloud data services that millions of AWS customers rely on for digital transformations, business analytics, machine learning, generative AI, and next generation customer experiences. With over 25 years of experience in the technology industry, Mai-Lan is a pioneer in helping customers take advantage of cloud-based technologies to transform their businesses.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
					
			
		
		
			</item>
		<item>
		<title>PythonOperator and BashOperator Now Available on Amazon Managed Workflows for Apache Airflow (Amazon MWAA) Serverless</title>
		<link>https://aws.amazon.com/blogs/big-data/pythonoperator-and-bashoperator-now-available-on-amazon-managed-workflows-for-apache-airflow-amazon-mwaa-serverless/</link>
		
		<dc:creator><![CDATA[Pradeep Kumar Nalluri]]></dc:creator>
		<pubDate>Tue, 25 Aug 2026 19:28:00 +0000</pubDate>
				<category><![CDATA[Amazon Managed Workflows for Apache Airflow (Amazon MWAA)]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Intermediate (200)]]></category>
		<guid isPermaLink="false">9e37e8b20dda0aef314f0bff1c703779bef957c7</guid>

					<description>You can now use PythonOperator and BashOperator to run custom Python functions and shell scripts directly in the Amazon MWAA Serverless runtime, without provisioning additional infrastructure. This post walks through building a serverless pipeline that converts CSV files to JSON using a PythonOperator and verifies the output with a BashOperator.</description>
										<content:encoded>&lt;p&gt;If you run &lt;a href="https://airflow.apache.org/" target="_blank" rel="noopener"&gt;Apache Airflow workflows&lt;/a&gt; on &lt;a href="https://docs.aws.amazon.com/mwaa/latest/mwaa-serverless-userguide/what-is-mwaa-serverless.html" target="_blank" rel="noopener"&gt;Amazon MWAA Serverless&lt;/a&gt;, you can now use PythonOperator and BashOperator to run custom code directly in the serverless runtime. Previously, Amazon Managed Workflows for Apache Airflow (Amazon MWAA) Serverless only supported orchestration of AWS services through operators for scheduling tasks, managing dependencies, and handling retries. It did not support running your own Python functions or shell scripts natively. If you needed custom Python logic or shell commands, you had to wrap code in AWS Lambda functions, start Amazon Elastic Container Service (Amazon ECS) tasks, or use other AWS compute services. These alternatives add complexity, cost, and latency to your orchestration pipelines.&lt;/p&gt; 
&lt;p&gt;With this launch, you can run custom Python functions and shell scripts directly within the serverless task runtime, without requiring additional infrastructure. This means you can now use &lt;a href="https://airflow.apache.org/docs/apache-airflow-providers-standard/stable/operators/python.html#pythonoperator" target="_blank" rel="noopener"&gt;PythonOperator&lt;/a&gt; and &lt;a href="https://airflow.apache.org/docs/apache-airflow-providers-standard/stable/operators/bash.html#bashoperator" target="_blank" rel="noopener"&gt;BashOperator&lt;/a&gt; many data engineering teams rely on for ETL pipelines and data quality checks – without provisioning additional compute.&lt;/p&gt; 
&lt;p&gt;In this post, we walk through how this feature works and demonstrate a practical example: building a serverless pipeline that converts CSV files to JSON format using a PythonOperator, and verifies the output using a BashOperator. By the end, you will know how to:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Package a Python module with dependencies and upload it to an Amazon Simple Storage Service (Amazon S3) bucket as a code bundle&lt;/li&gt; 
 &lt;li&gt;Define a multi-task workflow using the &lt;a href="https://www.astronomer.io/docs/learn/dag-factory" target="_blank" rel="noopener"&gt;dag-factory&lt;/a&gt; compatible YAML&lt;/li&gt; 
 &lt;li&gt;Create and run a workflow with the AWS Command Line Interface (AWS CLI)&lt;/li&gt; 
 &lt;li&gt;Verify that your pipeline produced the expected output&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="how-it-works"&gt;How it works&lt;/h2&gt; 
&lt;p&gt;With MWAA Serverless, you can package your custom code, upload it to an Amazon S3 bucket, and reference it when creating a workflow. The service snapshots your code at workflow creation time and uses that snapshot for all subsequent runs of the same workflow version.&lt;/p&gt; 
&lt;h3 id="code-bundles"&gt;Code bundles&lt;/h3&gt; 
&lt;p&gt;A code bundle is the package that contains your custom logic. You package your Python modules or shell scripts and upload them to an Amazon S3 bucket. A code bundle can be:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;A single .py file or .sh bash script (uploaded to an Amazon S3 bucket)&lt;/li&gt; 
 &lt;li&gt;A ZIP archive containing multiple shell scripts, Python modules and dependencies (up to 250 MB)&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3 id="execution-model"&gt;Execution model&lt;/h3&gt; 
&lt;p&gt;When you create or update a workflow, MWAA Serverless snapshots your code bundle from an Amazon S3 bucket provided and stores it on the service side. At task execution time, the service uses this snapshot – not the object currently residing in your Amazon S3 bucket – to run your code in an isolated runtime environment.&lt;/p&gt; 
&lt;p&gt;Python and Bash tasks do not have internet access. They can reach only Amazon S3, Amazon Elastic Container Registry (Amazon ECR), and Amazon CloudWatch, which are the services the runtime requires to operate. To have internet access, &lt;a href="https://docs.aws.amazon.com/mwaa/latest/mwaa-serverless-userguide/networking.html" target="_blank" rel="noopener"&gt;configure the workflow with Amazon VPC&lt;/a&gt; so that it can go through the provided VPC.&lt;/p&gt; 
&lt;h3 id="supported-operators"&gt;Supported operators&lt;/h3&gt; 
&lt;p&gt;The following table describes the two operators now available in MWAA Serverless.&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Operator&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;PythonOperator&lt;/td&gt; 
   &lt;td&gt;Executes a Python callable (function) from your code bundle&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;BashOperator&lt;/td&gt; 
   &lt;td&gt;Runs shell commands or scripts&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;h3 id="security"&gt;Security&lt;/h3&gt; 
&lt;p&gt;AWS Key Management Service (AWS KMS) encrypts your code bundles at rest. IAM policies control who can create, update, and trigger the workflows. The execution role scopes what AWS resources your code can access at runtime.&lt;/p&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;p&gt;Before getting started, verify that you have the following resources and tools configured in your AWS account:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An AWS account with access to Amazon MWAA Serverless&lt;/li&gt; 
 &lt;li&gt;AWS CLI v2 (latest version) installed and configured. To install or update, see &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" target="_blank" rel="noopener"&gt;Installing or updating to the latest version of the AWS CLI&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;An Amazon S3 bucket for storing DAG definitions and code bundles&lt;/li&gt; 
 &lt;li&gt;An IAM role that MWAA Serverless can assume (see the execution role setup below)&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="walkthrough-building-a-serverless-csv-to-json-pipeline"&gt;Walkthrough: Building a serverless CSV-to-JSON pipeline&lt;/h2&gt; 
&lt;p&gt;In this walkthrough, we build a pipeline that converts CSV files to JSON format – a common data transformation for downstream APIs and analytics systems that consume JSON. The pipeline uses a PythonOperator for the conversion logic and a BashOperator to verify the output. Here is what the pipeline does:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;Reads a CSV file from an Amazon S3 bucket&lt;/li&gt; 
 &lt;li&gt;Converts it to JSON format with column type inference&lt;/li&gt; 
 &lt;li&gt;Writes the JSON file back to an Amazon S3 bucket&lt;/li&gt; 
 &lt;li&gt;Validates record counts match between source and output&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="step-1-create-the-execution-role"&gt;Step 1: Create the execution role&lt;/h3&gt; 
&lt;p&gt;Create an IAM role that your workflow assumes at runtime. The trust policy must allow the airflow-serverless.amazonaws.com service to assume the role:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;cat &amp;gt; trust-policy.json &amp;lt;&amp;lt; 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "airflow-serverless.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Create the role and attach an inline policy granting least-privilege access to your S3 bucket:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws iam create-role \
  --role-name MWAAServerlessExecutionRole \
  --assume-role-policy-document file://trust-policy.json

aws iam put-role-policy \
  --role-name MWAAServerlessExecutionRole \
  --policy-name MWAAServerlessAccessPolicy \
  --policy-document '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::amzn-s3-demo-mwaa-data",
        "arn:aws:s3:::amzn-s3-demo-mwaa-data/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "logs:DescribeLogStreams",
        "logs:GetLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:log-group:/aws/mwaa-serverless/*"
    }
  ]
}'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="step-2-write-the-python-module"&gt;Step 2: Write the Python module&lt;/h3&gt; 
&lt;p&gt;Create a file called &lt;code&gt;csv_to_json.py&lt;/code&gt; with the conversion logic:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;# csv_to_json.py
import csv
import json
import boto3
import io

def convert(**kwargs):
    """Read a CSV from S3 and write it back as JSON lines."""
    bucket = "amzn-s3-demo-mwaa-data"
    source_key = "raw/sales_data.csv"
    output_key = "processed/sales_data.json"

    s3 = boto3.client("s3")

    # Read source file
    response = s3.get_object(Bucket=bucket, Key=source_key)
    content = response["Body"].read().decode("utf-8")

    # Parse CSV
    reader = csv.DictReader(io.StringIO(content))
    rows = list(reader)

    # Type inference - convert numeric fields
    for row in rows:
        for key, value in row.items():
            try:
                row[key] = float(value)
            except (ValueError, TypeError):
                pass

    # Write as JSON lines
    output = "\n".join(json.dumps(row) for row in rows) + "\n"
    s3.put_object(Bucket=bucket, Key=output_key, Body=output.encode("utf-8"))

    print(f"Converted {len(rows)} rows to JSON lines")
    print(f"Output: s3://amzn-s3-demo-mwaa-data/{output_key}")
    return {"rows": len(rows), "output_key": output_key}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;This function uses &lt;a href="https://pypi.org/project/boto3/1.43.10/" target="_blank" rel="noopener"&gt;boto3&lt;/a&gt; (which comes pre-installed with the MWAA Serverless execution environment) and Python’s built-in csv and json modules. The conversion reads the CSV, infers numeric types, and writes a JSON lines file back to the S3 bucket.&lt;/p&gt; 
&lt;h3 id="step-3-write-the-verification-script"&gt;Step 3: Write the verification script&lt;/h3&gt; 
&lt;p&gt;Create a file called &lt;code&gt;verify_output.sh&lt;/code&gt;. This script validates the pipeline output by comparing the record count in the source CSV against the output JSON file. If the counts do not match, the task fails with a non-zero exit code, which causes the workflow run to fail.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;#!/bin/bash
echo "=== Data Validation ==="

# Count source records (skip CSV header)
SOURCE_COUNT=$(python3 -m awscli s3 cp s3://amzn-s3-demo-mwaa-data/raw/sales_data.csv - | tail -n +2 | wc -l)
echo "Source CSV records: $SOURCE_COUNT"

# Count output records
OUTPUT_COUNT=$(python3 -m awscli s3 cp s3://amzn-s3-demo-mwaa-data/processed/sales_data.json - | wc -l)
echo "Output JSON records: $OUTPUT_COUNT"

# Validate counts match
if [ "$SOURCE_COUNT" -ne "$OUTPUT_COUNT" ]; then
    echo "FAILED: Record count mismatch (source=$SOURCE_COUNT, output=$OUTPUT_COUNT)"
    exit 1
fi

echo "PASSED: Record counts match ($OUTPUT_COUNT records)"
echo "Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;This script runs the AWS CLI, which is bundled as a dependency in the code package. The s3 cp streams the file content to &lt;code&gt;stdout&lt;/code&gt; without writing to disk, allowing standard shell tools like &lt;code&gt;wc -l&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; to process it. The execution role credentials are automatically available in the execution environment, so the CLI can access S3 without additional configuration.&lt;/p&gt; 
&lt;h3 id="step-4-package-and-upload-the-code-to-amazon-s3"&gt;Step 4: Package and upload the code to Amazon S3&lt;/h3&gt; 
&lt;p&gt;Since the verification script uses the AWS CLI, bundle it as a dependency in the ZIP archive along with your Python module and shell script:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;BUCKET="amzn-s3-demo-mwaa-data"
REGION="us-east-1"

# Install awscli into a package directory
pip install awscli \
  --target my_package/ \
  --platform manylinux2014_x86_64 \
  --python-version 3.12 \
  --only-binary=:all:

# Add your module
cp csv_to_json.py my_package/
cp verify_output.sh my_package/

# Create the ZIP archive
cd my_package &amp;amp;&amp;amp; zip -r ../code_bundle.zip . &amp;amp;&amp;amp; cd ..
# Upload to S3
aws s3 cp code_bundle.zip s3://$BUCKET/code/code_bundle.zip --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Upload a sample CSV file for testing:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;cat &amp;gt; sales_data.csv &amp;lt;&amp;lt; 'EOF'
date,region,product,units,revenue
2026-07-01,us-east,widget-a,150,4500.00
2026-07-01,eu-west,widget-b,89,2670.00
2026-07-02,us-east,widget-a,203,6090.00
2026-07-02,ap-south,widget-c,67,1340.00
2026-07-03,us-east,widget-b,178,5340.00
EOF

aws s3 cp sales_data.csv s3://$BUCKET/raw/sales_data.csv --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="step-5-define-the-dag-yaml"&gt;Step 5: Define the DAG (YAML)&lt;/h3&gt; 
&lt;p&gt;MWAA Serverless uses a declarative YAML format for DAG definitions. Create a file called &lt;code&gt;conversion_dag.yaml&lt;/code&gt;:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-yaml"&gt;csv_to_json_pipeline:
  start_date: "2026-01-01"
  schedule: null
  tasks:
    convert_to_json:
      operator: airflow.operators.python.PythonOperator
      python_callable: csv_to_json.convert
    verify_output:
      operator: airflow.operators.bash.BashOperator
      bash_command: "verify_output.sh"
      dependencies:
        - convert_to_json&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;This DAG defines two tasks:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code&gt;convert_to_json&lt;/code&gt; – Runs the convert function from the Python module to transform CSV to JSON lines.&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;verify_output&lt;/code&gt; – Runs a shell script that validates the pipeline output by comparing source and output record counts, failing the task if they do not match.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Upload the DAG definition to S3. Note: You can also run inline Bash commands directly without a shell script.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws s3 cp conversion_dag.yaml s3://$BUCKET/dags/conversion_dag.yaml --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="step-6-create-the-workflow"&gt;Step 6: Create the workflow&lt;/h3&gt; 
&lt;p&gt;Create the MWAA Serverless workflow, referencing the DAG definition and the code bundle:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;ROLE_ARN="arn:aws:iam::&amp;lt;your-account-id&amp;gt;:role/MWAAServerlessExecutionRole"

aws mwaa-serverless create-workflow \
  --name csv-to-json-workflow \
  --definition-s3-location Bucket="$BUCKET",ObjectKey="dags/conversion_dag.yaml" \
  --code '{"S3Location": {"Bucket":"'"$BUCKET"'","ObjectKey":"code/code_bundle.zip"}}' \
  --role-arn $ROLE_ARN \
  --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The response includes a WorkflowArn that you use to trigger runs:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "WorkflowArn": "arn:aws:airflow-serverless:us-east-1:123456789012:workflow/csv-to-json-workflow-abc123",
  "CreatedAt": "2026-07-15T10:30:00.000000+00:00",
  "WorkflowVersion": "a1b2c3d4e5f6"
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="step-7-run-the-workflow"&gt;Step 7: Run the workflow&lt;/h3&gt; 
&lt;p&gt;Trigger a workflow run:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;WORKFLOW_ARN="arn:aws:airflow-serverless:us-east-1:123456789012:workflow/csv-to-json-workflow-abc123"

aws mwaa-serverless start-workflow-run \
  --workflow-arn $WORKFLOW_ARN \
  --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The response confirms the run has started:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "RunId": "6OZV9ABF9enHKXk",
  "Status": "STARTING"
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="step-8-monitor-execution"&gt;Step 8: Monitor execution&lt;/h3&gt; 
&lt;p&gt;Check the status of your run:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;RUN_ID="6OZV9ABF9enHKXk"

aws mwaa-serverless get-workflow-run \
  --workflow-arn $WORKFLOW_ARN \
  --run-id $RUN_ID \
  --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;A successful run returns:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "RunDetail": {
    "Duration": 45,
    "RunState": "SUCCESS",
    "TaskInstances": ["ex_abc123_convert_to_json_1", "ex_abc123_verify_output_1"]
  },
  "RunId": "6OZV9ABF9enHKXk",
  "RunType": "ON_DEMAND",
  "WorkflowArn": "arn:aws:airflow-serverless:us-east-1:123456789012:workflow/csv-to-json-workflow-abc123",
  "WorkflowVersion": "a1b2c3d4e5f6"
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="step-9-verify-the-output"&gt;Step 9: Verify the output&lt;/h3&gt; 
&lt;p&gt;Confirm the JSON file was written to the S3 bucket:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# List the output file
aws s3 ls s3://$BUCKET/processed/sales_data.json --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;You should see the JSON file:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;2026-07-15 10:32:45 1847 sales_data.json&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;You can also verify task-level output in Amazon CloudWatch Logs. Open the log group for your workflow and find the &lt;code&gt;convert_to_json&lt;/code&gt; task log stream:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;Converted 5 rows to JSON lines
Output: s3://amzn-s3-demo-mwaa-data/processed/sales_data.json&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="considerations-and-limits"&gt;Considerations and limits&lt;/h2&gt; 
&lt;p&gt;When planning your workloads on MWAA Serverless with these operators, keep the following considerations in mind:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Code bundle size – ZIP archives must be under 250 MB per bundle.&lt;/li&gt; 
 &lt;li&gt;Network access – Python and Bash tasks do not have internet access. They can reach a limited set of AWS services required for the runtime to function (Amazon S3, Amazon ECR, and Amazon CloudWatch) but cannot call other AWS services or external endpoints. If your workflow requires calls to external APIs, preprocess that data and store it in an Amazon S3 bucket before invoking the workflow.&lt;/li&gt; 
 &lt;li&gt;Runtime dependencies – boto3 and the Python standard library are pre-installed. For additional packages (such as pandas or requests), bundle them in your ZIP archive following the &lt;a href="https://docs.aws.amazon.com/mwaa/latest/mwaa-serverless-userguide/operators-python-bash-detail.html#operators-python-bash-zip" target="_blank" rel="noopener"&gt;Amazon MWAA Serverless packaging guidelines&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;Execution timeout – Tasks are subject to the workflow’s configured timeout limits.&lt;/li&gt; 
 &lt;li&gt;Python version – Check the &lt;a href="https://docs.aws.amazon.com/mwaa/latest/mwaa-serverless-userguide/operators-python-bash-detail.html" target="_blank" rel="noopener"&gt;Amazon MWAA Serverless documentation&lt;/a&gt; for the currently supported Python runtime version.&lt;/li&gt; 
 &lt;li&gt;DAG format – MWAA Serverless uses YAML-based DAG definitions, not traditional Python DAG files. If you are migrating from MWAA Provisioned, you will need to convert your DAGs to the YAML format.&lt;/li&gt; 
 &lt;li&gt;Operators not supported – Some Airflow community operators and custom plugins are not available in the Serverless runtime. Refer to the &lt;a href="https://docs.aws.amazon.com/mwaa/latest/mwaa-serverless-userguide/operators.html" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt; for the full compatibility list.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="clean-up"&gt;Clean up&lt;/h2&gt; 
&lt;p&gt;To avoid ongoing charges, delete the resources you created in this walkthrough. The following commands remove the workflow, S3 objects, and IAM role:&lt;/p&gt; 
&lt;p&gt;Note: &lt;code&gt;$WORKFLOW_ARN&lt;/code&gt; is defined in Step 7.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# Delete the workflow
aws mwaa-serverless delete-workflow \
  --workflow-arn $WORKFLOW_ARN \
  --region $REGION&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Note: &lt;code&gt;$BUCKET&lt;/code&gt; is exported in Step 4. If appropriate, delete the bucket as well.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# Remove S3 objects
aws s3 rm s3://$BUCKET/code/code_bundle.zip
aws s3 rm s3://$BUCKET/dags/conversion_dag.yaml
aws s3 rm s3://$BUCKET/raw/sales_data.csv
aws s3 rm s3://$BUCKET/processed/sales_data.json&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# Delete the IAM role
aws iam delete-role-policy \
  --role-name MWAAServerlessExecutionRole \
  --policy-name MWAAServerlessAccessPolicy

aws iam delete-role --role-name MWAAServerlessExecutionRole&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;With native support for PythonOperator and BashOperator, you can now run the custom code execution patterns that many data engineering teams rely on daily directly in MWAA Serverless. Run data transformations, format conversions, validations, and shell scripts in the serverless runtime – without provisioning additional compute or managing containers.&lt;/p&gt; 
&lt;p&gt;If you are running Airflow workloads on MWAA Provisioned or self-managed infrastructure, your existing PythonOperator and BashOperator logic requires minimal changes. Convert your Python DAG files to the YAML format, package your code as a bundle, and you are ready to run on MWAA Serverless.&lt;/p&gt; 
&lt;p&gt;To get started, visit the &lt;a href="https://docs.aws.amazon.com/mwaa/latest/mwaa-serverless-userguide/operators-python-bash-detail.html" target="_blank" rel="noopener"&gt;Amazon MWAA Serverless documentation&lt;/a&gt; and try the walkthrough earlier in this post with your own data. For pricing details, visit the &lt;a href="https://aws.amazon.com/managed-workflows-for-apache-airflow/pricing/" target="_blank" rel="noopener"&gt;Amazon MWAA pricing page&lt;/a&gt;. We look forward to your feedback.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="alignleft size-full wp-image-93754" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/pnalluri.jpeg" alt="" width="100" height="133"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Pradeep Kumar Nalluri&lt;/h3&gt; 
  &lt;p&gt;Pradeep is a Software Development Engineer at AWS, specializing in architecting and developing scalable applications. In his free time, he enjoys watching TV shows and movies.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/Screenshot-2026-08-24-at-11.11.30 PM.png" alt="" width="100" height="129" class="alignleft size-full wp-image-93757"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Karthik Seshadri&lt;/h3&gt; 
  &lt;p&gt;Karthik is a Sr.&amp;nbsp;Software Development Engineer at AWS, where he specializes in orchestration of big data technologies. He is enthusiastic about serverless technologies, data engineering and building scalable services. Outside of work, he enjoys traveling and playing various sports.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/Screenshot-2026-08-24-at-11.11.54 PM.png" alt="" width="100" height="124" class="alignleft size-full wp-image-93758"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Aritra Ghosh&lt;/h3&gt; 
  &lt;p&gt;Aritra is a Senior Product Manager at Amazon Web Services (AWS), where he leads product development for Amazon Managed Workflows for Apache Airflow (Amazon MWAA) and Amazon SageMaker Unified Studio. Outside of work, Aritra enjoys playing squash and hitting the gym.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/25/Screenshot-2026-08-24-at-11.12.11 PM.png" alt="" width="100" height="109" class="alignleft size-full wp-image-93759"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Sriram Ramarathnam&lt;/h3&gt; 
  &lt;p&gt;Sriram is a Software Development Manager on the AWS Glue, AWS Data Pipeline and Managed Serverless Airflow team in AWS Analytics. His team works on solving challenging problems in orchestration space across serverless and provisioned compute offerings.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Enable cross-cloud analytics with Amazon S3 Tables and Google BigQuery, Part 1: IAM-based access control</title>
		<link>https://aws.amazon.com/blogs/big-data/enable-cross-cloud-analytics-with-amazon-s3-tables-and-google-bigquery-part-1-iam-based-access-control/</link>
		
		<dc:creator><![CDATA[Lakshmi Nair]]></dc:creator>
		<pubDate>Tue, 25 Aug 2026 16:38:49 +0000</pubDate>
				<category><![CDATA[Advanced (300)]]></category>
		<category><![CDATA[Amazon S3 Tables]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<guid isPermaLink="false">a13d515c70f9ec678c3ea53a5e6ed0501a244c26</guid>

					<description>Your Google BigQuery users need to query data that lives in Amazon S3 Tables on AWS without copying it across clouds. This post shows how to connect BigQuery to Amazon S3 Tables through the AWS Glue Iceberg REST Catalog using IAM-based access control, so you keep one governed dataset and query it live from BigQuery.</description>
										<content:encoded>&lt;p&gt;Organizations running analytics workloads across multiple clouds often hit the same friction: the data lives on one cloud, but the engine querying it lives on another. Copying data across the boundary creates a second dataset that must be kept in sync, adding cost, latency, and reconciliation overhead. In this post, we address a specific instance of that pattern: your Google BigQuery users need to work with data that lives in &lt;a href="https://aws.amazon.com/s3/features/tables/" target="_blank" rel="noopener"&gt;Amazon S3 Tables&lt;/a&gt;, a capability of Amazon Simple Storage Service (Amazon S3), on AWS. The ideal outcome is a single, governed dataset that serves teams in both clouds without a standing replication pipeline between them.&lt;/p&gt; 
&lt;p&gt;With Amazon S3 Tables, you get managed Apache Iceberg tables with built-in compaction, snapshot management, and an integration with the &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/serverless-etl-aws-glue/aws-glue-data-catalog.html" target="_blank" rel="noopener"&gt;AWS Glue Data Catalog&lt;/a&gt;. Because S3 Tables stores data in the open Iceberg format, supported external engines can read it directly if the right access path exists.&lt;/p&gt; 
&lt;p&gt;This two-part blog series demonstrates how you can connect Google BigQuery to Amazon S3 Tables using the cross-cloud lakehouse with AWS Glue. We cover two access control approaches:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/iam/" target="_blank" rel="noopener"&gt;AWS Identity and Access Management (IAM)&lt;/a&gt;: You can define a single policy that uses IAM permissions to set up access to both table metadata and data.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/lake-formation/" target="_blank" rel="noopener"&gt;AWS Lake Formation&lt;/a&gt;: You can use temporary vended credentials for data access, with metadata access managed by Lake Formation permissions.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;This post focuses on the IAM-based approach. &lt;a href="https://aws.amazon.com/blogs/big-data/enable-cross-cloud-analytics-with-amazon-s3-tables-and-google-bigquery-part-2-access-control-with-lake-formation/" target="_blank" rel="noopener"&gt;Part 2&lt;/a&gt; covers the Lake Formation approach for organizations that need credential-vended access across multiple engines.&lt;/p&gt; 
&lt;p&gt;By the end, you will have BigQuery querying Iceberg tables stored on S3 Tables without data copy or duplication, providing live access to Iceberg data.&lt;/p&gt; 
&lt;h2 id="cross-cloud-analytics-scenarios"&gt;Cross-cloud analytics scenarios&lt;/h2&gt; 
&lt;p&gt;There are several scenarios where organizations benefit from cross-cloud querying capabilities. Here are some of the common patterns this architecture addresses:&lt;/p&gt; 
&lt;h3 id="schema-evolution-across-cloud-boundaries"&gt;Schema evolution across cloud boundaries&lt;/h3&gt; 
&lt;p&gt;When source schemas change frequently, streaming pipelines writing to BigQuery-managed store require coordinated DDL changes on the BigQuery table and downstream views. Teams often work around this challenge by storing payloads as untyped columns and parsing them later.&lt;/p&gt; 
&lt;p&gt;With Iceberg on S3 Tables, schema evolution is tracked in table metadata. When the writing engine adds a new column, BigQuery’s Lakehouse refresh picks up the updated schema automatically on the next sync cycle.&lt;/p&gt; 
&lt;h3 id="multi-cloud-analytics-without-data-duplication"&gt;Multi-cloud analytics without data duplication&lt;/h3&gt; 
&lt;p&gt;A company has its production data environment on AWS (data lakes, warehouses, streaming) but acquired a business unit that runs analytics exclusively on BigQuery. In-place querying from BigQuery keeps your data in Amazon S3 Tables, so you pay for one copy, work from live data, and avoid the operational overhead of a synchronized second store.&lt;/p&gt; 
&lt;h3 id="cost-optimization-for-infrequently-queried-datasets"&gt;Cost optimization for infrequently queried datasets&lt;/h3&gt; 
&lt;p&gt;An organization has hundreds of datasets on AWS, but only a fraction is queried daily from BigQuery. Replicating all of them to Google Cloud Storage drives unnecessary storage and transfer costs. With Lakehouse catalog federation, you keep your data on S3 Tables. BigQuery reads data only when queried, so you pay per query rather than per-copy storage.&lt;/p&gt; 
&lt;h3 id="decoupled-compute-across-engines"&gt;Decoupled compute across engines&lt;/h3&gt; 
&lt;p&gt;Data team wants storage on AWS with the flexibility for multiple engines to read the same data: BigQuery and &lt;a href="https://aws.amazon.com/redshift/" target="_blank" rel="noopener"&gt;Amazon Redshift&lt;/a&gt; for data warehousing use cases, &lt;a href="https://aws.amazon.com/athena/" target="_blank" rel="noopener"&gt;Amazon Athena&lt;/a&gt; for interactive ad-hoc querying, &lt;a href="https://aws.amazon.com/sagemaker/ai/" target="_blank" rel="noopener"&gt;Amazon SageMaker AI&lt;/a&gt; for machine learning (ML). With Apache Iceberg’s open format, you can use one storage layer, many compute engines, no data copies between them.&lt;/p&gt; 
&lt;h2 id="solution-overview"&gt;Solution overview&lt;/h2&gt; 
&lt;p&gt;You use the AWS Glue Iceberg REST Catalog (IRC) as the bridge between BigQuery and S3 Tables. BigQuery’s cross-cloud Lakehouse creates a federated catalog that syncs metadata from the Glue IRC, then uses the synced metadata to read Iceberg data files directly.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-1-1.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-1-1.png" alt="Architecture diagram showing BigQuery connecting to Amazon S3 Tables through the AWS Glue Iceberg REST Catalog" width="800" height="792"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Architecture diagram showing BigQuery connecting to Amazon S3 Tables through the AWS Glue Iceberg REST Catalog&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The key components in this architecture:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon S3 Tables&lt;/strong&gt;: With Amazon S3 Tables, you get a fully managed Apache Iceberg table experience in Amazon S3, optimized for analytics workloads. You can register table metadata in the AWS Glue Data Catalog for discovery and governance.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Glue Data Catalog&lt;/strong&gt;: With AWS Glue Data Catalog, you can access the federated &lt;code&gt;s3tablescatalog&lt;/code&gt; catalog that maps S3 Tables resources (table buckets, namespaces, tables) into a catalog hierarchy from supported analytics engines. The standard Iceberg REST endpoint of Glue Data Catalog serves table metadata to external engines. BigQuery connects through this endpoint.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Google Cross-Cloud Lakehouse&lt;/strong&gt;: With Google Cross-Cloud Lakehouse, you can connect BigQuery to external Iceberg catalogs. It assumes an AWS IAM role using OpenID Connect (OIDC), calls the Glue Iceberg REST endpoint, and syncs metadata on a configurable refresh interval.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;p&gt;Before you begin, you need:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An AWS account with Amazon S3 Tables available in your AWS Region.&lt;/li&gt; 
 &lt;li&gt;A Google Cloud project with billing enabled and the BigLake API activated.&lt;/li&gt; 
 &lt;li&gt;AWS Command Line Interface (AWS CLI) and gcloud CLI installed and configured.&lt;/li&gt; 
 &lt;li&gt;An S3 table bucket with at least one namespace and table containing data.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="setting-up-amazon-s3-tables"&gt;Setting up Amazon S3 Tables&lt;/h2&gt; 
&lt;p&gt;If you already have S3 Tables with data, skip to the next section. Otherwise, create a table bucket, namespace, and populate a table.&lt;/p&gt; 
&lt;h3 id="create-a-table-bucket-and-namespace"&gt;Create a table bucket and namespace&lt;/h3&gt; 
&lt;p&gt;Use the AWS CLI to create resources as follows:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;# Create a Table bucket
aws s3tables create-table-bucket \
    --name &amp;lt;TABLE_BUCKET_NAME&amp;gt; \
    --region &amp;lt;REGION&amp;gt;

# Create a Namespace (Database)
aws s3tables create-namespace \
    --table-bucket-arn "arn:aws:s3tables:&amp;lt;REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET_NAME&amp;gt;" \
    --namespace &amp;lt;NAMESPACE&amp;gt; \
    --region &amp;lt;REGION&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="integrating-s3-tables-with-the-glue-data-catalog"&gt;Integrating S3 Tables with the Glue Data Catalog&lt;/h2&gt; 
&lt;p&gt;For BigQuery to access S3 Tables, the tables must be discoverable through the Glue Data Catalog. S3 Tables integrates with Glue through a federated catalog called &lt;code&gt;s3tablescatalog&lt;/code&gt;.&lt;/p&gt; 
&lt;h3 id="set-up-s3-tables-integration-with-the-glue-data-catalog-using-iam-mode"&gt;Set up S3 Tables integration with the Glue Data Catalog using IAM mode&lt;/h3&gt; 
&lt;p&gt;Open the Amazon S3 console:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;In the navigation pane, choose &lt;strong&gt;Table buckets&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Enable integration&lt;/strong&gt;, and then choose &lt;strong&gt;Enable integration&lt;/strong&gt; again to confirm.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;This creates the &lt;code&gt;s3tablescatalog&lt;/code&gt; federated catalog in Glue, where access is controlled entirely by IAM policies on the calling role. This is a one-time setup per account and Region. After you enable it, the analytics integration applies to all table buckets in your account.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-2-1.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-2-1.png" alt="The Enable integration option on the table buckets page of the Amazon S3 console" width="800" height="126"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Enabling the S3 Tables integration in the Amazon S3 console&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Alternatively, create the catalog using the AWS CLI:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;aws glue create-catalog --region &amp;lt;REGION&amp;gt; --cli-input-json '{
  "Name": "s3tablescatalog",
  "CatalogInput": {
    "FederatedCatalog": {
      "Identifier": "arn:aws:s3tables:&amp;lt;REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/*",
      "ConnectionName": "aws:s3tables"
    },
    "CreateDatabaseDefaultPermissions": [
      { "Principal": {"DataLakePrincipalIdentifier": "IAM_ALLOWED_PRINCIPALS"}, "Permissions": ["ALL"] }
    ],
    "CreateTableDefaultPermissions": [
      { "Principal": {"DataLakePrincipalIdentifier": "IAM_ALLOWED_PRINCIPALS"}, "Permissions": ["ALL"] }
    ]
  }
}'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h4 id="create-a-table-and-insert-data"&gt;Create a table and insert data&lt;/h4&gt; 
&lt;p&gt;Now, to create the table and insert data, open the Amazon Athena console. In the query editor, select &lt;code&gt;s3tablescatalog/&amp;lt;TABLE_BUCKET_NAME&amp;gt;&lt;/code&gt; as your data source and &lt;code&gt;&amp;lt;NAMESPACE&amp;gt;&lt;/code&gt; as the database. Then run the following SQL statements one by one:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;CREATE TABLE `&amp;lt;NAMESPACE&amp;gt;`.orders (
    order_id STRING,
    customer_id STRING,
    amount BIGINT,
    order_date DATE,
    region STRING
)
TBLPROPERTIES ('table_type' = 'iceberg');

INSERT INTO orders
VALUES
    ('ORD-001', 'C100', 4500, DATE '2024-06-01', 'EMEA'),
    ('ORD-002', 'C200', 8900, DATE '2024-06-01', 'EMEA'),
    ('ORD-003', 'C100', 3200, DATE '2024-06-02', 'NAMER'),
    ('ORD-004', 'C300', 12000, DATE '2024-06-02', 'NAMER'),
    ('ORD-005', 'C400', 6700, DATE '2024-06-03', 'APJ'),
    ('ORD-006', 'C200', 4100, DATE '2024-06-03', 'APJ'),
    ('ORD-007', 'C500', 9500, DATE '2024-06-04', 'EMEA'),
    ('ORD-008', 'C100', 2800, DATE '2024-06-04', 'LATAM'),
    ('ORD-009', 'C600', 15000, DATE '2024-06-05', 'NAMER'),
    ('ORD-010', 'C300', 7200, DATE '2024-06-05', 'LATAM');&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="configuring-cross-cloud-access"&gt;Configuring cross-cloud access&lt;/h2&gt; 
&lt;p&gt;BigQuery assumes an AWS IAM role via OIDC federation to access the Glue IRC. This section walks through creating the role, OIDC provider, and permissions.&lt;/p&gt; 
&lt;h3 id="create-the-oidc-identity-provider"&gt;Create the OIDC identity provider&lt;/h3&gt; 
&lt;p&gt;Register Google as an OIDC identity provider in your AWS account. This allows AWS to validate tokens issued by Google’s identity service:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;aws iam create-open-id-connect-provider \
    --url https://accounts.google.com \
    --client-id-list accounts.google.com \
    --thumbprint-list 08745487e891c19e3078c1f2a07e452950ef36f6&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The –thumbprint-list parameter is optional. When omitted, IAM automatically retrieves the thumbprint from the OIDC provider’s certificate. See &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html" target="_blank" rel="noopener"&gt;AWS documentation&lt;/a&gt; for details.&lt;/p&gt; 
&lt;h3 id="create-the-cross-cloud-iam-role"&gt;Create the cross-cloud IAM role&lt;/h3&gt; 
&lt;p&gt;Login into AWS Console, and&amp;nbsp; create the role with a placeholder trust policy. You will update it with the actual BigLake service account ID after you create the federated catalog in Google Cloud.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;aws iam create-role \
    --role-name bigquery-cross-cloud-role \
    --max-session-duration 43200 \
    --assume-role-policy-document '{
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": {
          "Federated": "arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/accounts.google.com"
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": {
            "accounts.google.com:sub": ["PLACEHOLDER"],
            "accounts.google.com:aud": ["PLACEHOLDER"]
          }
        }
      }]
    }'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The &lt;code&gt;--max-session-duration 43200&lt;/code&gt; allows sessions up to 12 hours, which is needed for long-running BigQuery queries.&lt;/p&gt; 
&lt;h3 id="attach-permissions"&gt;Attach permissions&lt;/h3&gt; 
&lt;p&gt;The permissions policy differs based on your access control approach. For the IAM-based approach, attach the following policy:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "GlueRead",
      "Effect": "Allow",
      "Action": [
        "glue:GetCatalog", "glue:GetDatabase", "glue:GetDatabases",
        "glue:GetTable", "glue:GetTables", "glue:GetPartition", "glue:GetPartitions"
      ],
      "Resource": [
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:catalog",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:catalog/s3tablescatalog",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:catalog/s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:database/s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;/&amp;lt;NAMESPACE&amp;gt;",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:table/s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;/&amp;lt;NAMESPACE&amp;gt;/*"
      ]
    },
    {
      "Sid": "S3TablesRead",
      "Effect": "Allow",
      "Action": [
        "s3tables:GetTableBucket", "s3tables:ListTableBuckets",
        "s3tables:ListNamespaces", "s3tables:GetNamespace",
        "s3tables:ListTables", "s3tables:GetTable",
        "s3tables:GetTableMetadataLocation", "s3tables:GetTableData"
      ],
      "Resource": [
        "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;",
        "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;/*"
      ]
    },
    {
      "Sid": "S3TablesListBuckets",
      "Effect": "Allow",
      "Action": ["s3tables:ListTableBuckets"],
      "Resource": "*"
    }
  ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="connecting-bigquery-to-s3-tables"&gt;Connecting BigQuery to S3 Tables&lt;/h2&gt; 
&lt;p&gt;With the AWS side configured, create the federated catalog in Google Cloud that connects BigQuery to the Glue IRC.&lt;/p&gt; 
&lt;h3 id="create-the-federated-catalog"&gt;Create the federated catalog&lt;/h3&gt; 
&lt;p&gt;Authenticate to Google Cloud using &lt;code&gt;gcloud auth login&lt;/code&gt;, or use Cloud Shell, which is pre-authenticated. Verify that the BigLake API is enabled:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;gcloud services enable biglake.googleapis.com --project="&amp;lt;GCP_PROJECT_ID&amp;gt;"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h4 id="for-iam-mode"&gt;For IAM mode:&lt;/h4&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;gcloud alpha biglake iceberg catalogs create &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" \
    --catalog-type=federated \
    --federated-catalog-type=glue \
    --glue-aws-region=&amp;lt;AWS_REGION&amp;gt; \
    --glue-aws-role-arn=arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:role/bigquery-cross-cloud-role \
    --glue-warehouse=&amp;lt;AWS_ACCOUNT_ID&amp;gt;:s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt; \
    --primary-location=&amp;lt;GCP_REGION&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The &lt;code&gt;--glue-warehouse&lt;/code&gt; parameter uses the format &lt;code&gt;&amp;lt;AWS_ACCOUNT_ID&amp;gt;:s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;&lt;/code&gt;. This tells the Glue IRC to scope requests to your specific S3 Tables bucket within the federated catalog hierarchy.&lt;/p&gt; 
&lt;p&gt;The &lt;code&gt;--primary-location&lt;/code&gt; refers to the Google Cloud region where the federated catalog metadata is stored. Use the &lt;a href="https://docs.cloud.google.com/lakehouse/docs/regions-capabilities-cross-cloud-lakehouse" target="_blank" rel="noopener"&gt;AWS to Google Cloud region mapping&lt;/a&gt; to find the corresponding GCP region for your AWS Region. For example, AWS us-east-1 maps to GCP us-east4.&lt;/p&gt; 
&lt;h3 id="retrieve-the-biglake-service-account-id"&gt;Retrieve the BigLake service account ID&lt;/h3&gt; 
&lt;p&gt;After catalog creation, Google provisions a dedicated service account for your federated catalog. Retrieve its numeric ID:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;BIGLAKE_SA_ID=$(gcloud alpha biglake iceberg catalogs describe &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" \
    --format="value(biglake-service-account-id)")
echo $BIGLAKE_SA_ID&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="update-the-aws-trust-policy"&gt;Update the AWS trust policy&lt;/h3&gt; 
&lt;p&gt;Back on AWS, replace the placeholder in the IAM role’s trust policy with the actual service account ID:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;aws iam update-assume-role-policy \
    --role-name bigquery-cross-cloud-role \
    --policy-document '{
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": {
          "Federated": "arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/accounts.google.com"
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": {
            "accounts.google.com:sub": ["&amp;lt;BIGLAKE_SA_ID&amp;gt;"],
            "accounts.google.com:aud": ["&amp;lt;BIGLAKE_SA_ID&amp;gt;"]
          }
        }
      }]
    }'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Register the service account ID in the OIDC provider’s audience list. Without this step, AWS rejects the token because the &lt;code&gt;aud&lt;/code&gt; claim doesn’t match any registered client:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;aws iam add-client-id-to-open-id-connect-provider \
    --open-id-connect-provider-arn "arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/accounts.google.com" \
    --client-id "&amp;lt;BIGLAKE_SA_ID&amp;gt;"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="set-up-metadata-sync"&gt;Set up metadata sync&lt;/h3&gt; 
&lt;p&gt;Wait 3–5 minutes for IAM changes to propagate globally, then set up background refresh:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;gcloud alpha biglake iceberg catalogs update &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" \
    --refresh-interval=300s&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The &lt;code&gt;--refresh-interval&lt;/code&gt; (300 seconds in this example) determines how often BigQuery syncs metadata from the Glue IRC. New tables and schema changes appear in BigQuery within this interval.&lt;/p&gt; 
&lt;h3 id="querying-from-bigquery"&gt;Querying from BigQuery&lt;/h3&gt; 
&lt;p&gt;After the catalog refresh completes, BigQuery automatically creates external datasets corresponding to the synced namespaces. No manual CREATE SCHEMA is required.&lt;/p&gt; 
&lt;p&gt;Verify the sync:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;gcloud alpha biglake iceberg namespaces list \
    --catalog="&amp;lt;FEDERATED_CATALOG_NAME&amp;gt;" \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Run a query in BigQuery:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * FROM `&amp;lt;GCP_PROJECT_ID&amp;gt;.&amp;lt;FEDERATED_CATALOG_NAME&amp;gt;.&amp;lt;NAMESPACE&amp;gt;.orders` LIMIT 1000&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Sample Query Output:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT
    customer_id,
    COUNT(*) as order_count,
    SUM(amount) as total_spend
FROM `&amp;lt;PROJECT_ID&amp;gt;.&amp;lt;FEDERATED_CATALOG_NAME&amp;gt;.&amp;lt;NAMESPACE&amp;gt;.orders`
GROUP BY customer_id
ORDER BY total_spend DESC&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-3-1.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-3-1.png" alt="BigQuery query results showing order count and total spend per customer from the Amazon S3 Tables data" width="800" height="816"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: BigQuery query results returned directly from the Amazon S3 Tables data&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;BigQuery reads the Iceberg metadata to identify which Parquet data files contain relevant data. It also applies partition pruning where applicable, and fetches only the necessary files from S3 Tables managed storage.&lt;/p&gt; 
&lt;h2 id="schema-evolution"&gt;Schema evolution&lt;/h2&gt; 
&lt;p&gt;When new columns are added to an Iceberg table on the AWS side (through Spark, Athena, or the Glue IRC), the schema change is captured in Iceberg’s metadata. On the next Lakehouse refresh cycle, BigQuery picks up the new columns automatically. No DDL changes are needed in BigQuery.&lt;/p&gt; 
&lt;h2 id="metadata-freshness"&gt;Metadata freshness&lt;/h2&gt; 
&lt;p&gt;The &lt;code&gt;s3tablescatalog&lt;/code&gt; in Glue is a federated catalog that resolves table metadata live from the S3 Tables service on each request. When a streaming job commits new data to an S3 Table, the latest metadata is immediately available through the AWS Glue IRC. BigQuery sees the update on its next refresh cycle (as configured by &lt;code&gt;--refresh-interval&lt;/code&gt;).&lt;/p&gt; 
&lt;h2 id="oidc-identity-federation"&gt;OIDC identity federation&lt;/h2&gt; 
&lt;p&gt;The trust relationship between Google Cloud and AWS uses OpenID Connect. When BigQuery Lakehouse needs to access your data, it presents a signed JWT token containing:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code&gt;iss&lt;/code&gt;: &lt;code&gt;accounts.google.com&lt;/code&gt; (the issuer)&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;sub&lt;/code&gt;: The BigLake service account ID (identifies which catalog is making the request)&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;aud&lt;/code&gt;: The same service account ID (the intended audience)&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;AWS validates this token against the registered OIDC provider and trust policy conditions before issuing temporary credentials. Each federated catalog receives a unique service account ID, providing per-catalog isolation and auditability through AWS CloudTrail.&lt;/p&gt; 
&lt;h2 id="network-path"&gt;Network path&lt;/h2&gt; 
&lt;p&gt;By default, traffic between BigQuery and AWS travels over the public internet. For workloads requiring private connectivity, Google Cloud supports Cross-Cloud Interconnect or Partner Interconnect. This helps routing queries over a dedicated network path. Refer to the &lt;a href="https://docs.cloud.google.com/lakehouse/docs/set-up-cross-cloud-lakehouse-aws-glue" target="_blank" rel="noopener"&gt;Google Cloud documentation&lt;/a&gt; for private interconnect configuration.&lt;/p&gt; 
&lt;h2 id="clean-up"&gt;Clean up&lt;/h2&gt; 
&lt;p&gt;To avoid ongoing charges, remove the resources created in this walkthrough.&lt;/p&gt; 
&lt;p&gt;On AWS:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;# Delete the table (if created for this walkthrough)
aws s3tables delete-table \
    --table-bucket-arn "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;" \
    --namespace analytics --name orders --region &amp;lt;AWS_REGION&amp;gt;

# Delete namespace and table bucket
aws s3tables delete-namespace \
    --table-bucket-arn "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;" \
    --namespace &amp;lt;NAMESPACE&amp;gt; --region &amp;lt;AWS_REGION&amp;gt;

aws s3tables delete-table-bucket --name &amp;lt;TABLE_BUCKET&amp;gt; --region &amp;lt;AWS_REGION&amp;gt;

# Delete IAM role and OIDC provider (if no longer needed)
aws iam delete-role --role-name bigquery-cross-cloud-role&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;On Google Cloud:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-code"&gt;gcloud alpha biglake iceberg catalogs delete &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" --location=&amp;lt;GCP_REGION&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;This post demonstrated how to query Amazon S3 Tables from Google BigQuery using the open Apache Iceberg format and the AWS Glue Iceberg REST Catalog as the metadata bridge. Using Apache Iceberg’s open format, you can write data once on AWS and read it from supported engines that speak Iceberg, including BigQuery. We used IAM-based access control to govern access to both Glue Data Catalog metadata and the underlying Amazon S3 Tables data. This is the simpler configuration path with fewer components. In &lt;a href="https://aws.amazon.com/blogs/big-data/enable-cross-cloud-analytics-with-amazon-s3-tables-and-google-bigquery-part-2-access-control-with-lake-formation/" target="_blank" rel="noopener"&gt;Part 2&lt;/a&gt;, we walk through configuring AWS Lake Formation to vend temporary, scoped credentials to BigQuery for data access.&lt;/p&gt; 
&lt;p&gt;To get started with this pattern in your environment:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Review the &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables.html" target="_blank" rel="noopener"&gt;Amazon S3 Tables documentation&lt;/a&gt; for table bucket setup.&lt;/li&gt; 
 &lt;li&gt;Follow the &lt;a href="https://docs.cloud.google.com/lakehouse/docs/set-up-cross-cloud-lakehouse-aws-glue" target="_blank" rel="noopener"&gt;Google Cloud cross-cloud Lakehouse setup guide&lt;/a&gt; for the BigQuery federation configuration.&lt;/li&gt; 
 &lt;li&gt;Explore the &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/iceberg-rest-apis.html" target="_blank" rel="noopener"&gt;AWS Glue Iceberg REST Catalog APIs&lt;/a&gt; for programmatic access patterns.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p style="clear: both"&gt;&lt;/p&gt; 
&lt;hr style="width: 100%"&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-4-1.jpeg" alt="Lakshmi Nair" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Lakshmi Nair&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/lakshmi--nair/" target="_blank" rel="noopener"&gt;Lakshmi&lt;/a&gt; is a Principal Analytics Specialist Solutions Architect at AWS. She specializes in designing advanced analytics systems across industries. She focuses on crafting cloud-based data platforms, enabling real-time streaming, big data processing, and robust data governance.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-5-1.png" alt="Srividya Parthasarathy" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Srividya Parthasarathy&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/srividya-parthasarathy-8b71bb32/" target="_blank" rel="noopener"&gt;Srividya&lt;/a&gt; was a Senior Big Data Architect on the AWS Lake Formation team. She works with product team and customer to build robust features and solutions for their analytical data platform. She enjoys building data mesh solutions and sharing them with the community.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Enable cross-cloud analytics with Amazon S3 Tables and Google BigQuery, Part 2: access control with Lake Formation</title>
		<link>https://aws.amazon.com/blogs/big-data/enable-cross-cloud-analytics-with-amazon-s3-tables-and-google-bigquery-part-2-access-control-with-lake-formation/</link>
		
		<dc:creator><![CDATA[Lakshmi Nair]]></dc:creator>
		<pubDate>Tue, 25 Aug 2026 16:38:46 +0000</pubDate>
				<category><![CDATA[Advanced (300)]]></category>
		<category><![CDATA[Amazon S3 Tables]]></category>
		<category><![CDATA[AWS Lake Formation]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<guid isPermaLink="false">bd850988019d2e588a639548b91218a53b4faac1</guid>

					<description>In Part 2 of this series, connect Google BigQuery to Amazon S3 Tables using AWS Lake Formation credential vending. Lake Formation manages fine-grained permissions and issues short-lived, scoped credentials to external engines, so you can centrally govern which teams and query engines read your Iceberg tables on AWS without managing IAM policies for every consumer.</description>
										<content:encoded>&lt;p&gt;In &lt;a href="https://aws.amazon.com/blogs/big-data/enable-cross-cloud-analytics-with-amazon-s3-tables-and-google-bigquery-part-1-iam-based-access-control/" target="_blank" rel="noopener"&gt;Part 1&lt;/a&gt;, we showed how to connect Google BigQuery to Amazon Simple Storage Service (Amazon S3) Tables, a capability of Amazon S3, using access control based on AWS Identity and Access Management (IAM). A single IAM policy governs both table metadata and data access. We also walked through common cross-cloud analytics scenarios where this pattern adds value. This post covers the approach using AWS Lake Formation. Instead of relying solely on IAM policies for data access, Lake Formation manages fine-grained permissions and vends temporary, scoped credentials to the requesting engine. This is a better fit when multiple engines need different levels of access to the same tables, or when you want to manage grants centrally without touching IAM policies every time a new consumer comes along.&lt;/p&gt; 
&lt;h2 id="solution-overview"&gt;Solution overview&lt;/h2&gt; 
&lt;p&gt;You use the AWS Glue Iceberg REST Catalog (IRC) as the bridge between BigQuery and S3 Tables. BigQuery’s cross-cloud Lakehouse creates a federated catalog that syncs metadata from the Glue IRC, then uses the synced metadata to read Iceberg data files directly.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-1-1.png" alt="Architecture diagram showing BigQuery connecting to Amazon S3 Tables through the AWS Glue Iceberg REST Catalog" width="800" height="792"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Architecture diagram showing BigQuery connecting to Amazon S3 Tables through the AWS Glue Iceberg REST Catalog&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The key components in this architecture:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon S3 Tables&lt;/strong&gt;: With Amazon S3 Tables, data is stored in table buckets, specifically designed for storing tables in the Apache Iceberg format. Table metadata is registered on AWS Glue Data Catalog for discovery and governance.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Glue Data Catalog&lt;/strong&gt;: With AWS Glue Data Catalog, you can access the federated &lt;code&gt;s3tablescatalog&lt;/code&gt; catalog that maps S3 Tables resources (table buckets, namespaces, tables) into a catalog hierarchy from supported analytics engines. The standard Iceberg REST endpoint of Glue Data Catalog serves table metadata to external engines. BigQuery connects through this endpoint.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Lake Formation&lt;/strong&gt;: With AWS Lake Formation, you define access permissions at the catalog, database, and table level. Instead of granting broad IAM permissions for data access, Lake Formation evaluates permissions at query time and issues short-lived credentials limited to the resources the caller is authorized to read.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Google Cross-Cloud Lakehouse&lt;/strong&gt;: With Google Cross-Cloud Lakehouse, you can connect BigQuery to external Iceberg catalogs. It assumes an IAM role using OpenID Connect (OIDC), calls the AWS Glue Iceberg REST endpoint, and syncs metadata on a configurable refresh interval.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;p&gt;Before you begin, you need:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An AWS account with Amazon S3 Tables available in your AWS Region.&lt;/li&gt; 
 &lt;li&gt;A Google Cloud project with billing enabled and the BigLake API activated.&lt;/li&gt; 
 &lt;li&gt;AWS Command Line Interface (AWS CLI) and gcloud CLI installed and configured.&lt;/li&gt; 
 &lt;li&gt;An S3 table bucket with at least one namespace and table containing data.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="setting-up-amazon-s3-tables"&gt;Setting up Amazon S3 Tables&lt;/h2&gt; 
&lt;p&gt;If you already have S3 Tables with data, skip to the next section. Otherwise, create a table bucket, namespace, and populate a table.&lt;/p&gt; 
&lt;h3 id="create-a-table-bucket-and-namespace"&gt;Create a table bucket and namespace&lt;/h3&gt; 
&lt;p&gt;Use AWS CLI to create resources as follows:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# Create a Table bucket
aws s3tables create-table-bucket \
    --name &amp;lt;TABLE_BUCKET_NAME&amp;gt; \
    --region &amp;lt;REGION&amp;gt;

# Create a Namespace (Database)
aws s3tables create-namespace \
    --table-bucket-arn "arn:aws:s3tables:&amp;lt;REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET_NAME&amp;gt;" \
    --namespace &amp;lt;NAMESPACE&amp;gt; \
    --region &amp;lt;REGION&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="set-up-s3-tables-integration-with-the-glue-data-catalog-using-lake-formation-mode"&gt;Set up S3 Tables integration with the Glue Data Catalog using Lake Formation mode&lt;/h3&gt; 
&lt;p&gt;Lake Formation needs its own service role to interact with S3 Tables on your behalf. This is the role Lake Formation assumes internally when it reads or writes data on behalf of authorized callers.&lt;/p&gt; 
&lt;p&gt;Create a Lake Formation service IAM role named &lt;code&gt;LakeFormationS3TablesServiceRole&lt;/code&gt; with the following policy:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LakeFormationPermissionsForS3ListTableBucket",
      "Effect": "Allow",
      "Action": ["s3tables:ListTableBuckets"],
      "Resource": ["*"]
    },
    {
      "Sid": "LakeFormationDataAccessPermissionsForS3TableBucket",
      "Effect": "Allow",
      "Action": [
        "s3tables:CreateTableBucket", "s3tables:GetTableBucket",
        "s3tables:CreateNamespace", "s3tables:GetNamespace",
        "s3tables:ListNamespaces", "s3tables:DeleteNamespace",
        "s3tables:DeleteTableBucket", "s3tables:CreateTable",
        "s3tables:DeleteTable", "s3tables:GetTable",
        "s3tables:ListTables", "s3tables:RenameTable",
        "s3tables:UpdateTableMetadataLocation", "s3tables:GetTableMetadataLocation",
        "s3tables:GetTableData", "s3tables:PutTableData"
      ],
      "Resource": ["arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/*"]
    }
  ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Attach the following trust relationship:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LakeFormationDataAccessPolicy",
      "Effect": "Allow",
      "Principal": { "Service": "lakeformation.amazonaws.com" },
      "Action": ["sts:AssumeRole", "sts:SetContext", "sts:SetSourceIdentity"],
      "Condition": { "StringEquals": { "aws:SourceAccount": "&amp;lt;AWS_ACCOUNT_ID&amp;gt;" } }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;In the Lake Formation console, in the navigation pane, choose &lt;strong&gt;Catalogs&lt;/strong&gt;, and then choose &lt;strong&gt;Enable S3 Table Integration&lt;/strong&gt;.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-2-1.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-2-1.png" alt="The Enable S3 Table Integration option on the Catalogs page of the Lake Formation console" width="800" height="510"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Enabling the S3 Tables integration in the Lake Formation console&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Choose the role you created earlier when prompted for an IAM role, and select &lt;strong&gt;Allow external engines to access data in Amazon S3 locations with full table access&lt;/strong&gt;.&lt;/p&gt; 
&lt;p&gt;S3 Tables integration performs the following:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Registers the S3 Tables data location with Lake Formation.&lt;/li&gt; 
 &lt;li&gt;Creates the &lt;code&gt;s3tablescatalog&lt;/code&gt; federated catalog in Glue.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Before enabling the integration, verify your Lake Formation data lake settings have empty default permissions to prevent &lt;code&gt;IAMAllowedPrincipals&lt;/code&gt; from being auto-granted on the catalog:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws lakeformation put-data-lake-settings \
    --data-lake-settings '{"DataLakeAdmins":[{"DataLakePrincipalIdentifier":"arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:role/&amp;lt;ADMIN_ROLE&amp;gt;"}],"CreateDatabaseDefaultPermissions":[],"CreateTableDefaultPermissions":[]}' \
    --region &amp;lt;AWS_REGION&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-3-1.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-3-1.png" alt="The S3 Tables integration dialog in Lake Formation with full table access selected" width="800" height="419"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: Selecting full table access for external engines during S3 Tables integration&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;When you select this option, you&amp;nbsp; allow external engines to access data in Amazon S3 locations with full table access, and Lake Formation grants full table-level access to external engines. Column-level and row-level filtering are not enforced for external engine connections. Access is granted at the whole-table level.&lt;/p&gt; 
&lt;p&gt;Verify the integration by confirming the catalog in Lake Formation console.&lt;/p&gt; 
&lt;h4 id="create-a-table-and-insert-data"&gt;Create a table and insert data&lt;/h4&gt; 
&lt;p&gt;Now, to create the table and insert data, open the Amazon Athena console. In the query editor, select &lt;code&gt;s3tablescatalog/&amp;lt;TABLE_BUCKET_NAME&amp;gt;&lt;/code&gt; as your data source and &lt;code&gt;&amp;lt;NAMESPACE&amp;gt;&lt;/code&gt; as the database. Then run the following SQL statements one by one:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;CREATE TABLE `&amp;lt;NAMESPACE&amp;gt;`.orders (
    order_id STRING,
    customer_id STRING,
    amount BIGINT,
    order_date DATE,
    region STRING
)
TBLPROPERTIES ('table_type' = 'iceberg');

INSERT INTO orders
VALUES
    ('ORD-001', 'C100', 4500, DATE '2024-06-01', 'EMEA'),
    ('ORD-002', 'C200', 8900, DATE '2024-06-01', 'EMEA'),
    ('ORD-003', 'C100', 3200, DATE '2024-06-02', 'NAMER'),
    ('ORD-004', 'C300', 12000, DATE '2024-06-02', 'NAMER'),
    ('ORD-005', 'C400', 6700, DATE '2024-06-03', 'APJ'),
    ('ORD-006', 'C200', 4100, DATE '2024-06-03', 'APJ'),
    ('ORD-007', 'C500', 9500, DATE '2024-06-04', 'EMEA'),
    ('ORD-008', 'C100', 2800, DATE '2024-06-04', 'LATAM'),
    ('ORD-009', 'C600', 15000, DATE '2024-06-05', 'NAMER'),
    ('ORD-010', 'C300', 7200, DATE '2024-06-05', 'LATAM');&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="configuring-cross-cloud-access"&gt;Configuring cross-cloud access&lt;/h2&gt; 
&lt;p&gt;BigQuery assumes an AWS IAM role using OIDC federation to access the AWS Glue IRC. This section walks through creating the role, OIDC provider, and permissions.&lt;/p&gt; 
&lt;h3 id="create-the-oidc-identity-provider"&gt;Create the OIDC identity provider&lt;/h3&gt; 
&lt;p&gt;Register Google as an OIDC identity provider in your AWS account. This allows AWS to validate tokens issued by Google’s identity service:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws iam create-open-id-connect-provider \
    --url https://accounts.google.com \
    --client-id-list accounts.google.com \
    --thumbprint-list 08745487e891c19e3078c1f2a07e452950ef36f6&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The –thumbprint-list parameter is optional. When omitted, IAM automatically retrieves the thumbprint from the OIDC provider’s certificate. See &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html" target="_blank" rel="noopener"&gt;AWS documentation&lt;/a&gt; for details.&lt;/p&gt; 
&lt;h3 id="create-the-cross-cloud-iam-role-on-aws"&gt;Create the cross-cloud IAM role on AWS&lt;/h3&gt; 
&lt;p&gt;Sign in to the AWS Management Console. Create the role with a placeholder trust policy. You will update it with the actual BigLake service account ID after you create the federated catalog in Google Cloud.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws iam create-role \
    --role-name bigquery-cross-cloud-role \
    --max-session-duration 43200 \
    --assume-role-policy-document '{
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": {
          "Federated": "arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/accounts.google.com"
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": {
            "accounts.google.com:sub": ["PLACEHOLDER"],
            "accounts.google.com:aud": ["PLACEHOLDER"]
          }
        }
      }]
    }'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The &lt;code&gt;--max-session-duration 43200&lt;/code&gt; allows sessions up to 12 hours, which is needed for long-running BigQuery queries.&lt;/p&gt; 
&lt;h3 id="attach-permissions"&gt;Attach permissions&lt;/h3&gt; 
&lt;p&gt;The permissions policy differs based on your access control approach. For the Lake Formation approach, attach the following policy:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "GlueRead",
      "Effect": "Allow",
      "Action": [
        "glue:GetCatalog", "glue:GetDatabase", "glue:GetDatabases",
        "glue:GetTable", "glue:GetTables", "glue:GetPartition", "glue:GetPartitions"
      ],
      "Resource": [
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:catalog",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:catalog/s3tablescatalog",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:catalog/s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:database/s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;/&amp;lt;NAMESPACE&amp;gt;",
        "arn:aws:glue:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:table/s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;/&amp;lt;NAMESPACE&amp;gt;/*"
      ]
    },
    {
      "Sid": "S3TablesRead",
      "Effect": "Allow",
      "Action": [
        "s3tables:GetTableBucket", "s3tables:ListTableBuckets",
        "s3tables:ListNamespaces", "s3tables:GetNamespace",
        "s3tables:ListTables", "s3tables:GetTable",
        "s3tables:GetTableMetadataLocation", "s3tables:GetTableData"
      ],
      "Resource": [
        "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;",
        "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;/*"
      ]
    },
    {
      "Sid": "LakeFormationCredentialVending",
      "Effect": "Allow",
      "Action": ["lakeformation:GetDataAccess"],
      "Resource": "*"
    }
  ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="grant-lake-formation-permissions"&gt;Grant Lake Formation permissions&lt;/h3&gt; 
&lt;p&gt;Lake Formation permissions work as a layered grant model: you grant access at each level of the catalog hierarchy, from catalog down to table. The cross-cloud role needs &lt;code&gt;DESCRIBE&lt;/code&gt; on the catalog and database so it can discover what exists, and &lt;code&gt;SELECT&lt;/code&gt; plus &lt;code&gt;DESCRIBE&lt;/code&gt; on the table so it can read the actual data. Without grants at every level, Lake Formation denies access even if the IAM policy allows it.&lt;/p&gt; 
&lt;p&gt;If using Lake Formation, grant the &lt;code&gt;bigquery-cross-cloud-role&lt;/code&gt; access to your tables:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Grant catalog permission: &lt;code&gt;DESCRIBE&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;Grant database permission: &lt;code&gt;DESCRIBE&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;Grant table permission: &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;DESCRIBE&lt;/code&gt;.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h4 id="grant-lake-formation-permissions-on-the-cross-cloud-role-one-time."&gt;Grant Lake Formation permissions on the cross-cloud role (one-time).&lt;/h4&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws lakeformation grant-permissions     --principal '{"DataLakePrincipalIdentifier":"arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:role/bigquery-cross-cloud-role"}'     --resource '{"Catalog":{"Id":"&amp;lt;AWS_ACCOUNT_ID&amp;gt;:s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;"}}'     --permissions '["DESCRIBE"]'     --region &amp;lt;AWS_REGION&amp;gt;

aws lakeformation grant-permissions     --principal '{"DataLakePrincipalIdentifier":"arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:role/bigquery-cross-cloud-role"}'     --resource '{"Database":{"CatalogId":"&amp;lt;AWS_ACCOUNT_ID&amp;gt;:s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;","Name":"&amp;lt;NAMESPACE&amp;gt;"}}'     --permissions '["DESCRIBE"]'     --region &amp;lt;AWS_REGION&amp;gt;

aws lakeformation grant-permissions     --principal '{"DataLakePrincipalIdentifier":"arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:role/bigquery-cross-cloud-role"}'     --resource '{"Table":{"CatalogId":"&amp;lt;AWS_ACCOUNT_ID&amp;gt;:s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;","DatabaseName":"&amp;lt;NAMESPACE&amp;gt;","Name":"orders"}}'     --permissions '["SELECT","DESCRIBE"]'     --region &amp;lt;AWS_REGION&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Before granting Lake Formation permissions, revoke the default &lt;code&gt;IAMAllowedPrincipals&lt;/code&gt; access. By default, Lake Formation grants &lt;code&gt;IAMAllowedPrincipals&lt;/code&gt; full access to all databases and tables, so you first need to revoke this to enforce fine grain access. &lt;code&gt;IAMAllowedPrincipals&lt;/code&gt; provides backward compatibility when you start using Lake Formation permissions to secure the Data Catalog resources that were earlier protected by IAM policies for AWS Glue.&lt;/p&gt; 
&lt;h3 id="set-up-lake-formation-for-external-engines"&gt;Set up Lake Formation for external engines&lt;/h3&gt; 
&lt;p&gt;For table metadata to sync from Glue to BigLake/BigQuery, the following Lake Formation settings are required. You might notice that a similar setting also appeared during the S3 Table integration setup. The first one registers the data location and enables external access at the catalog level, while this one enables the Lake Formation credential vending mechanism at the account level for all external engines. For a clean cross-cloud setup, we recommend that you enable both.&lt;/p&gt; 
&lt;p&gt;In the Lake Formation console, choose &lt;strong&gt;Administration&lt;/strong&gt;, then &lt;strong&gt;Application integration settings&lt;/strong&gt;, and then select &lt;strong&gt;Allow external engines to access data in Amazon S3 locations with full table access&lt;/strong&gt;.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-4-1.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-4-1.png" alt="Application integration settings in the Lake Formation console with external-engine access enabled" width="800" height="682"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 4: Enabling external-engine access in Lake Formation application integration settings&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="connecting-bigquery-to-s3-tables"&gt;Connecting BigQuery to S3 Tables&lt;/h2&gt; 
&lt;p&gt;With the AWS side configured, create the federated catalog in Google Cloud that connects BigQuery to the AWS Glue IRC.&lt;/p&gt; 
&lt;h3 id="create-the-federated-catalog"&gt;Create the federated catalog&lt;/h3&gt; 
&lt;p&gt;Authenticate to Google Cloud using &lt;code&gt;gcloud auth login&lt;/code&gt;, or use Cloud Shell, which is pre-authenticated. Verify the BigLake API is enabled:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;gcloud services enable biglake.googleapis.com --project="&amp;lt;GCP_PROJECT_ID&amp;gt;"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h4 id="for-lake-formation-mode-with-credential-vending"&gt;For Lake Formation mode (with credential vending):&lt;/h4&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;gcloud alpha biglake iceberg catalogs create &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" \
    --catalog-type=federated \
    --federated-catalog-type=glue \
    --glue-aws-region=&amp;lt;AWS_REGION&amp;gt; \
    --glue-aws-role-arn=arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/bigquery-cross-cloud-role \
    --glue-warehouse=&amp;lt;ACCOUNT_ID&amp;gt;:s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt; \
    --primary-location=&amp;lt;GCP_REGION&amp;gt; \
    --credential-mode=vended-credentials&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The &lt;code&gt;--glue-warehouse&lt;/code&gt; parameter uses the format &lt;code&gt;&amp;lt;AWS_ACCOUNT_ID&amp;gt;:s3tablescatalog/&amp;lt;TABLE_BUCKET&amp;gt;&lt;/code&gt;. This tells the AWS Glue IRC to scope requests to your specific S3 Tables bucket within the federated catalog hierarchy.&lt;/p&gt; 
&lt;p&gt;The &lt;code&gt;--credential-mode=vended-credentials&lt;/code&gt; flag (Lake Formation mode) instructs BigQuery Lakehouse to request scoped temporary credentials from Lake Formation rather than using the role’s IAM permissions directly for data access.&lt;/p&gt; 
&lt;p&gt;The &lt;code&gt;--primary-location&lt;/code&gt; refers to the Google Cloud region where the federated catalog metadata is stored. Use the &lt;a href="https://docs.cloud.google.com/lakehouse/docs/regions-capabilities-cross-cloud-lakehouse" target="_blank" rel="noopener"&gt;AWS to Google Cloud region mapping&lt;/a&gt; to find the corresponding GCP region for your AWS Region. For example, AWS us-east-1 maps to GCP us-east4.&lt;/p&gt; 
&lt;h3 id="retrieve-the-biglake-service-account-id"&gt;Retrieve the BigLake service account ID&lt;/h3&gt; 
&lt;p&gt;After catalog creation, Google provisions a dedicated service account for your federated catalog. Retrieve its numeric ID:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;BIGLAKE_SA_ID=$(gcloud alpha biglake iceberg catalogs describe &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" \
    --format="value(biglake-service-account-id)")
echo $BIGLAKE_SA_ID&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="update-the-aws-trust-policy"&gt;Update the AWS trust policy&lt;/h3&gt; 
&lt;p&gt;Back on AWS, replace the placeholder in the IAM role’s trust policy with the actual service account ID:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws iam update-assume-role-policy \
    --role-name bigquery-cross-cloud-role \
    --policy-document '{
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": {
          "Federated": "arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/accounts.google.com"
        },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": {
            "accounts.google.com:sub": ["&amp;lt;BIGLAKE_SA_ID&amp;gt;"],
            "accounts.google.com:aud": ["&amp;lt;BIGLAKE_SA_ID&amp;gt;"]
          }
        }
      }]
    }'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Register the service account ID in the OIDC provider’s audience list. Without this step, AWS rejects the token because the &lt;code&gt;aud&lt;/code&gt; claim doesn’t match any registered client:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws iam add-client-id-to-open-id-connect-provider \
    --open-id-connect-provider-arn "arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:oidc-provider/accounts.google.com" \
    --client-id "&amp;lt;BIGLAKE_SA_ID&amp;gt;"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h3 id="set-up-metadata-sync"&gt;Set up metadata sync&lt;/h3&gt; 
&lt;p&gt;Wait 3–5 minutes for IAM changes to propagate globally, then set up background refresh:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;gcloud alpha biglake iceberg catalogs update &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" \
    --refresh-interval=300s&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The &lt;code&gt;--refresh-interval&lt;/code&gt; (300 seconds in this example) determines how often BigQuery syncs metadata from the AWS Glue IRC. New tables and schema changes appear in BigQuery within this interval.&lt;/p&gt; 
&lt;h3 id="querying-from-bigquery"&gt;Querying from BigQuery&lt;/h3&gt; 
&lt;p&gt;After the catalog refresh completes, BigQuery automatically creates external datasets corresponding to the synced namespaces. No manual CREATE SCHEMA is required.&lt;/p&gt; 
&lt;p&gt;Verify the sync:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;gcloud alpha biglake iceberg namespaces list \
    --catalog="&amp;lt;FEDERATED_CATALOG_NAME&amp;gt;" \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Run a query in BigQuery:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * FROM `&amp;lt;GCP_PROJECT_ID&amp;gt;.&amp;lt;FEDERATED_CATALOG_NAME&amp;gt;.&amp;lt;NAMESPACE&amp;gt;.orders` LIMIT 1000&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Sample Query Output:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT
    customer_id,
    COUNT(*) as order_count,
    SUM(amount) as total_spend
FROM `&amp;lt;GCP_PROJECT_ID&amp;gt;.&amp;lt;FEDERATED_CATALOG_NAME&amp;gt;.&amp;lt;NAMESPACE&amp;gt;.orders`
GROUP BY customer_id
ORDER BY total_spend DESC&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-5-1.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P2-5-1.png" alt="BigQuery query results showing order count and total spend per customer from the Amazon S3 Tables data" width="800" height="816"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 5: BigQuery query results returned through Lake Formation credential vending&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;BigQuery reads the Iceberg metadata to identify which Parquet data files contain relevant data. It also applies partition pruning where applicable, and fetches only the necessary files from S3 Tables managed storage.&lt;/p&gt; 
&lt;h2 id="schema-evolution"&gt;Schema evolution&lt;/h2&gt; 
&lt;p&gt;When new columns are added to an Iceberg table on the AWS side (through Spark, Athena, or the AWS Glue IRC), the schema change is captured in Iceberg’s metadata. On the next Lakehouse refresh cycle, BigQuery picks up the new columns automatically. No DDL changes are needed in BigQuery.&lt;/p&gt; 
&lt;h2 id="metadata-freshness"&gt;Metadata freshness&lt;/h2&gt; 
&lt;p&gt;The &lt;code&gt;s3tablescatalog&lt;/code&gt; catalog in AWS Glue is a federated catalog that resolves table metadata live from the S3 Tables service on each request. When a streaming job commits new data to an S3 Table, the latest metadata is immediately available through the AWS Glue IRC. BigQuery sees the update on its next refresh cycle (as configured by &lt;code&gt;--refresh-interval&lt;/code&gt;).&lt;/p&gt; 
&lt;h2 id="oidc-identity-federation"&gt;OIDC identity federation&lt;/h2&gt; 
&lt;p&gt;The trust relationship between Google Cloud and AWS uses OpenID Connect. When BigQuery Lakehouse needs to access your data, it presents a signed JWT token containing:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code&gt;iss&lt;/code&gt;: &lt;code&gt;accounts.google.com&lt;/code&gt; (the issuer)&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;sub&lt;/code&gt;: The BigLake service account ID (identifies which catalog is making the request)&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;aud&lt;/code&gt;: The same service account ID (the intended audience)&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;AWS validates this token against the registered OIDC provider and trust policy conditions before issuing temporary credentials. Each federated catalog receives a unique service account ID, providing per-catalog isolation and auditability through AWS CloudTrail.&lt;/p&gt; 
&lt;h2 id="network-path"&gt;Network path&lt;/h2&gt; 
&lt;p&gt;By default, traffic between BigQuery and AWS travels over the public internet. For workloads requiring private connectivity, Google Cloud supports Cross-Cloud Interconnect or Partner Interconnect. This helps routing queries over a dedicated network path. Refer to the &lt;a href="https://docs.cloud.google.com/lakehouse/docs/set-up-cross-cloud-lakehouse-aws-glue" target="_blank" rel="noopener"&gt;Google Cloud documentation&lt;/a&gt; for private interconnect configuration.&lt;/p&gt; 
&lt;h2 id="clean-up"&gt;Clean up&lt;/h2&gt; 
&lt;p&gt;To avoid ongoing charges, remove the resources created in this walkthrough.&lt;/p&gt; 
&lt;p&gt;On AWS:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# Delete the table (if created for this walkthrough)
aws s3tables delete-table \
    --table-bucket-arn "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;" \
    --namespace analytics --name orders --region &amp;lt;AWS_REGION&amp;gt;

# Delete namespace and table bucket
aws s3tables delete-namespace \
    --table-bucket-arn "arn:aws:s3tables:&amp;lt;AWS_REGION&amp;gt;:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:bucket/&amp;lt;TABLE_BUCKET&amp;gt;" \
    --namespace &amp;lt;NAMESPACE&amp;gt; --region &amp;lt;AWS_REGION&amp;gt;

aws s3tables delete-table-bucket --name &amp;lt;TABLE_BUCKET&amp;gt; --region &amp;lt;AWS_REGION&amp;gt;

# Delete IAM role and OIDC provider (if no longer needed)
aws iam delete-role --role-name bigquery-cross-cloud-role&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;On Google Cloud:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;gcloud alpha biglake iceberg catalogs delete &amp;lt;FEDERATED_CATALOG_NAME&amp;gt; \
    --project="&amp;lt;GCP_PROJECT_ID&amp;gt;" --location=&amp;lt;GCP_REGION&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;This post demonstrated how to query Amazon S3 Tables from Google BigQuery using AWS Lake Formation credential vending, where Lake Formation manages the permissions and issues temporary, scoped credentials for data access. With the open Iceberg format, you can write data once on AWS and read it from supported engines that speak Iceberg, including BigQuery.&lt;/p&gt; 
&lt;p&gt;Together with the IAM approach covered in &lt;a href="https://aws.amazon.com/blogs/big-data/enable-cross-cloud-analytics-with-amazon-s3-tables-and-google-bigquery-part-1-iam-based-access-control/" target="_blank" rel="noopener"&gt;Part 1&lt;/a&gt;, two access control modes provide flexibility: IAM for teams who want a straightforward setup and Lake Formation for organizations with complex governance requirements where multiple engines need centrally managed access to the same data.&lt;/p&gt; 
&lt;p&gt;To get started with this pattern in your environment:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Review the &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables.html" target="_blank" rel="noopener"&gt;Amazon S3 Tables documentation&lt;/a&gt; for table bucket setup.&lt;/li&gt; 
 &lt;li&gt;Follow the &lt;a href="https://docs.cloud.google.com/lakehouse/docs/set-up-cross-cloud-lakehouse-aws-glue" target="_blank" rel="noopener"&gt;Google Cloud cross-cloud Lakehouse setup guide&lt;/a&gt; for the BigQuery federation configuration.&lt;/li&gt; 
 &lt;li&gt;Explore the &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/iceberg-rest-apis.html" target="_blank" rel="noopener"&gt;AWS Glue Iceberg REST Catalog APIs&lt;/a&gt; for programmatic access patterns.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p style="clear: both"&gt;&lt;/p&gt; 
&lt;hr style="width: 100%"&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-4-1.jpeg" alt="Lakshmi Nair" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Lakshmi Nair&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/lakshmi--nair/" target="_blank" rel="noopener"&gt;Lakshmi&lt;/a&gt; is a Principal Analytics Specialist Solutions Architect at AWS. She specializes in designing advanced analytics systems across industries. She focuses on crafting cloud-based data platforms, enabling real-time streaming, big data processing, and robust data governance.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-6059P1-5-1.png" alt="Srividya Parthasarathy" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Srividya Parthasarathy&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/srividya-parthasarathy-8b71bb32/" target="_blank" rel="noopener"&gt;Srividya&lt;/a&gt; was a Senior Big Data Architect on the AWS Lake Formation team. She works with product team and customer to build robust features and solutions for their analytical data platform. She enjoys building data mesh solutions and sharing them with the community.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>GPU-accelerated Apache Spark with Amazon EMR and NVIDIA RTX PRO 4500 on Amazon EC2 G7 instances runs up to 3.7x faster</title>
		<link>https://aws.amazon.com/blogs/big-data/gpu-accelerated-apache-spark-with-amazon-emr-and-nvidia-rtx-pro-4500-on-amazon-ec2-g7-instances-runs-up-to-3-7x-faster/</link>
		
		<dc:creator><![CDATA[McCall Peltier]]></dc:creator>
		<pubDate>Tue, 25 Aug 2026 16:17:45 +0000</pubDate>
				<category><![CDATA[Amazon EC2]]></category>
		<category><![CDATA[Amazon EMR]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Intermediate (200)]]></category>
		<guid isPermaLink="false">2c8ccc739be91eff44d0e3d625fb16abf97ecfd5</guid>

					<description>Amazon EMR on EKS now runs Apache Spark up to 3.7x faster on Amazon EC2 G7 instances with NVIDIA RTX PRO 4500 Blackwell GPUs than on comparable CPU instances, with no changes to existing Spark code. See the TPC-DS benchmark results, the cost comparison, and how to get started.</description>
										<content:encoded>&lt;p&gt;For years, Apache Spark has been the backbone of large-scale data processing. However, as datasets grow and artificial intelligence and machine learning (AI/ML) pipelines become more complex, modern workloads demand more computational power. Feature engineering for machine learning models, large-scale extract, transform, and load (ETL) transformations, and real-time analytics workloads are computationally intensive by nature. GPU-accelerated instances improve performance and transform jobs that once took hours into minutes, so you can iterate on models faster and reduce operational costs. You can process larger datasets in single batches, make decisions in real time, and achieve strong performance without over-provisioning infrastructure.&lt;/p&gt; 
&lt;p&gt;We’re excited to share the benchmarking results on Amazon EMR with &lt;a href="https://aws.amazon.com/ec2/instance-types/g7/" target="_blank" rel="noopener"&gt;Amazon Elastic Compute Cloud (Amazon EC2) G7 instances&lt;/a&gt;, powered by NVIDIA RTX PRO 4500 Blackwell Server Edition GPUs. For data engineers and data scientists running Apache Spark workloads, this means faster pipelines, shorter iteration cycles, and more time spent on insights.&lt;/p&gt; 
&lt;p&gt;Amazon EMR on EKS natively supports the NVIDIA cuDF plugin for Apache Spark. This support is the result of joint engineering between AWS and NVIDIA to qualify the cuDF plugin for Amazon EMR, co-optimize Spark execution paths for RTX PRO 4500, and validate performance at scale through shared TPC-DS benchmarking on Amazon EC2 G7 instances. Now, Apache Spark workloads on Amazon EMR on EKS run up to 3.7x faster with Amazon EC2 G7 GPU instances than with comparable CPU instances, and require no changes to existing Spark code.&lt;/p&gt; 
&lt;p&gt;In the TPC-DS 3 TB benchmark, at the 64 GB memory tier, EC2 G7 instances with RTX PRO finished in 4.7 minutes. If you run large-scale data processing pipelines, you can cut job run times by more than two-thirds while maintaining full compatibility with the applications you already have in production.&lt;/p&gt; 
&lt;p&gt;The use cases that benefit most are those where speed directly unlocks business value. In AI/ML feature engineering, faster Spark jobs mean data science teams can iterate on features more quickly, reducing the time from raw data to trained model. In complex ETL pipelines, like financial transactions, clickstream aggregation, or supply chain data consolidation, GPU acceleration compresses multi-hour batch windows into near-real-time processing. For real-time analytics, teams running fraud detection, personalization engines, or operational dashboards can process larger volumes of data within tighter latency windows, without redesigning their architecture.&lt;/p&gt; 
&lt;p&gt;Beyond data analytics, the G7 instances will support a broad range of AI and graphics workloads, including conversational AI, content generation, recommender systems, and video streaming and rendering. Built on the AWS Nitro System, they deliver the security and resource efficiency that production AI, analytics, and graphics workloads demand.&lt;/p&gt; 
&lt;p&gt;The following sections walk through the cluster configuration, benchmark methodology, and performance results.&lt;/p&gt; 
&lt;h2 id="cluster-configuration"&gt;Cluster configuration&lt;/h2&gt; 
&lt;p&gt;We benchmarked four instance types to measure the real-world performance of G7 GPU instances against comparable CPU instances for Spark SQL performance. The g7.4xlarge also provides 80 Gbps network bandwidth (compared to 15–17 Gbps on the CPU baselines) and uses RapidsShuffleManager. However, CPU runs showed no evidence of being network- or shuffle-bound at this cluster scale. All tests used Amazon EMR on EKS 7.12.0 with Apache Spark 3.5.6 and cuDF plugin 26.04.2, running the full &lt;a href="https://www.tpc.org/tpc_documents_current_versions/pdf/tpc-ds_v2.4.0.pdf" target="_blank" rel="noopener"&gt;TPC-DS&lt;/a&gt; benchmark at 3 TB scale across 103 queries. Each experiment ran 5 iterations. We report the median. Data was stored as Parquet on Amazon Simple Storage Service (Amazon S3) (same-region gateway endpoint). All instances were launched in a single Availability Zone.&lt;/p&gt; 
&lt;h3 id="instance-specifications"&gt;Instance specifications&lt;/h3&gt; 
&lt;p&gt;All four instance types share the same compute footprint of 16 vCPUs and 64 GB system RAM. The g7.4xlarge additionally includes an NVIDIA RTX PRO 4500 Blackwell GPU with 32 GB of dedicated video memory (VRAM), which the cuDF plugin uses to accelerate Spark SQL operations. The baseline for all speedup and cost comparisons is m9gd.4xlarge (Graviton), the lowest-cost CPU instance in the group.&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;span style="color: #ffffff"&gt;.&lt;/span&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;g7.4xlarge&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;m9gd.4xlarge&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;m8id.4xlarge&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;m8a.4xlarge&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;x86_64&lt;/td&gt; 
   &lt;td&gt;arm64 (Graviton)&lt;/td&gt; 
   &lt;td&gt;x86_64&lt;/td&gt; 
   &lt;td&gt;x86_64&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;vCPU&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;16&lt;/td&gt; 
   &lt;td&gt;16&lt;/td&gt; 
   &lt;td&gt;16&lt;/td&gt; 
   &lt;td&gt;16&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;RAM&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;64 GB&lt;/td&gt; 
   &lt;td&gt;64 GB&lt;/td&gt; 
   &lt;td&gt;64 GB&lt;/td&gt; 
   &lt;td&gt;64 GB&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;GPU&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;1× RTX PRO 4500 Blackwell (32 GB VRAM)&lt;/td&gt; 
   &lt;td&gt;–&lt;/td&gt; 
   &lt;td&gt;–&lt;/td&gt; 
   &lt;td&gt;–&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;NVMe&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;875 GB&lt;/td&gt; 
   &lt;td&gt;950 GB&lt;/td&gt; 
   &lt;td&gt;950 GB&lt;/td&gt; 
   &lt;td&gt;EBS only (GP3 16k IOPS and 2000 MB/s throughput to match NVMe&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Network&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;80 Gbps&lt;/td&gt; 
   &lt;td&gt;Up to 17 Gbps&lt;/td&gt; 
   &lt;td&gt;Up to 15 Gbps&lt;/td&gt; 
   &lt;td&gt;Up to 15 Gbps&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The g7.4xlarge uses the RTX PRO 4500 Blackwell Server Edition GPU. The CPU baselines cover all three major architectures: m8id.4xlarge (Intel x86), m8a.4xlarge (AMD x86), and m9gd.4xlarge (Graviton arm64).&lt;/p&gt; 
&lt;h3 id="spark-configuration"&gt;Spark configuration&lt;/h3&gt; 
&lt;p&gt;All instances used eight executor nodes with the following configuration:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;GPU instances&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;CPU instances&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Amazon EMR release&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;emr-7.12.0-spark-rapids-latest&lt;/td&gt; 
   &lt;td&gt;emr-7.12.0-latest&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;executor.cores&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;14&lt;/td&gt; 
   &lt;td&gt;14&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;executor.instances&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;8&lt;/td&gt; 
   &lt;td&gt;8&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;executor.memory&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;20G&lt;/td&gt; 
   &lt;td&gt;20G&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;executor.memoryOverhead&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;30G&lt;/td&gt; 
   &lt;td&gt;30G&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;spark.plugins&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;com.nvidia.spark.SQLPlugin&lt;/td&gt; 
   &lt;td&gt;–&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;rapids.memory.pinnedPool.size&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;8G&lt;/td&gt; 
   &lt;td&gt;–&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;rapids.sql.concurrentGpuTasks&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;3&lt;/td&gt; 
   &lt;td&gt;–&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;shuffle.manager&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;RapidsShuffleManager&lt;/td&gt; 
   &lt;td&gt;default (sort)&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;sql.adaptive.enabled&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;true&lt;/td&gt; 
   &lt;td&gt;true&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;io.compression.codec&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;zstd&lt;/td&gt; 
   &lt;td&gt;zstd&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;CPU instances use the same 30 GB memoryOverhead as GPU to make sure that the memory comparison is apples-to-apples. This setting reserves off-heap memory for shuffle and caching on both sides.&lt;/p&gt; 
&lt;p&gt;For GPU instances, the cuDF plugin offloads Spark SQL operations to the GPU automatically. No code changes are required. The &lt;code&gt;executor.memoryOverhead&lt;/code&gt; value is set higher on GPU instances to accommodate GPU memory management and the RAPIDS shuffle manager.&lt;/p&gt; 
&lt;p&gt;The cuDF plugin automatically falls back to CPU execution for unsupported operators and user-defined functions (UDFs). Your job still completes, but those stages run without GPU acceleration. To identify which operations run on GPU compared to CPU, set &lt;code&gt;spark.rapids.sql.explain=NOT_ON_GPU&lt;/code&gt; in your Spark configuration. For a pre-migration assessment of your workloads, use the &lt;a href="https://docs.nvidia.com/spark-rapids/user-guide/latest/qualification/overview.html" target="_blank" rel="noopener"&gt;NVIDIA cuDF tool&lt;/a&gt; to estimate GPU acceleration potential before moving to G7 instances.&lt;/p&gt; 
&lt;p&gt;To tune settings like &lt;code&gt;concurrentGpuTasks&lt;/code&gt; and &lt;code&gt;pinnedPool.size&lt;/code&gt;, use the Spark History Server on Amazon EMR on EKS, which provides per-stage execution details to identify CPU fallback and shuffle bottlenecks.&lt;/p&gt; 
&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt; 
&lt;p&gt;Reference the &lt;a href="https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/tutorial-spark-rapids.html" target="_blank" rel="noopener"&gt;Using cuDF Accelerator for Apache Spark with Amazon EMR on EKS&lt;/a&gt; for detailed setup instructions.&lt;/p&gt; 
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt; 
&lt;p&gt;Before running GPU-accelerated Spark on Amazon EMR on EKS, make sure the following are in place:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon EMR on EKS&lt;/strong&gt; release version 6.9.0 or later (this post uses &lt;code&gt;emr-7.12.0-spark-rapids-latest&lt;/code&gt;).&lt;/li&gt; 
&lt;/ul&gt; 
&lt;blockquote&gt;
 &lt;p&gt;The &lt;code&gt;-spark-rapids&lt;/code&gt; release variant ships the NVIDIA cuDF plugin pre-installed.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon Elastic Kubernetes Service (Amazon EKS) cluster&lt;/strong&gt; with a GPU-enabled node group using G7 instances.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Node AMI:&lt;/strong&gt; &lt;code&gt;AL2023_x86_64_NVIDIA&lt;/code&gt; (Amazon EKS optimized accelerated AMI).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;NVIDIA device plugin&lt;/strong&gt; installed in the cluster to expose GPUs to Kubernetes pods: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-bash"&gt;kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.9.0/nvidia-device-plugin.yml&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Amazon EMR on EKS virtual cluster&lt;/strong&gt; registered to the EKS namespace.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;To validate GPU availability on your nodes:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;kubectl get nodes "-o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu"&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Getting started with GPU-accelerated Spark on Amazon EMR is straightforward. To use the latest cuDF plugin, overlay the latest version (for example, &lt;a href="https://mvnrepository.com/artifact/com.nvidia/rapids-4-spark_2.13/26.04.2" target="_blank" rel="noopener"&gt;26.04.2&lt;/a&gt; as of May 2026) onto the Amazon EMR RAPIDS image using an &lt;a href="https://github.com/kubeflow/spark-operator/blob/f56ba30d5c36feeaaba5c89ddd48a3f663060f0d/docs/api-docs.md?plain=1#L3118" target="_blank" rel="noopener"&gt;initContainer&lt;/a&gt; technique. This replaces the bundled cuDF JAR&amp;nbsp; with a newer version while preserving all other Amazon EMR dependencies. We recommend using the latest Amazon EMR release to get the most up-to-date cuDF plugin for better performance. In our benchmarks, upgrading from cuDF plugin 25.08.0 to 26.04.2 reduced runtime by 36–38 percent. Download the latest cuDF plugin JAR from the &lt;a href="https://nvidia.github.io/cudf-spark/docs/download.html" target="_blank" rel="noopener"&gt;NVIDIA repository&lt;/a&gt;. AWS Support covers Amazon EMR. For issues specific to a cuDF JAR, file a GitHub issue or contact NVIDIA at &lt;a class="email" href="mailto:cudf-spark-support@nvidia.com"&gt;cudf-spark-support@nvidia.com&lt;/a&gt;.&lt;/p&gt; 
&lt;h3 id="performance-benchmarks-and-cost-efficiency"&gt;Performance benchmarks and cost efficiency&lt;/h3&gt; 
&lt;p&gt;We ran the full TPC-DS benchmark suite (103 queries) at 3 TB scale on 8-node clusters in us-east-1. The following table summarizes the results:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;span style="color: #ffffff"&gt;.&lt;/span&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;GPU instances&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;CPU instances&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Cost per run&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;$2.06&lt;/td&gt; 
   &lt;td&gt;$2.93–$3.18&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Total time (103 queries)&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;281s (4.7 min)&lt;/td&gt; 
   &lt;td&gt;1,010–1,043s (16.8–17.4 min)&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Speedup compared to CPU instances&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;3.7×&lt;/td&gt; 
   &lt;td&gt;baseline&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;&lt;em&gt;Cost per run&lt;/em&gt; is the total cluster cost for the benchmark’s duration: Cluster $/hr × (median runtime ÷ 3,600). The hourly rate combines the EC2 On-Demand cost for all 8 nodes and the Amazon EMR on EKS charge for the vCPU and memory the Spark pods consume. Both are billed per second (one-minute minimum), so you pay only for what a job uses while it runs. All runs used Amazon EMR on EKS 7.12.0 in us-east-1, with 8 × 4xlarge nodes (128 vCPU) on both the GPU and CPU sides. The g7.4xlarge cluster runs at $26.35/hr (8 × $3.042 EC2 = $24.34, plus $2.01 for Amazon EMR on EKS) and finishes in 281 seconds, at $2.06 per run. The CPU clusters run at a lower hourly rate ($10.43–$10.99) but take 1,010–1,043 seconds, landing at $2.93–$3.18 per run. All prices reflect On-Demand pricing in us-east-1 as of May 2026. G7 instances are also eligible for EC2 Spot and Compute Savings Plans, which can further reduce costs for recurring batch workloads.&lt;/p&gt; 
&lt;p&gt;Cost-per-run calculations include EC2 and Amazon EMR charges only. They exclude the EKS control-plane fee, EBS volumes, S3 request and storage costs, and the driver pod.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-5875-1-1.png" alt="Bar chart of total TPC-DS runtime by instance type, showing g7.4xlarge finishing far faster than the CPU instances" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Total runtime by instance type for all 103 TPC-DS queries at 3 TB scale. The g7.4xlarge with GPU acceleration completed the benchmark in 4.7 minutes, 3.7× faster than CPU instances (16.8-17.4 minutes)&lt;/p&gt;
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-5875-2-1.png" alt="Bar chart of total cost per benchmark run by instance type, showing the g7.4xlarge GPU instance costing less than the CPU instances" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Total cost per benchmark run, including both Amazon EC2 instance and Amazon EMR on EKS cost across all 8 nodes. Despite a ~2.5× higher hourly rate, the g7.4xlarge GPU instance costs up to 31% less per run than Graviton because it finishes the workload 3.7× faster&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="where-gpu-acceleration-excels"&gt;Where GPU acceleration excels&lt;/h3&gt; 
&lt;p&gt;GPU acceleration completed the 103-query power run in 281s compared to 1,032s on CPU, an overall 3.7× speedup that saves 750 seconds per run. GPU was faster on 102 of 103 query executions.&lt;/p&gt; 
&lt;p&gt;GPU acceleration delivers the largest gains on the long-running, compute- and shuffle-heavy queries where kernel throughput outweighs launch overhead. The biggest absolute time savings:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Query&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;CPU time&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;GPU time&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Speedup&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Time saved&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;q24 (part 1+2)&lt;/td&gt; 
   &lt;td&gt;81.6s&lt;/td&gt; 
   &lt;td&gt;15.9s&lt;/td&gt; 
   &lt;td&gt;~5.1×&lt;/td&gt; 
   &lt;td&gt;65.7s&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;q23 (part 1+2)&lt;/td&gt; 
   &lt;td&gt;79.4s&lt;/td&gt; 
   &lt;td&gt;16.4s&lt;/td&gt; 
   &lt;td&gt;~4.9×&lt;/td&gt; 
   &lt;td&gt;63.1s&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;q93&lt;/td&gt; 
   &lt;td&gt;63.7s&lt;/td&gt; 
   &lt;td&gt;5.6s&lt;/td&gt; 
   &lt;td&gt;11.4×&lt;/td&gt; 
   &lt;td&gt;58.1s&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;q76&lt;/td&gt; 
   &lt;td&gt;30.4s&lt;/td&gt; 
   &lt;td&gt;3.4s&lt;/td&gt; 
   &lt;td&gt;9.0×&lt;/td&gt; 
   &lt;td&gt;27.0s&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;q64&lt;/td&gt; 
   &lt;td&gt;35.4s&lt;/td&gt; 
   &lt;td&gt;8.5s&lt;/td&gt; 
   &lt;td&gt;4.2×&lt;/td&gt; 
   &lt;td&gt;26.9s&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;q50&lt;/td&gt; 
   &lt;td&gt;27.6s&lt;/td&gt; 
   &lt;td&gt;3.5s&lt;/td&gt; 
   &lt;td&gt;7.9×&lt;/td&gt; 
   &lt;td&gt;24.0s&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;Speedup distribution across all 103 executions:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Speedup band&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Queries&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;≥5×&lt;/td&gt; 
   &lt;td&gt;15&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;4–5×&lt;/td&gt; 
   &lt;td&gt;13&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;3–4×&lt;/td&gt; 
   &lt;td&gt;21&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;2–3×&lt;/td&gt; 
   &lt;td&gt;26&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;1–2×&lt;/td&gt; 
   &lt;td&gt;27&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&amp;lt;1× (CPU faster)&lt;/td&gt; 
   &lt;td&gt;1&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;Median per-query speedup 2.94× (geomean 2.84×). The heaviest wins (q50, q76, q93) are aggregation- and shuffle-join-intensive queries that convert cleanly to GpuHashAggregate and GpuBroadcastHashJoin.&lt;/p&gt; 
&lt;h3 id="where-cpu-wins"&gt;Where CPU wins&lt;/h3&gt; 
&lt;p&gt;With RAPIDS 26.04.2, the following query showcases a workload pattern where CPU was faster:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Query&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;CPU time&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;GPU time&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Ratio&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Root cause&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;q16&lt;/td&gt; 
   &lt;td&gt;0.96s&lt;/td&gt; 
   &lt;td&gt;1.44s&lt;/td&gt; 
   &lt;td&gt;CPU 1.5x faster&lt;/td&gt; 
   &lt;td&gt;Trivial/near-empty scan. Sub-second runtime where GPU kernel-launch overhead is not amortized&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;h2 id="choosing-the-right-instance"&gt;Choosing the right instance&lt;/h2&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Instance&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;g7.4xlarge (RTX PRO GPU)&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;Fastest and most cost-effective&lt;/td&gt; 
   &lt;td&gt;Up to 3.7× faster than comparable CPU instances and up to 31% cheaper per run. Completes in 4.7 min compared to 17.2 min. Best choice for both speed and cost efficiency.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;CPU instances (m8a / m8id / m9gd)&lt;/td&gt; 
   &lt;td&gt;Flexibility, availability, and always-on workloads&lt;/td&gt; 
   &lt;td&gt;Multiple architecture options deliver similar Spark SQL performance. Choose CPU when GPUs are unavailable, when clusters need to remain running continuously (for example, overnight jobs ready for next-day analysis), or when workloads cannot use GPU acceleration. CPU instances offer broad availability and predictable capacity without startup delays.&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;G7 instances require a G-instance vCPU service quota in your account (default is often 0 for GPU types). Request a quota increase through the Service Quotas console, or use On-Demand Capacity Reservations (ODCRs) to guarantee availability for recurring batch jobs.&lt;/p&gt; 
&lt;p&gt;Based on these benchmark results, consider evaluating GPU acceleration for your own Apache Spark workloads. Start by identifying compute-intensive operations in your current pipelines, particularly those involving large-scale aggregations, joins, or machine learning feature engineering that could benefit from the performance improvements demonstrated here.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Amazon EMR on EKS with NVIDIA RTX PRO 4500 together provide a meaningful step forward for teams running data-intensive Spark workloads at scale. Whether you’re building ML pipelines that demand rapid feature iteration, running complex ETL transformations across massive datasets, or powering real-time analytics that can’t afford to wait on slow batch jobs, GPU-accelerated Spark on G7 delivers the performance and speed to do more. As data and AI workloads continue to evolve, GPU-accelerated analytics on Amazon EMR is becoming the foundation for data teams. Get started with GPU-accelerated Spark on Amazon EMR on EKS today by visiting &lt;a href="https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/tutorial-spark-rapids.html" target="_blank" rel="noopener"&gt;Amazon EMR documentation&lt;/a&gt; to launch your first G7-powered cluster and see the performance gains for yourself.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-5875-3-1.png" alt="McCall Peltier" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;McCall Peltier&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/mccallpeltier/" target="_blank" rel="noopener"&gt;McCall&lt;/a&gt; is a Senior Product Marketing Manager at AWS focused on data processing services, including Amazon EMR. She leads messaging and launches that support customers building modern data platforms on AWS, collaborating across product and field teams to drive adoption and customer impact.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-5875-4.png" alt="Karthik Prabhakar" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Karthik Prabhakar&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/karthik-prabhakar" target="_blank" rel="noopener"&gt;Karthik&lt;/a&gt; is a Data Processing Engines Architect for Amazon EMR at Amazon Web Services (AWS). He specializes in distributed systems architecture and query optimization, working with customers to solve complex performance challenges in large-scale data processing workloads. His focus spans engine internals, cost-optimization strategies, and architectural patterns that enable customers to run petabyte-scale analytics efficiently&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/BDB-5875-5-1.jpg" alt="Kshitija Dound" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Kshitija Dound&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/kshitijadound/" target="_blank" rel="noopener"&gt;Kshitija&lt;/a&gt; is a Specialist Solutions Architect at AWS based in New York City, focusing on data and AI. She collaborates with customers to transform their ideas into cloud solutions, using AWS Big Data and AI services. She also engages in public speaking opportunities, sharing her expertise on cloud technologies, industry trends, and career in the cloud. In her spare time, Kshitija enjoys exploring museums, indulging in art, and embracing NYC’s outdoor scene.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="size-full wp-image-29797 alignleft" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2025/09/13/image-5-4.png" alt="Kinshuk Paharae" width="120" height="160"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Kinshuk Pahare&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/kinshukpahare/" target="_blank" rel="noopener"&gt;Kinshuk&lt;/a&gt; is head of product for data processing, leading product teams for AWS Glue, Amazon EMR, and Amazon Athena. He has been with AWS for over 6 years.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Introducing AWS Glue 6.0 for faster and more cost-effective data integration</title>
		<link>https://aws.amazon.com/blogs/big-data/introducing-aws-glue-6-0-for-apache-spark/</link>
		
		<dc:creator><![CDATA[Aarthi Srinivasan]]></dc:creator>
		<pubDate>Mon, 24 Aug 2026 19:06:39 +0000</pubDate>
				<category><![CDATA[Advanced (300)]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Glue]]></category>
		<guid isPermaLink="false">6e2e94ef9817fdbc98c934aa3fa0902000d8b2a1</guid>

					<description>AWS Glue 6.0 is now available, lowering AWS Glue pricing by 30%, adding an AWS optimized build of Apache Spark 4.1, and introducing Apache Iceberg V3 capabilities suitable for enterprise adoption. This post covers the key capabilities and performance benefits, with code examples to help you get started.</description>
										<content:encoded>&lt;p&gt;Organizations running large data processing pipelines want lower costs, faster job runtimes, and dependable support for open table formats, without adding operational overhead. &lt;a href="https://aws.amazon.com/glue/" target="_blank" rel="noopener"&gt;AWS Glue&lt;/a&gt;, a serverless, scalable data integration service that you can use to discover, prepare, move, and integrate data from multiple sources, has now launched AWS Glue 6.0, the new version of AWS Glue that addresses these needs. This version upgrade lowers &lt;a href="https://aws.amazon.com/glue/pricing/" target="_blank" rel="noopener"&gt;AWS Glue pricing&lt;/a&gt; by 30% and improves performance with AWS optimized Apache Spark 4.1. It also augments developer experience with new features and adds support for &lt;a href="https://iceberg.apache.org/spec/#version-3-extended-types-and-capabilities" target="_blank" rel="noopener"&gt;Apache Iceberg V3 specifications&lt;/a&gt; that are suitable for enterprise adoption. The newly available AWS Glue 6.0 makes data processing workloads more manageable, faster to run, and easier to operate.&lt;/p&gt; 
&lt;p&gt;In this post, we cover the key capabilities of &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/08/aws-glue-6-0-price-reduction-iceberg-v3/" target="_blank" rel="noopener"&gt;AWS Glue 6.0&lt;/a&gt; and their performance benefits. We share code examples to help you take full advantage of the release, and we show you how to get started.&lt;/p&gt; 
&lt;h2 id="aws-glue-6.0-highlights"&gt;AWS Glue 6.0 highlights&lt;/h2&gt; 
&lt;p&gt;AWS Glue 6.0 brings together four major improvements designed to transform how you build and run data integration workloads.&lt;/p&gt; 
&lt;p&gt;First, it upgrades the &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/release-notes.html" target="_blank" rel="noopener"&gt;underlying runtime&lt;/a&gt; to Apache Spark 4.1.1, Python 3.13, Scala 2.13, and &lt;a href="https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/home.html" target="_blank" rel="noopener"&gt;AWS SDK for Java 2.x&lt;/a&gt;, delivering performance improvements that can help with faster job completion times and lower costs.&lt;/p&gt; 
&lt;p&gt;Second, this release reduces current &lt;a href="https://aws.amazon.com/glue/pricing/" target="_blank" rel="noopener"&gt;AWS Glue usage rate&lt;/a&gt; by 30%, and when combined with the performance improvements, you may realize an even lower effective cost.&lt;/p&gt; 
&lt;p&gt;Third, AWS Glue 6.0 introduces support for more capabilities of &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/release-notes.html#release-notes-versions" target="_blank" rel="noopener"&gt;Apache Iceberg V3.&lt;/a&gt; This includes the VARIANT data type with automatic shredding, deletion vectors, row lineage tracking, nanosecond timestamps, and geo types. With these capabilities, you can build modern lakehouse architectures on the latest open table format standards.&lt;/p&gt; 
&lt;p&gt;Finally, new features like Spark Declarative Pipelines, Real-Time Mode for streaming and Python virtual environments with S3 caching are designed to further improve performance and developer experience. The following sections dive deeper into each of these areas.&lt;/p&gt; 
&lt;h3 id="runtime-upgrades"&gt;Runtime upgrades&lt;/h3&gt; 
&lt;p&gt;AWS Glue 6.0 upgrades the core runtime stack across the board, bringing newer versions of Apache Spark, Python, Scala, and the AWS SDK to your serverless data integration workloads.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://spark.apache.org/docs/4.1.1/" target="_blank" rel="noopener"&gt;Apache Spark 4.1.1&lt;/a&gt; – AWS Glue 6.0 runs an AWS optimized build of Apache Spark 4.1.1, a major generational leap from Spark 3.5 on AWS Glue 5.1. This release introduces improvements focused on intent-driven data engineering, real-time streaming with sub-second latencies down to single-digit milliseconds for stateless tasks, faster PySpark performance, and expanded SQL features.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://devdocs.io/python~3.13/" target="_blank" rel="noopener"&gt;Python 3.13&lt;/a&gt; – Supports Python 3.13, a stable release that brings interpreter changes, Python data model enhancements, standard library updates, and security updates.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://scala-lang.org/files/archive/spec/2.13/" target="_blank" rel="noopener"&gt;Scala 2.13&lt;/a&gt; – Upgrades to Scala 2.13 which includes a collections library overhaul, language and syntax feature changes, standard library additions, and compiler performance updates.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3 id="reduced-pricing"&gt;Reduced Pricing&lt;/h3&gt; 
&lt;p&gt;AWS Glue 6.0 cuts current AWS Glue pricing by 30%. This means every job you run on AWS Glue 6.0 costs 30% less per DPU-hour compared to AWS Glue 5.1, with no changes required to your workload configuration. When you combine this pricing reduction with the performance improvements delivered by runtime upgrades, your effective cost savings can compound because jobs can complete faster and consume fewer DPU-hours on a lower price point. If you run large-scale Extract, Transform, and Load (ETL) pipelines or recurring batch workloads, this compounding effect can help reduce your monthly spend.&lt;/p&gt; 
&lt;p&gt;To quantify the comparison, we ran the industry-standard TPC-DS benchmark at 3 TB scale on Parquet data stored in &lt;a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener"&gt;Amazon Simple Storage Service (Amazon S3)&lt;/a&gt;, using 30 G.2X workers on AWS Glue. The following table compares the results we obtained in our tests for AWS Glue 6.0 and AWS Glue 5.1. Thus, based on TPC-DS benchmark at 3 TB scale, AWS Glue 6.0 delivers up to 36% better price performance than AWS Glue 5.1.&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;span style="color: #ffffff"&gt;.&lt;/span&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;AWS Glue 6.0&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;AWS Glue 5.1&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Estimated Cost ($)&lt;/td&gt; 
   &lt;td&gt;USD 5.61&lt;/td&gt; 
   &lt;td&gt;USD 8.87&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;&lt;em&gt;Table 1: 3TB TPC-DS benchmark comparison between AWS Glue 6.0 and AWS Glue 5.1&lt;/em&gt;&lt;/p&gt; 
&lt;h3 id="updated-open-table-format-otf-support"&gt;Updated Open Table Format (OTF) support&lt;/h3&gt; 
&lt;p&gt;AWS Glue 6.0 ships with updated versions of all three major open table formats – Iceberg 1.11.0, Hudi 1.1.1, and Delta Lake 4.2.0 – providing better performance, improved merge-on-read capabilities, streamlined concurrency control, and expanded SQL compatibility.&lt;/p&gt; 
&lt;p&gt;Besides supporting the latest open table format versions, AWS Glue 6.0 delivers Apache Iceberg V3 specification that is suitable for enterprise use. The highlight is Variant shredding, which AWS Glue uses to automatically decompose semi-structured data into physically optimized, columnar sub-fields, which should result in faster query read performance. Combined with deletion vectors for efficient row-level updates, UNKNOWN column types, default column values, and richer data type support, AWS Glue 6.0 is designed to make your open data lakes faster, more flexible, and more cost-efficient. AWS Glue 6.0 also adds support for geospatial data types (Geometry and Geography) and nanosecond-precision timestamps from the Apache Iceberg V3 specification, neither of which are currently supported in open-source Apache Spark 4.1. Additional features like row lineage tracking round out the Apache Iceberg V3 capabilities available on AWS Glue 6.0.&lt;/p&gt; 
&lt;p&gt;In the following sections, we illustrate select capabilities from Apache Iceberg V3 specification on AWS Glue 6.0.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;VARIANT column type&lt;/strong&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;blockquote&gt;
 &lt;p&gt;Apache Iceberg V3 introduces the Variant type to store semi-structured data (think JSON, XML, logs, and deeply nested event data) in a compact binary format. Variant shredding is designed to automatically decompose VARIANT columns into physically optimized, columnar sub-fields, facilitating predicate pushdowns and reducing scan overhead. It aims to provide simpler management of semi-structured data, without the need for complex flattening logic. With Variant type, you get the flexibility of embedding a JSON data type in your table columns while shredding is designed to help accelerate read queries and reduce costs.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;ol start="2" type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;UNKNOWN column type&lt;/strong&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;blockquote&gt;
 &lt;p&gt;The UNKNOWN type in Apache Iceberg V3 acts as a flexible placeholder for columns where the data type is not yet determined at the time of table creation or data ingestion. Tables can accept all-null data initially, and the column type can be upgraded later without breaking ingestion pipelines or consuming applications. This can simplify schema evolution for rapidly changing data sources. Apache Iceberg V3’s UNKNOWN column type maps to Spark 4.1’s VOID type.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;ol start="3" type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;DEFAULT column values&lt;/strong&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;blockquote&gt;
 &lt;p&gt;Apache Iceberg V3’s DEFAULT column values allow specifying a default value for a column in the table metadata. When you add a new column, the query engine is designed to automatically apply this default to older rows, without rewriting data or running manual backfill operations.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;The following code demonstrates creating an Apache Iceberg V3 table that uses VARIANT and UNKNOWN types, and DEFAULT values for a column.&lt;/p&gt; 
&lt;h4 id="prerequisites"&gt;Prerequisites&lt;/h4&gt; 
&lt;p&gt;To get started with this code example, make sure you have the following prerequisites.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;An AWS account.&lt;/li&gt; 
 &lt;li&gt;An &lt;a href="https://aws.amazon.com/iam/" target="_blank" rel="noopener"&gt;AWS Identity and Access Management&lt;/a&gt; (IAM) role with permissions for AWS Glue, the AWS Glue Data Catalog, and Amazon S3. For more information, see &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/set-up-iam.html" target="_blank" rel="noopener"&gt;Setting up IAM permissions for AWS Glue&lt;/a&gt;. This will be the AWS Glue job execution role.&lt;/li&gt; 
 &lt;li&gt;An S3 bucket to store the Iceberg table data.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h4 id="steps"&gt;Steps&lt;/h4&gt; 
&lt;p&gt;To create an AWS Glue 6.0 job, use the following steps.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Log in to your AWS account and open the &lt;a href="https://console.aws.amazon.com/glue/home" target="_blank" rel="noopener"&gt;AWS Glue console&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;Create a new ETL job, with &lt;strong&gt;Script editor&lt;/strong&gt; option. 
  &lt;ol type="a"&gt; 
   &lt;li&gt;Choose engine as &lt;strong&gt;Spark&lt;/strong&gt; in the drop-down menu.&lt;/li&gt; 
   &lt;li&gt;&lt;strong&gt;Start fresh&lt;/strong&gt;, &lt;strong&gt;Create script&lt;/strong&gt; and copy-paste the following code.&lt;/li&gt; 
   &lt;li&gt;Replace the demo S3 bucket name with your bucket name in the code.&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;# Example pySpark script for testing few Iceberg v3's new data types
from pyspark.sql import SparkSession

CATALOG = "glue_catalog"
DATABASE = "sample_glue6_iceberg_db"
TABLE_NAME = "sample_glue6_table"
TABLE = f"{CATALOG}.{DATABASE}.{TABLE_NAME}"
TABLE_LOCATION = "s3://amzn-s3-demo-table-bucket/glue6blog-newdatatypes/"

# Configure Spark to use Apache Iceberg with the AWS Glue Data Catalog.
spark = (
    SparkSession.builder
    .appName("Glue6NewDataTypes")
    .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
    .config(f"spark.sql.catalog.{CATALOG}", "org.apache.iceberg.spark.SparkCatalog")
    .config(f"spark.sql.catalog.{CATALOG}.catalog-impl", "org.apache.iceberg.aws.glue.GlueCatalog")
    .config(f"spark.sql.catalog.{CATALOG}.io-impl", "org.apache.iceberg.aws.s3.S3FileIO")
    .config(f"spark.sql.catalog.{CATALOG}.warehouse", "s3://amzn-s3-demo-table-bucket/glue6blog-newdatatypes")
    .config("spark.sql.defaultColumn.enabled", "true")
    .getOrCreate()
)

spark.sql(f"CREATE DATABASE IF NOT EXISTS {CATALOG}.{DATABASE}")

# Create an Iceberg v3 table with VARIANT, unknown, and a default value.
# Spark's VOID type is stored as the Iceberg v3 unknown type.
spark.sql(
    f"""
    CREATE TABLE {TABLE} (
        record_id BIGINT,
        payload VARIANT,
        reserved_field VOID,
        status STRING DEFAULT 'active'
    )
    USING ICEBERG
    LOCATION '{TABLE_LOCATION}'
    TBLPROPERTIES ('format-version' = '3')
    """
)

# Insert two rows. The omitted columns use null and the declared default.
spark.sql(
    f"""
    INSERT INTO {TABLE} (record_id, payload)
    VALUES
    (1, parse_json('{{"event_type":"created","score":98.5}}')),
    (2, parse_json('{{"event_type":"processed","score":87.2}}'))
    """
)

# Query the row and extract values from the VARIANT column.
spark.sql(
    f"""
    SELECT
        record_id,
        variant_get(payload, '$.event_type', 'string') AS event_type,
        variant_get(payload, '$.score', 'double') AS score,
        reserved_field,
        status
    FROM {TABLE}
    """
).show(truncate=False)

spark.stop()&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;ol start="3" type="1"&gt; 
 &lt;li&gt;Provide the following details in the &lt;strong&gt;Job details&lt;/strong&gt; tab. 
  &lt;ol type="a"&gt; 
   &lt;li&gt;A &lt;strong&gt;Name&lt;/strong&gt; for the job.&lt;/li&gt; 
   &lt;li&gt;The IAM role you have from Prerequisites (2) for the &lt;strong&gt;IAM role&lt;/strong&gt; of the job.&lt;/li&gt; 
   &lt;li&gt;Choose &lt;strong&gt;Glue 6.0&lt;/strong&gt; for the &lt;strong&gt;Glue version&lt;/strong&gt;.&lt;/li&gt; 
   &lt;li&gt;Leave the rest as defaults.The following screenshot shows the &lt;strong&gt;Job details&lt;/strong&gt; tab with illustrated values in the AWS Glue console. &lt;p&gt;&lt;/p&gt;
    &lt;div style="width: 810px" class="wp-caption alignnone"&gt;
     &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-1-1.png" alt="AWS Glue Job details tab with the Glue version set to Glue 6.0 and other settings left as defaults" width="800"&gt;
     &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Job details tab with the Glue version set to Glue 6.0&lt;/p&gt;
    &lt;/div&gt;&lt;/li&gt; 
   &lt;li&gt;Scroll down. Under &lt;strong&gt;Advanced properties&lt;/strong&gt;, for &lt;strong&gt;Job parameters&lt;/strong&gt;, add the following additional Job parameter key-value pair:&lt;code&gt;--datalake-formats=iceberg&lt;/code&gt;The following screenshot shows the &lt;strong&gt;Job parameters&lt;/strong&gt; with the illustrated key-value pair in the AWS Glue console. &lt;p&gt;&lt;/p&gt;
    &lt;div style="width: 724px" class="wp-caption alignnone"&gt;
     &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-2-1.png" alt="Advanced properties section showing the Job parameters key –datalake-formats set to the value iceberg" width="714"&gt;
     &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Job parameters with the datalake-formats key set to iceberg&lt;/p&gt;
    &lt;/div&gt;&lt;/li&gt; 
  &lt;/ol&gt; &lt;/li&gt; 
 &lt;li&gt;Save the job and choose &lt;strong&gt;Run&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;After the job is completed successfully, from the &lt;strong&gt;Runs&lt;/strong&gt; tab – &lt;strong&gt;Run details&lt;/strong&gt;, you can inspect the &lt;strong&gt;Output logs&lt;/strong&gt; that take you to the logs in the &lt;a href="https://aws.amazon.com/cloudwatch/" target="_blank" rel="noopener"&gt;Amazon CloudWatch&lt;/a&gt; console. The following shows the sample output for the &lt;code&gt;SELECT&lt;/code&gt; query in the script.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-text"&gt;+---------+----------+-----+--------------+------+
|record_id|event_type|score|reserved_field|status|
+---------+----------+-----+--------------+------+
|1        |created   |98.5 |NULL          |active|
|2        |processed |87.2 |NULL          |active|
+---------+----------+-----+--------------+------+&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Notice that we inserted two rows with values only in the &lt;code&gt;record_id&lt;/code&gt; and the variant column. Variant column inserts were done using &lt;code&gt;parse_json()&lt;/code&gt;. The &lt;code&gt;reserved_field&lt;/code&gt; is of VOID type, hence returns NULL values. The &lt;code&gt;status&lt;/code&gt; column is declared with a default &lt;code&gt;active&lt;/code&gt; value and returns &lt;code&gt;active&lt;/code&gt;, since the column was omitted during the insert operation.&lt;/p&gt; 
&lt;ol start="4"&gt; 
 &lt;li&gt;&lt;strong&gt;Deletion Vectors&lt;/strong&gt;&lt;br&gt; Apache Iceberg V3 replaces the traditional positional delete files used in Apache Iceberg V2 to &lt;a href="https://iceberg.apache.org/spec/#deletion-vectors" target="_blank" rel="noopener"&gt;deletion vectors&lt;/a&gt;. This change can help improve Merge-on-Read (MoR) performance. This shift replaces heavy, multi-file Parquet reads with highly compressed, direct binary bitmaps that can provide lower storage overhead and faster reads on delete-heavy tables. In scenarios with heavy table updates, such as streaming change data capture (CDC) from operational databases, Apache Iceberg V3 can offer read performance advantage over Apache Iceberg V2.&lt;p&gt;&lt;/p&gt; &lt;p style="clear: both"&gt;&lt;/p&gt; To validate the performance of deletion vectors, we created two identical AWS Glue streaming jobs and ingested the events into two different Iceberg tables, one in Apache Iceberg V2 and another in Apache Iceberg V3 format. The streaming CDC events were approximately 150,000 events per second, merge-on-read, update-heavy. Every micro-batch writes row-level deletes. We froze both tables at the same delete-heavy state and disabled compaction, leaving the tables with roughly 1.7 million rows in valid state out of the 26.4 million physical rows. The following table summarizes the read performance latency of the two Iceberg tables. We observed in this testing that reading from the delete-heavy Apache Iceberg V3 is at least 1.5 times faster than the reading from a similar Apache Iceberg V2 table. For larger enterprise scale Apache Iceberg V3 tables, the read performance could improve further.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;figure&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-3-1.png" alt="Read latency comparison showing Apache Iceberg V3 deletion vectors reading at least 1.5 times faster than Apache Iceberg V2 delete files" width="800"&gt;
&lt;/figure&gt; 
&lt;p&gt;&lt;em&gt;Table 2 – Read latency comparison between Apache Iceberg V2 delete files and Apache Iceberg V3 deletion vectors&lt;/em&gt;&lt;/p&gt; 
&lt;h3 id="new-etl-features"&gt;New ETL features&lt;/h3&gt; 
&lt;p&gt;AWS Glue 6.0 introduces several additional capabilities designed to simplify how you build and manage data pipelines, some of which are discussed in the following list.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Spark Declarative Pipelines (SDP) where you define the outcomes you want for your entire data pipelines in a declarative fashion with SQL statements or Python decorators while AWS Glue handles execution flow, dependency resolution, parallelism, checkpointing, CDC, and recovery automatically. This helps you focus on business logic rather than orchestration plumbing.&lt;/li&gt; 
 &lt;li&gt;Real-Time Mode (RTM) for streaming delivers continuous execution for Structured Streaming with sub-second latencies, down to single-digit milliseconds for stateless tasks. This can help support real-time use cases like fraud detection, live dashboards, and event-driven architectures without managing dedicated streaming infrastructure.&lt;/li&gt; 
 &lt;li&gt;Arrow-Native UDFs/UDTFs execute Python functions directly on PyArrow batches without Pandas conversion overhead, which can result in faster performance for custom transformation logic at scale.&lt;/li&gt; 
 &lt;li&gt;Recursive Common Table Expressions (CTE) adds WITH RECURSIVE queries natively, allowing graph traversals and hierarchical queries without workarounds or external libraries.&lt;/li&gt; 
 &lt;li&gt;Python data source filter pushdown evaluates filters at the data source designed to minimize data movement, reduce the volume of data scanned, and improve job performance.&lt;/li&gt; 
 &lt;li&gt;With Python virtual environments and S3 caching, you can provide pre-built Python dependencies, which should result in reduced startup latency for AWS Glue jobs by eliminating runtime dependency resolution. For existing jobs that use &lt;code&gt;--additional-python-modules&lt;/code&gt;, no action is required. AWS Glue automatically handles the conversion to virtual environments when your job runs on AWS Glue 6.0.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3 id="dependent-library-upgrades"&gt;Dependent library upgrades&lt;/h3&gt; 
&lt;p&gt;The following table summarizes the key runtime and library version upgrades on AWS Glue 6.0.&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;&lt;strong&gt;Glue 6.0&lt;/strong&gt;&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;&lt;strong&gt;Glue 5.1&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Spark&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;4.1.1-amzn-0&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;3.5.6-amzn-1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Python&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;3.13.14&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;3.11.15&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Scala&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;2.13.17&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;2.12.18&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;AWS SDK for Java&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;2.44.6&lt;br&gt; (Version 1.x removed)&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;2.35.5&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Boto3&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;1.42.84&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;1.40.61&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Java&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;17.0.20&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;17.0.19&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Iceberg&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;1.11.0&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;1.10.0&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Hudi&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;1.1.1&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;.0.2&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Delta Lake&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;4.2.0&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;3.3.2&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;AWS Glue Data Catalog client&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;4.11.0&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;4.9.0&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;EMR DynamoDB connector&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;6.1.0&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;5.7.0&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Arrow&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;18.3.0&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;2.0.1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td style="text-align: left"&gt;Hive&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;2.3.10-amzn-1&lt;/td&gt; 
   &lt;td style="text-align: left"&gt;2.3.9-amzn-4&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;&lt;em&gt;Table 3: Runtime and library version comparison across AWS Glue 6.0 and AWS Glue 5.1&lt;/em&gt;&lt;/p&gt; 
&lt;h3 id="getting-started"&gt;Getting started&lt;/h3&gt; 
&lt;p&gt;&lt;span data-contrast="none"&gt;To get started with AWS Glue 6.0, you can use one of the following methods.&amp;nbsp;&lt;/span&gt;&lt;span data-ccp-props="{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559739&amp;quot;:200,&amp;quot;335559740&amp;quot;:288}"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li data-leveltext="•" data-font="Symbol" data-listid="10" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:720,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Symbol&amp;quot;,&amp;quot;469769242&amp;quot;:[8226],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;•&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;hybridMultilevel&amp;quot;}" data-aria-posinset="4" data-aria-level="1"&gt;&lt;span data-contrast="none"&gt;Open the&amp;nbsp;&lt;/span&gt;&lt;a href="https://console.aws.amazon.com/glue/home" target="_blank" rel="noopener"&gt;&lt;span data-contrast="none"&gt;AWS Glue console&lt;/span&gt;&lt;/a&gt;&lt;span data-contrast="none"&gt;&amp;nbsp;and create a new ETL job, selecting Glue 6.0&amp;nbsp;for the Job&amp;nbsp;version.&amp;nbsp;You can also use AWS SDK,&amp;nbsp;&lt;/span&gt;&lt;a href="https://aws.amazon.com/cli/" target="_blank" rel="noopener"&gt;&lt;span data-contrast="none"&gt;AWS Command Line Interface&lt;/span&gt;&lt;/a&gt;&lt;span data-contrast="none"&gt;&amp;nbsp;(CLI)&amp;nbsp;and&amp;nbsp;&lt;/span&gt;&lt;a href="https://aws.amazon.com/sagemaker/unified-studio/" target="_blank" rel="noopener"&gt;&lt;span data-contrast="none"&gt;Amazon SageMaker Unified Studio&lt;/span&gt;&lt;/a&gt;&lt;span data-contrast="none"&gt;&amp;nbsp;for creating Glue jobs with version 6.0.&amp;nbsp;&lt;/span&gt;&lt;span data-ccp-props="{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559739&amp;quot;:200,&amp;quot;335559740&amp;quot;:288}"&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt; 
 &lt;li data-leveltext="•" data-font="Symbol" data-listid="10" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:720,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Symbol&amp;quot;,&amp;quot;469769242&amp;quot;:[8226],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;•&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;hybridMultilevel&amp;quot;}" data-aria-posinset="4" data-aria-level="1"&gt;&lt;span data-contrast="none"&gt;To get started with AWS Glue 6.0 interactively, use&amp;nbsp;&lt;/span&gt;&lt;a href="https://docs.aws.amazon.com/glue/latest/dg/interactive-sessions-chapter.html" target="_blank" rel="noopener"&gt;&lt;span data-contrast="none"&gt;Glue Interactive Sessions&lt;/span&gt;&lt;/a&gt;&lt;span data-contrast="none"&gt;&amp;nbsp;within Glue Studio Notebooks (via the&amp;nbsp;&lt;/span&gt;&lt;a href="https://docs.aws.amazon.com/glue/latest/dg/interactive-sessions-magics.html" target="_blank" rel="noopener"&gt;&lt;span data-contrast="none"&gt;%glue_version magic&lt;/span&gt;&lt;/a&gt;&lt;span data-contrast="none"&gt;) or connect with&amp;nbsp;&lt;/span&gt;&lt;a href="https://docs.aws.amazon.com/glue/latest/dg/interactive-sessions-spark-connect.html" target="_blank" rel="noopener"&gt;&lt;span data-contrast="none"&gt;Spark Connect&lt;/span&gt;&lt;/a&gt;&lt;span data-contrast="none"&gt;&amp;nbsp;from your local IDE.&lt;/span&gt;&lt;span data-ccp-props="{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559739&amp;quot;:200,&amp;quot;335559740&amp;quot;:288}"&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt; 
 &lt;li data-leveltext="•" data-font="Symbol" data-listid="10" data-list-defn-props="{&amp;quot;335552541&amp;quot;:1,&amp;quot;335559685&amp;quot;:720,&amp;quot;335559991&amp;quot;:360,&amp;quot;469769226&amp;quot;:&amp;quot;Symbol&amp;quot;,&amp;quot;469769242&amp;quot;:[8226],&amp;quot;469777803&amp;quot;:&amp;quot;left&amp;quot;,&amp;quot;469777804&amp;quot;:&amp;quot;•&amp;quot;,&amp;quot;469777815&amp;quot;:&amp;quot;hybridMultilevel&amp;quot;}" data-aria-posinset="4" data-aria-level="1"&gt;&lt;span data-contrast="none"&gt;Migrate your existing AWS Glue jobs to AWS Glue 6.0 using the&amp;nbsp;&lt;/span&gt;&lt;a href="https://docs.aws.amazon.com/glue/latest/dg/upgrade-analysis.html" target="_blank" rel="noopener"&gt;&lt;span data-contrast="none"&gt;Spark Upgrade Agent&lt;/span&gt;&lt;/a&gt;&lt;span data-contrast="none"&gt;.&lt;/span&gt;&lt;span data-ccp-props="{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559739&amp;quot;:200,&amp;quot;335559740&amp;quot;:288}"&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3 id="clean-up"&gt;Clean up&lt;/h3&gt; 
&lt;p&gt;To avoid incurring costs, clean up the resources you created for this post.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Delete the Data Catalog database and the Iceberg table.&lt;/li&gt; 
 &lt;li&gt;Delete the data and metadata folders of the Iceberg table from your S3 bucket.&lt;/li&gt; 
 &lt;li&gt;Delete the AWS Glue job and the AWS Glue job execution IAM role.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt; 
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/glue/latest/dg/migrating-version-60.html#migrating-version-60-features" target="_blank" rel="noopener"&gt;AWS Glue 6.0&lt;/a&gt; is designed to be faster, more cost-effective, and easier to use for building your open data lakehouse architectures and orchestrating your data pipelines. In this post, we discussed the key highlights of AWS Glue 6.0 and illustrated usage of Apache Iceberg V3 features with code samples. You can create new AWS Glue jobs on AWS Glue 6.0 or &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/migrating-version-60.html" target="_blank" rel="noopener"&gt;migrate your existing AWS Glue jobs&lt;/a&gt; to benefit from these improvements.&lt;/p&gt; 
&lt;p&gt;With Apache Spark 4.1.1, Apache Iceberg V3, Python 3.13, upgraded open table format libraries, and new streaming capabilities, AWS Glue 6.0 aims to help you build new data applications or to operate your existing data pipelines more efficiently and with less maintenance overhead.&lt;/p&gt; 
&lt;p&gt;We encourage you to test AWS Glue 6.0 in your development environment today. Check out this blog that talks about &lt;a href="https://aws.amazon.com/blogs/big-data/upgrade-aws-glue-jobs-to-glue-6-0-with-ai-powered-spark-upgrades/" target="_blank" rel="noopener"&gt;upgrading your AWS Glue jobs to AWS Glue 6.0&lt;/a&gt;. Also, in the coming days and weeks, look out for blogs on individual topics illustrating various features of Spark 4.1.1 and Apache Iceberg V3 on AWS Glue 6.0.&lt;/p&gt; 
&lt;p&gt;Acknowledgements: We thank the numerous engineers and leaders who helped build AWS Glue 6.0 to support customers with a highly performant Spark runtime and other value-added capabilities.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-4-1.jpg" alt="Aarthi Srinivasan" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Aarthi Srinivasan&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/aarthis/" target="_blank" rel="noopener"&gt;Aarthi&lt;/a&gt; is a Senior Big Data Architect working on data, analytics and GenAI topics with the worldwide specialist org at AWS. She works with AWS customers and partners to architect open data lake solutions, enhance product features, and establish best practices for data governance and analytics services adoption.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-5-1.png" alt="Shrey Malpani" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Shrey Malpani&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/shreymalpani/" target="_blank" rel="noopener"&gt;Shrey&lt;/a&gt; is a Senior Product Manager Technical at Amazon Web Services (AWS), where he works at the intersection of distributed data processing and data integration. He is focused on building and scaling data integration and data management capabilities across services like AWS Glue, Amazon EMR, and Amazon Redshift that help customers build AI-ready data platforms for their analytics and machine learning workflows.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-6-1.png" alt="Angel Conde Manjon" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Angel Conde Manjon&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/acmanjon/" target="_blank" rel="noopener"&gt;Angel&lt;/a&gt; is a Senior Solutions Architect at AWS where he helps partners develop businesses centered on Data and AI. He has previously worked on research related to Data Analytics and Artificial Intelligence in diverse European research projects. Angel is also an Apache Iceberg contributor.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-7.png" alt="Peter Tsai" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Peter Tsai&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/wei-ying-tsai-08214675/" target="_blank" rel="noopener"&gt;Peter&lt;/a&gt; is a Software Development Engineer at AWS, where he enjoys solving challenges in the design and performance of the AWS Glue runtime. In his leisure time, he enjoys hiking and cycling.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-8.png" alt="Danylo Prozorov" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Danylo Prozorov&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/danylo-prozorov/" target="_blank" rel="noopener"&gt;Danylo&lt;/a&gt; is a Software Development Engineer at AWS Glue, where he works on building data integration and generative AI solutions for analytics customers. Outside of work, he enjoys sports, hiking, riding motorcycles, and building his overland rig.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-9.png" alt="Bo Li" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Bo Li&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/bo-li-535664107/" target="_blank" rel="noopener"&gt;Bo&lt;/a&gt; is a Senior Software Development Engineer on the AWS Glue team. He is devoted to designing and building end-to-end solutions to address customers’ data analytic and processing needs with cloud-based, data-intensive and GenAI technologies.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-10.png" alt="Kartik Panjabi" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Kartik Panjabi&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/kartik-panjabi/" target="_blank" rel="noopener"&gt;Kartik&lt;/a&gt; is a Software Development Manager on the AWS Glue team. His team builds generative AI features for the Data Integration and distributed system for data integration.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-6093-11.png" alt="Mohit Saxena" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Mohit Saxena&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/mohitsax/" target="_blank" rel="noopener"&gt;Mohit&lt;/a&gt; leads AWS Glue and AWS Data Analytics agentic AI initiatives that help customers build and operate big data applications on Apache Spark, Amazon S3, and cloud data lakes and warehouses, spanning across AWS Glue, Amazon EMR, and Amazon Athena.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Upgrade AWS Glue jobs to Glue 6.0 with AI-powered Spark upgrades</title>
		<link>https://aws.amazon.com/blogs/big-data/upgrade-aws-glue-jobs-to-glue-6-0-with-ai-powered-spark-upgrades/</link>
		
		<dc:creator><![CDATA[Prasad Nadig]]></dc:creator>
		<pubDate>Mon, 24 Aug 2026 19:06:16 +0000</pubDate>
				<category><![CDATA[Advanced (300)]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[AWS Glue]]></category>
		<guid isPermaLink="false">b701ea01f9a32fe7b0961dd09a27ddeb65ec2130</guid>

					<description>Walk through upgrading a PySpark ETL job from AWS Glue 5.1 to AWS Glue 6.0 using the generative AI upgrades for Apache Spark. The upgrade analysis automatically detects incompatibilities, applies fixes, and validates results with data quality checks.</description>
										<content:encoded>&lt;p&gt;Upgrading PySpark jobs to a new Apache Spark major version can introduce breaking changes. Removed configuration keys, stricter type casting, and Python library incompatibilities can cause runtime failures or silent behavior differences. With &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/08/aws-glue-6-0-price-reduction-iceberg-v3/" target="_blank" rel="noopener"&gt;AWS Glue 6.0&lt;/a&gt; now running Apache Spark 4.1 and Python 3.13, you need a reliable way to migrate your existing jobs while validating correctness.&lt;/p&gt; 
&lt;p&gt;In this post, we walk through upgrading a PySpark ETL job from AWS Glue 5.1 to AWS Glue 6.0. We use the generative AI upgrades for Apache Spark in the AWS Glue console. The upgrade analysis automatically identifies incompatibilities, iteratively resolves them, validates the result with data quality checks, and presents recommended changes for your review. AWS Glue 6.0 also delivers up to 36% better price performance* along with Iceberg v3, Spark Declarative Pipelines, Real-Time Mode, and Arrow-native Python UDFs.&lt;/p&gt; 
&lt;h2 id="what-changes-with-aws-glue-6.0"&gt;What changes with AWS Glue 6.0&lt;/h2&gt; 
&lt;p&gt;AWS Glue 6.0 runs Apache Spark 4.1, which introduces several behavioral changes from the Spark 3.5 runtime used in AWS Glue 5.1:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Behavior&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Spark 3.5 (AWS Glue 5.1)&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Spark 4.1 (AWS Glue 6.0)&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;ANSI SQL mode&lt;/td&gt; 
   &lt;td&gt;Disabled by default&lt;/td&gt; 
   &lt;td&gt;Enabled by default&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Legacy Parquet datetime configs&lt;/td&gt; 
   &lt;td&gt;Supported&lt;/td&gt; 
   &lt;td&gt;Removed (renamed)&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Python runtime&lt;/td&gt; 
   &lt;td&gt;3.11&lt;/td&gt; 
   &lt;td&gt;3.13&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;Beyond version compatibility, AWS Glue 6.0 also introduces:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Apache Iceberg v3&lt;/strong&gt; with VARIANT Shredding for efficient semi-structured data handling.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Spark Declarative Pipelines&lt;/strong&gt; — agent-authorable ETL.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Real-Time Mode&lt;/strong&gt; — single-digit millisecond streaming latency.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Arrow-native Python UDFs&lt;/strong&gt; (PyArrow) for improved performance.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Built-in observability&lt;/strong&gt; with structured metrics and enhanced Spark UI.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Up to 36% better price performance compared to AWS Glue 5.1&lt;/strong&gt;*.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;These runtime changes mean your existing AWS Glue jobs might encounter removed configuration keys, stricter type casting behavior, or Python package version incompatibilities when running on AWS Glue 6.0. Fixing these manually is time-consuming and error-prone. The following sections show how the generative upgrade analysis handles this automatically.&lt;/p&gt; 
&lt;h2 id="the-sample-job"&gt;The sample job&lt;/h2&gt; 
&lt;p&gt;Our example is a daily ecommerce order analytics pipeline running on AWS Glue 5.1:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;What the job does:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Ingests 10,000 orders from Parquet files with INT96 timestamps (including pre-1900 historical dates from a legacy system migration).&lt;/li&gt; 
 &lt;li&gt;Computes revenue metrics by casting string prices to numeric values and calculating line totals with discounts and tax.&lt;/li&gt; 
 &lt;li&gt;Segments customers using recency, frequency, and monetary (RFM) scoring through &lt;code&gt;mapInPandas&lt;/code&gt; with pandas and scikit-learn.&lt;/li&gt; 
 &lt;li&gt;Writes enriched results back to &lt;a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener"&gt;Amazon Simple Storage Service (Amazon S3)&lt;/a&gt;.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Job configuration (AWS Glue 5.1):&lt;/strong&gt;&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-text"&gt;Glue version: 5.1
Worker type: G.1X
Workers: 10
Python modules: pandas==2.2.2, scikit-learn==1.5.0, numpy==1.26.4
Spark configs:
  spark.sql.legacy.parquet.datetimeRebaseModeInWrite=LEGACY
  spark.sql.legacy.parquet.int96RebaseModeInWrite=LEGACY
  spark.sql.parquet.datetimeRebaseModeInRead=LEGACY
  spark.sql.parquet.int96RebaseModeInRead=LEGACY&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;This job runs successfully on AWS Glue 5.1. The following sections walk through how the upgrade analysis identifies and resolves incompatibilities when upgrading this job to AWS Glue 6.0. Before starting, confirm you have the prerequisites in place.&lt;/p&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;An AWS account with access to the AWS Glue console.&lt;/li&gt; 
 &lt;li&gt;An existing AWS Glue job on version 5.1 or earlier with at least one successful run.&lt;/li&gt; 
 &lt;li&gt;An Amazon S3 path for storing the upgrade analysis results.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="running-the-upgrade-analysis-from-the-console"&gt;Running the upgrade analysis from the console&lt;/h2&gt; 
&lt;p&gt;The following steps walk through the upgrade analysis workflow using the AWS Glue console.&lt;/p&gt; 
&lt;h3 id="step-1-select-your-job"&gt;Step 1: Select your job&lt;/h3&gt; 
&lt;p&gt;Navigate to your job in the &lt;a href="https://console.aws.amazon.com/gluestudio/home#/jobs" target="_blank" rel="noopener"&gt;AWS Glue Studio&lt;/a&gt; console. Confirm the job has a successful run history on AWS Glue 5.1 before starting the upgrade analysis.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-1.png" alt="AWS Glue Studio job run history showing a successful run on AWS Glue 5.1" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Job run status for the job on AWS Glue 5.1&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="step-2-start-the-upgrade-analysis"&gt;Step 2: Start the upgrade analysis&lt;/h3&gt; 
&lt;p&gt;From the job’s &lt;strong&gt;Actions&lt;/strong&gt; menu, select &lt;strong&gt;Upgrade with generative AI&lt;/strong&gt;. Configure the following:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Target AWS Glue version:&lt;/strong&gt; 6.0.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Results S3 path:&lt;/strong&gt; An S3 location where the analysis stores its artifacts and recommendations.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-2.png" alt="Actions menu in AWS Glue Studio with the Upgrade with generative AI option" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: The Upgrade with generative AI option in the Actions menu&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Configure the target AWS Glue version and the S3 results path, then choose &lt;strong&gt;Run&lt;/strong&gt;.&lt;/p&gt; 
&lt;div style="width: 611px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-3.png" alt="Upgrade window with the target AWS Glue version set to 6.0 and an Amazon S3 results path" width="601"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: The Upgrade with generative AI window for setting the target AWS Glue version and results path&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Choose &lt;strong&gt;Run&lt;/strong&gt;. The analysis begins by running your job on AWS Glue 5.1 to establish a baseline. It then iteratively tests the job on AWS Glue 6.0, identifies failures, applies recommended fixes, and validates the job. If the upgrade analysis cannot resolve an incompatibility within its attempt budget, the analysis stops and reports the unresolved issue for manual review. Your original job remains unchanged.&lt;/p&gt; 
&lt;p&gt;&lt;em&gt;Note: The upgrade analysis executes your job multiple times (one baseline run plus one or more validation attempts), and each run consumes Data Processing Units (DPUs). For large or long-running jobs, consider using the run configuration option to specify fewer workers or a smaller dataset to optimize analysis cost.&lt;/em&gt;&lt;/p&gt; 
&lt;h3 id="step-3-monitor-progress"&gt;Step 3: Monitor progress&lt;/h3&gt; 
&lt;p&gt;The console displays the analysis progressing through multiple validation attempts. Each attempt either succeeds or fails with a specific error, and the upgrade analysis uses that error signal to determine and apply the appropriate fix for the next attempt.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-4.png" alt="Upgrade analysis progress showing multiple validation attempts with success and failure states" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 4: Upgrade analysis progress across multiple validation attempts&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="what-the-upgrade-analysis-found-and-fixed"&gt;What the upgrade analysis found and fixed&lt;/h2&gt; 
&lt;p&gt;The analysis completed in four validation attempts, identifying and resolving three distinct incompatibilities. The upgrade uses deterministic migration rules for known config changes, and automated diagnosis for runtime or code errors.&lt;/p&gt; 
&lt;h3 id="iteration-1-removed-parquet-legacy-configuration"&gt;Iteration 1: Removed Parquet legacy configuration&lt;/h3&gt; 
&lt;p&gt;The analysis first sanitizes any Spark configurations that were removed in Spark 4.1. Our job used &lt;code&gt;spark.sql.legacy.parquet.datetimeRebaseModeInWrite&lt;/code&gt; and &lt;code&gt;spark.sql.legacy.parquet.int96RebaseModeInWrite&lt;/code&gt;, which no longer exist.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Migration rule applied:&lt;/strong&gt; The SQL configs with the &lt;code&gt;spark.sql.legacy&lt;/code&gt; prefix were removed in Spark 4.1. They have been renamed to their non-legacy equivalents, preserving the original values.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Recommended change:&lt;/strong&gt;&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-text"&gt;Before:
spark.sql.legacy.parquet.datetimeRebaseModeInWrite=LEGACY
spark.sql.legacy.parquet.int96RebaseModeInWrite=LEGACY

After:
spark.sql.parquet.datetimeRebaseModeInWrite=LEGACY
spark.sql.parquet.int96RebaseModeInWrite=LEGACY&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The read-side configs (&lt;code&gt;datetimeRebaseModeInRead&lt;/code&gt;, &lt;code&gt;int96RebaseModeInRead&lt;/code&gt;) already used the correct non-legacy names and required no changes.&lt;/p&gt; 
&lt;p&gt;However, with this fix applied, the validation run still failed because the Python module installation encountered an error on the AWS Glue 6.0 image.&lt;/p&gt; 
&lt;h3 id="iteration-2-python-module-version-incompatibility"&gt;Iteration 2: Python module version incompatibility&lt;/h3&gt; 
&lt;p&gt;The pinned module versions (&lt;code&gt;pandas==2.2.2, scikit-learn==1.5.0, numpy==1.26.4&lt;/code&gt;) could not be installed in the AWS Glue 6.0 Python 3.13 environment.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-text"&gt;LAUNCH ERROR | Installation of Additional Python Modules failed&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Recommended change:&lt;/strong&gt; The upgrade analysis updated the version specifications from exact pins to minimum version constraints, allowing pip to resolve compatible versions for Python 3.13:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-text"&gt;Before: pandas==2.2.2, scikit-learn==1.5.0, numpy==1.26.4
After:  pandas&amp;gt;=2.1.0, scikit-learn&amp;gt;=1.3.0, numpy&amp;gt;=1.24.0&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;With modules installing successfully, the job launched on AWS Glue 6.0 but encountered a runtime error.&lt;/p&gt; 
&lt;h3 id="iteration-3-ansi-mode-strict-type-casting"&gt;Iteration 3: ANSI mode strict type casting&lt;/h3&gt; 
&lt;p&gt;Spark 4.1 enables ANSI SQL mode by default (&lt;code&gt;spark.sql.ansi.enabled=true&lt;/code&gt;). Our revenue calculation casts string prices to double, but approximately 1.8% of records contain non-numeric placeholder values such as “N/A”, “pending”, or “null” from the upstream system. From a business perspective, this meant 1.8% of revenue orders were silently excluded from revenue metrics. This data quality issue was invisible to the original pipeline.&lt;/p&gt; 
&lt;p&gt;On AWS Glue 5.1 (ANSI mode off), &lt;code&gt;cast("N/A" as double)&lt;/code&gt; silently returns null. On AWS Glue 6.0 (ANSI mode on), this throws an exception:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-text"&gt;NumberFormatException: [CAST_INVALID_INPUT] The value 'null' of the type
"STRING" cannot be cast to "DOUBLE" because it is malformed. Correct the
value as per the syntax, or change its target type. Use try_cast to
tolerate malformed input and return NULL instead. SQLSTATE: 22018&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Migration rule applied:&lt;/strong&gt; As of Spark 4.1, &lt;code&gt;spark.sql.ansi.enabled&lt;/code&gt; is on by default. Casting a malformed value now raises &lt;code&gt;CAST_INVALID_INPUT&lt;/code&gt; instead of returning &lt;code&gt;NULL&lt;/code&gt;. The upgrade analysis resolved this by updating the script to use &lt;code&gt;try_cast()&lt;/code&gt;, which safely returns &lt;code&gt;NULL&lt;/code&gt; for malformed input while preserving ANSI mode protections for the rest of the job.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Recommended change:&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;Before (AWS Glue 5.1):&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;F.col("unit_price").cast("double")&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;After (AWS Glue 6.0, fixed by the upgrade analysis):&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;F.expr("try_cast(unit_price as double)")&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;This is a targeted fix that handles the known dirty data without disabling ANSI mode globally, keeping overflow detection and type safety active throughout the job.&lt;/p&gt; 
&lt;h3 id="final-validation-and-data-quality-check"&gt;Final validation and data quality check&lt;/h3&gt; 
&lt;p&gt;After applying all three fixes, the analysis ran the job on AWS Glue 6.0 one final time and performed a data quality comparison between the AWS Glue 5.1 baseline output and the AWS Glue 6.0 output.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; The job completed successfully and all data validations passed with no mismatches detected between the source and target outputs.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-5.png" alt="Completed upgrade analysis status with links to the results output path in Amazon S3" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 5: Final analysis status with links to the results output path in Amazon S3&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="reviewing-the-upgrade-summary"&gt;Reviewing the upgrade summary&lt;/h2&gt; 
&lt;p&gt;The analysis produces a detailed summary stored in your S3 results path. This summary documents each validation attempt, the errors encountered, the migration rules applied, and the recommended configuration changes:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-text"&gt;s3://amzn-s3-demo-bucket/scripts/auto-upgrade/ja-{analysis-id}/
    summary/
        summary.md                       # Full iteration-by-iteration report
        data_validation_summary.md       # Data quality comparison results
    artifact/
        attempt_N/
            script/main.py               # Recommended script (if modified)
            job_config_modifications.json  # Recommended parameter changes
            requirements.txt             # Updated dependency versions&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The following is a snippet from the upgrade summary (summary.md) showing the recommended changes and validation attempt details:&lt;/p&gt; 
&lt;p&gt;The summary documents each validation attempt, the changes applied, and the data quality results.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-6.png" alt="Upgrade summary showing validation attempts, applied changes, and data quality comparison results" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 6: Upgrade summary snippet showing validation attempt details, data quality, and analysis results&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;After reviewing the recommendations, accept the changes to upgrade your job to AWS Glue 6.0. This updates your job definition with the recommended configuration, including the renamed Spark configs, updated module versions, and any script modifications. Because the analysis has already validated the job on AWS Glue 6.0 and confirmed data quality parity with the original, your job is ready for production.&lt;/p&gt; 
&lt;p&gt;After reviewing the recommendations, you can apply the upgraded script to your job.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-7.png" alt="AWS Glue Studio prompt to apply the upgraded script to the job" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 7: The option to apply the upgraded script to the job&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Choose &lt;strong&gt;Apply&lt;/strong&gt; to confirm the upgrade.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-8.png" alt="Confirmation dialog with the Apply button to upgrade the job to AWS Glue 6.0" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 8: The Apply button that confirms upgrading the job to AWS Glue 6.0&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;After applying, the job definition reflects the new AWS Glue version.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-9.png" alt="AWS Glue job details showing version 6.0 after applying the upgrade" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 9: The AWS Glue version for the job after applying the upgrade&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="python-virtual-environments-in-aws-glue-6.0"&gt;Python virtual environments in AWS Glue 6.0&lt;/h2&gt; 
&lt;p&gt;AWS Glue 6.0 introduces &lt;code&gt;--python-virtual-env-storage-prefix&lt;/code&gt;, a service-managed virtual environment with S3 caching that simplifies Python dependency management.&lt;/p&gt; 
&lt;p&gt;For existing jobs that use &lt;code&gt;--additional-python-modules&lt;/code&gt;, no action is required. AWS Glue automatically handles the conversion to virtual environments when your job runs on AWS Glue 6.0. Your jobs continue to work without any changes.&lt;/p&gt; 
&lt;p&gt;For new jobs on AWS Glue 6.0, we recommend using the virtual environment approach:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
    "DefaultArguments": {
        "--python-virtual-env-storage-prefix": "s3://amzn-s3-demo-bucket/glue-venv-cache/",
        "--additional-python-modules": "pandas&amp;gt;=2.1.0,scikit-learn&amp;gt;=1.3.0,numpy&amp;gt;=1.24.0"
    }
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;On the first run, AWS Glue installs your modules into a virtual environment, packages it, and caches the result to your specified S3 path (approximately 15–30 seconds of additional startup time).&lt;/li&gt; 
 &lt;li&gt;On subsequent runs, AWS Glue downloads and extracts the cached virtual environment instead of running &lt;code&gt;pip install&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;The cache is automatically invalidated when your module list, versions, or AWS Glue version changes.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;This approach provides faster cold starts after the first run, requires no Docker image management (unlike &lt;code&gt;--python-virtual-env&lt;/code&gt;), and is entirely service-managed with no maintenance burden.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;The generative upgrade analysis identified and resolved three distinct compatibility issues in our AWS Glue 5.1 job, so the job now runs successfully on AWS Glue 6.0 with Apache Spark 4.1:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;The upgrade analysis renamed legacy Parquet datetime configuration keys (removed in Spark 4.1) to their current equivalents.&lt;/li&gt; 
 &lt;li&gt;The upgrade analysis updated Python module version specifications that were incompatible with Python 3.13 to use flexible minimum version constraints.&lt;/li&gt; 
 &lt;li&gt;The upgrade analysis addressed the new ANSI SQL mode default (which causes runtime failures on malformed data) with a targeted fix using &lt;code&gt;try_cast()&lt;/code&gt; to safely handle non-numeric values while preserving ANSI mode protections.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The analysis validated that the upgraded job produces output consistent with the original, and presented all changes as recommendations for review before applying them to your job.&lt;/p&gt; 
&lt;h2 id="next-steps"&gt;Next steps&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;Review the &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/migrating-version-60.html" target="_blank" rel="noopener"&gt;AWS Glue 6.0 documentation&lt;/a&gt; for the complete list of new features and changes. For a detailed walkthrough of what is new, see &lt;a href="https://aws.amazon.com/blogs/big-data/introducing-aws-glue-6-0-for-apache-spark/" target="_blank" rel="noopener"&gt;Introducing AWS Glue 6.0 for Apache Spark&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;Read the &lt;a href="https://spark.apache.org/docs/latest/sql-migration-guide.html" target="_blank" rel="noopener"&gt;Apache Spark 4.1 migration&lt;/a&gt; for additional behavioral changes.&lt;/li&gt; 
 &lt;li&gt;Try the upgrade analysis on your AWS Glue jobs through the console.&lt;/li&gt; 
 &lt;li&gt;See the &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-python-libraries.html" target="_blank" rel="noopener"&gt;Python virtual environment documentation&lt;/a&gt; for detailed setup guidance.&lt;/li&gt; 
 &lt;li&gt;To reproduce this walkthrough, start with any existing AWS Glue job running on version 5.1 or earlier with a successful run history. No additional sample code or CloudFormation template is required.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;After you have reviewed and accepted the upgrade changes, you can delete the analysis results stored in your S3 results path.&lt;/p&gt; 
&lt;p&gt;*Based on 3TB TPC-DS benchmark comparing AWS Glue 6.0 to AWS Glue 5.1.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-10.jpeg" alt="Prasad Nadig" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Prasad Nadig&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="http://www.linkedin.com/in/prasad-nadig" target="_blank" rel="noopener"&gt;Prasad&lt;/a&gt; is a Senior Analytics Specialist Solutions Architect at Amazon Web Services (AWS), specializing in large-scale data analytics and AI. Prasad partners with customers to design, migrate, and modernize their analytics platforms on AWS into scalable, cost-effective solutions, with deep expertise in data lakes, data warehousing, distributed processing, and performance tuning at petabyte scale.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-11.jpeg" alt="Shrey Malpani" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Shrey Malpani&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/shreymalpani/" target="_blank" rel="noopener"&gt;Shrey&lt;/a&gt; is a Senior Product Manager Technical at Amazon Web Services (AWS), where he works at the intersection of distributed data processing and data integration. He is focused on building and scaling data integration and data management capabilities across services like AWS Glue, Amazon EMR, and Amazon Redshift that help customers build AI-ready data platforms for their analytics and machine learning workflows.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-12.jpeg" alt="Rishabh Nair" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Rishabh Nair&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/rishabh-nair-9a897142/" target="_blank" rel="noopener"&gt;Rishabh&lt;/a&gt; is a Software Development Engineer in the AWS analytics organization, where he combines generative AI with distributed systems to build agentic workflows that modernize large-scale data processing. He is passionate about the infrastructure that makes these workflows reliable and scalable for customers.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/19/BDB-6103-13.jpeg" alt="Keerthi Chadalavada" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Keerthi Chadalavada&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/keerthi-chadalavada-a7344133/" target="_blank" rel="noopener"&gt;Keerthi&lt;/a&gt; is a Senior Software Development Engineer in the AWS analytics organization. She focuses on combining generative AI and data integration technologies to design and build comprehensive solutions for analytics and data engineering workloads.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Long-term system tables retention in Amazon Redshift with Amazon S3 Tables</title>
		<link>https://aws.amazon.com/blogs/big-data/long-term-system-tables-retention-in-amazon-redshift-with-amazon-s3-tables/</link>
		
		<dc:creator><![CDATA[Nidhi Nayak]]></dc:creator>
		<pubDate>Thu, 20 Aug 2026 19:20:20 +0000</pubDate>
				<category><![CDATA[Amazon Redshift]]></category>
		<category><![CDATA[Amazon S3 Tables]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Intermediate (200)]]></category>
		<guid isPermaLink="false">6cce5352b29522f5af66bb2b7fc8cc1d9e06d6a5</guid>

					<description>Amazon Redshift system table integration with Amazon S3 Tables automatically delivers your system table logs to Amazon S3 Tables in Apache Iceberg format. You can retain this data well beyond the 7-day limit for compliance, auditing, and cross-warehouse observability, without custom ETL pipelines or cluster resource consumption.</description>
										<content:encoded>&lt;p&gt;&lt;a href="https://aws.amazon.com/redshift/" target="_blank" rel="noopener"&gt;Amazon Redshift&lt;/a&gt; system tables capture a continuous stream of operational signals: every query that runs, every connection that is made. This data powers observability, performance analysis, and compliance auditing across your data warehouses. Until now, the system tables retained this critical data for only 7 days, making long-term compliance and auditing difficult without custom workarounds.&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/redshift/latest/dg/serverless_views-monitoring.html" target="_blank" rel="noopener"&gt;Amazon Redshift system table&lt;/a&gt; integration with &lt;a href="https://aws.amazon.com/s3/features/tables/" target="_blank" rel="noopener"&gt;Amazon S3 Tables&lt;/a&gt;, a capability of Amazon Simple Storage Service (&lt;a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener"&gt;Amazon S3&lt;/a&gt;), automatically delivers your system table logs data to Amazon S3 Tables and stores them in &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/apache-iceberg-on-aws/introduction.html" target="_blank" rel="noopener"&gt;Apache Iceberg&lt;/a&gt; format. You can configure retention periods for Amazon Redshift system table beyond the current 7-day limit, giving you extended compliance, auditing, and cross-warehouse observability without custom ETL pipelines or cluster resource consumption. Your data is open, durable, and queryable from Amazon Redshift, &lt;a href="https://aws.amazon.com/athena/" target="_blank" rel="noopener"&gt;Amazon Athena&lt;/a&gt;, &lt;a href="https://aws.amazon.com/glue/" target="_blank" rel="noopener"&gt;AWS Glue&lt;/a&gt;, &lt;a href="https://aws.amazon.com/emr/" target="_blank" rel="noopener"&gt;Amazon EMR&lt;/a&gt;, or other Apache Iceberg-compatible engines.&lt;/p&gt; 
&lt;p&gt;In this post, we walk through how the Amazon Redshift system table integration delivers log data to Amazon S3 Tables. This feature is supported on RA3 and RG provisioned clusters and Amazon Redshift Serverless workgroups.&lt;/p&gt; 
&lt;h1&gt;The challenge&lt;/h1&gt; 
&lt;p&gt;If you run Amazon Redshift, you often face operational challenges driven by the 7-day system table retention limit:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Limited query trend visibility: You want to compare how the same query performed 30 days ago compared to today. When performance shifts gradually, extended baselines enable data-driven root cause analysis rather than reactive troubleshooting.&lt;/li&gt; 
 &lt;li&gt;Enable before-and-after comparisons: When you add a new workload, change instance type, or adjust &lt;a href="https://docs.aws.amazon.com/redshift/latest/dg/c_workload_mngmt_classification.html" target="_blank" rel="noopener"&gt;Workload Management (WLM)&lt;/a&gt; queues, you want to measure the impact precisely. Extended retention preserves the baseline data you need.&lt;/li&gt; 
 &lt;li&gt;Unlock seasonal capacity planning: Month-end spikes, quarter-close surges, and annual peaks require months of historical data to identify and plan. Extended retention reveals seasonal patterns across months and years.&lt;/li&gt; 
 &lt;li&gt;Custom ETL pipeline overhead: To work around the retention limit, teams build custom pipelines that copy system table data hourly/daily into persistent tables within Amazon Redshift Managed Storage. These pipelines consume cluster resources, compete with production workloads, and require ongoing engineering maintenance. When Amazon Redshift updates system table schemas and data sharing configurations, these pipelines require manual intervention and create gaps in records.&lt;/li&gt; 
 &lt;li&gt;Compliance requirements: Regulated industries are required to maintain audit trails spanning months or years. The 7-day limit requires custom infrastructure to meet these requirements. Amazon S3 Tables integration for Amazon Redshift system tables now addresses this.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h1&gt;How it works&lt;/h1&gt; 
&lt;p&gt;Amazon Redshift system tables integration with Amazon S3 Tables is a fully managed capability that automatically writes Amazon Redshift system table data to Amazon S3 tables in Apache Iceberg format. AWS handles partitioning, compression, and retention management automatically. The log writing process runs in an isolated background process that alleviates resource contention with production workloads. AWS manages the pipelines for you.&lt;/p&gt; 
&lt;p&gt;The feature supports over 25 system views at launch – see the supported system views &lt;a href="http://docs.aws.amazon.com/redshift/latest/mgmt/system-table-s3-tables.html" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;h1&gt;Setting up&lt;/h1&gt; 
&lt;p&gt;Follow these steps to enable system table integration with Amazon S3 Tables from the Amazon Redshift console:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open the Amazon Redshift console and navigate to the System table integrations page. You can also access this from the detail page of your provisioned cluster or Serverless workgroup.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Create System table integration&lt;/strong&gt;. This launches the configuration wizard.&lt;/li&gt; 
 &lt;li&gt;Select the Amazon Redshift Provisioned cluster or Amazon Redshift Serverless workgroup that you want to enable the feature on. &lt;p&gt;&lt;/p&gt;
  &lt;div id="attachment_93718" style="width: 2001px" class="wp-caption alignleft"&gt;
   &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5977-Image-1.jpg" target="_blank" rel="noopener"&gt;&lt;img aria-describedby="caption-attachment-93718" loading="lazy" class="wp-image-93718 size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5977-Image-1.jpg" alt="" width="1991" height="810"&gt;&lt;/a&gt;
   &lt;p id="caption-attachment-93718" class="wp-caption-text"&gt;Figure 1: Selecting the Amazon Redshift data warehouse in the System table integration wizard&lt;/p&gt;
  &lt;/div&gt;&lt;/li&gt; 
 &lt;li&gt;Choose the system views to publish from the &lt;strong&gt;Available system tables&lt;/strong&gt; list. Select individual SYS_* views, or choose &lt;strong&gt;Select all supported system tables&lt;/strong&gt; to publish all current and future supported views. If you select all, new views added in the future are automatically included without requiring a configuration change. 
  &lt;div id="attachment_93717" style="width: 1972px" class="wp-caption alignleft"&gt;
   &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5977-Image-2.jpg" target="_blank" rel="noopener"&gt;&lt;img aria-describedby="caption-attachment-93717" loading="lazy" class="wp-image-93717 size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/21/BDB-5977-Image-2.jpg" alt="" width="1962" height="810"&gt;&lt;/a&gt;
   &lt;p id="caption-attachment-93717" class="wp-caption-text"&gt;Figure 2: Choosing the system views to publish from the Available system tables list&lt;/p&gt;
  &lt;/div&gt; 
  &lt;div class="mceTemp"&gt;&lt;/div&gt;&lt;/li&gt; 
 &lt;li&gt;Select the deployment model. Choose how data is organized in Amazon S3 Tables: 
  &lt;ul type="1"&gt; 
   &lt;li&gt;&lt;strong&gt;Individual S3 table per system table per data warehouse&lt;/strong&gt; to keep this warehouse’s data in its own set of tables.&lt;/li&gt; 
   &lt;li&gt;&lt;strong&gt;Shared S3 table per system table across data warehouses&lt;/strong&gt; to consolidate data from multiple warehouses in the account into a shared set of tables.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;ol start="6" type="1"&gt; 
 &lt;li&gt;Optionally configure encryption with an AWS Key Management Service (AWS KMS) customer managed key. By default, data is encrypted with Amazon S3-managed key (SSE-S3) encryption.&lt;/li&gt; 
 &lt;li&gt;Save your changes. Amazon Redshift begins publishing the selected views to Amazon S3 Tables and continues adding new records on a fixed frequency.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;To verify the integration is active:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Navigate to your cluster or workgroup detail page.&lt;/li&gt; 
 &lt;li&gt;Check the integration status and the last ingestion time for each view.&lt;/li&gt; 
 &lt;li&gt;You can also view the published data from the Amazon S3 Tables console.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;After it’s enabled, Amazon Redshift writes log data to Amazon S3 tables periodically through an isolated background process, separate from production workloads. To start querying the retained logs, you will need to perform a one-time setup that connects your Amazon Redshift environment to Amazon S3 Tables data through AWS Glue Catalog. Complete the following steps:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Set up an AWS Identity and Access Management (IAM) role with the necessary permissions for AWS Glue Data Catalog and Amazon S3 Tables access, then associate it with your Amazon Redshift cluster or Amazon Redshift serverless namespace.&lt;/li&gt; 
 &lt;li&gt;In AWS Glue Data Catalog, create a resource link that points to the Amazon S3 Tables database where your logs reside.&lt;/li&gt; 
 &lt;li&gt;In Amazon Redshift, create an external schema that references the resource link: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-sql"&gt;CREATE EXTERNAL SCHEMA &amp;lt;schema_name&amp;gt;
FROM DATA CATALOG
DATABASE '&amp;lt;resource_link_database&amp;gt;'
IAM_ROLE '&amp;lt;iam_role_arn&amp;gt;';&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
 &lt;li&gt;With this in place, you can query your historical system table data using familiar 2-part notation: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * FROM &amp;lt;schema_name&amp;gt;.&amp;lt;table_name&amp;gt;;&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Because access to Amazon S3 Tables is read-only, the integrity of your audit trails is inherently preserved.&lt;/p&gt; 
&lt;p&gt;For detailed setup instructions including IAM policy examples, see &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/enable-s3-tables-catalog-integration.html" target="_blank" rel="noopener"&gt;Registering the S3 Tables bucket with AWS Glue Data Catalog&lt;/a&gt;.&lt;/p&gt; 
&lt;h1&gt;Your data is now in Apache Iceberg&lt;/h1&gt; 
&lt;p&gt;Your system table data is stored in Apache Iceberg, an open table format, so you have the freedom to choose a compatible query engine. Your observability and auditing data works with the tool you already use.&lt;/p&gt; 
&lt;p&gt;You can analyze your operational data using:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Amazon Redshift: After the S3 table bucket is integrated with AWS Glue Data Catalog, create an external schema in Amazon Redshift pointing at the resource link to query the retained tables.&lt;/li&gt; 
 &lt;li&gt;Amazon Athena: Run serverless SQL queries against historical logs with zero infrastructure provisioning.&lt;/li&gt; 
 &lt;li&gt;AWS Glue: Build automated data processing and transformation jobs on top of your operational data.&lt;/li&gt; 
 &lt;li&gt;Amazon EMR: Run Spark-based analytics at scale for complex cross-warehouse analysis.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Because the data is stored in open Apache Iceberg format in Amazon S3 Tables, you can query it with Amazon Redshift, Amazon Athena, AI agent skills for natural-language queries, &lt;a href="https://aws.amazon.com/sagemaker/unified-studio/" target="_blank" rel="noopener"&gt;Amazon SageMaker Unified Studio&lt;/a&gt;, an Iceberg-compatible engine, business intelligence (BI) tools, and observability systems.&lt;/p&gt; 
&lt;h1&gt;Cost efficiency&lt;/h1&gt; 
&lt;p&gt;Log delivery from Amazon Redshift to Amazon S3 Tables incurs no additional cost. You only pay for Amazon S3 Tables storage, maintenance, and querying the data with the engine of your choice.&lt;/p&gt; 
&lt;h1&gt;Solution overview&lt;/h1&gt; 
&lt;p&gt;The following scenarios illustrate how Amazon Redshift system tables integration with Amazon S3 Tables addresses common operational, compliance, and observability challenges across your Amazon Redshift environment. We also built a dedicated skill, &lt;a href="https://github.com/aws/agent-toolkit-for-aws/tree/main/skills/specialized-skills/system-table-skills" target="_blank" rel="noopener"&gt;querying-aws-redshift&lt;/a&gt;, for this feature and embedded it into the AWS MCP Server so you can query Amazon Redshift system tables from Amazon S3 Tables.&lt;/p&gt; 
&lt;h2 id="scenario-1-track-query-trends-over-time"&gt;Scenario 1: Track query trends over time&lt;/h2&gt; 
&lt;p&gt;With months or years of &lt;code&gt;SYS_QUERY_HISTORY&lt;/code&gt; data retained, you can trace how individual queries perform over extended periods. You can compare execution time, queue time, and resource consumption for a query across days, weeks, or months.&lt;/p&gt; 
&lt;p&gt;You can pinpoint exactly when performance started degrading and correlate it with what changed: a new schema, a spike in data volume, or an additional concurrent workload. Extended retention turns troubleshooting into proactive, data-driven root cause analysis.&lt;/p&gt; 
&lt;h2 id="scenario-2-assess-workload-impact-before-and-after-changes"&gt;Scenario 2: Assess workload impact before and after changes&lt;/h2&gt; 
&lt;p&gt;Every workload change affects your system: a new ETL pipeline, an instance type change, a Workload Management (WLM) queue adjustment, or a new team of analysts running ad hoc queries. The question is always: how did this change affect performance?&lt;/p&gt; 
&lt;p&gt;With Amazon S3 Tables integration for Amazon Redshift system table, you can make data-driven decisions with confidence. Query &lt;code&gt;SYS_QUERY_HISTORY&lt;/code&gt; to compare execution times, queue wait durations, and concurrency scaling events from the weeks before a change versus the weeks after. If you onboarded a new reporting workload two weeks ago and want to understand its effect on existing queries, the data to confirm that is already there, with zero custom pipeline required.&lt;/p&gt; 
&lt;h2 id="scenario-3-build-observability-dashboards"&gt;Scenario 3: Build observability dashboards&lt;/h2&gt; 
&lt;p&gt;Your system table data is stored in Apache Iceberg and cataloged in AWS Glue, which means an observability or business intelligence (BI) tool that reads Apache Iceberg can connect directly to it. Visualize workload distribution trends in &lt;a href="https://aws.amazon.com/quicksight/" target="_blank" rel="noopener"&gt;Amazon Quick Sight&lt;/a&gt; for executive reporting. Use Amazon SageMaker Unified Studio for deeper analytical exploration or to power AI-driven insights from your operational data. Beyond AWS services, connect your preferred third-party observability systems and BI tools to track query volumes, monitor connection patterns, set up alerts for anomalies, or correlate Amazon Redshift operational data alongside application-level logs.&lt;/p&gt; 
&lt;p&gt;Your observability and auditing data works with tools that you already use. Direct access to durable, structured operational data, with a tool you prefer.&lt;/p&gt; 
&lt;h2 id="scenario-4-plan-capacity-with-seasonal-context"&gt;Scenario 4: Plan capacity with seasonal context&lt;/h2&gt; 
&lt;p&gt;Workload demand varies throughout the year. Month-end close, quarter-end reporting, annual planning cycles, and promotional events all create predictable usage spikes, but only if you have enough historical data to see the pattern.&lt;/p&gt; 
&lt;p&gt;With extended retention, you can analyze utilization trends across multiple business cycles. Identify when you consistently approach capacity limits, measure how demand shifts quarter over quarter, and validate whether your provisioned resources align with actual usage.&lt;/p&gt; 
&lt;h2 id="scenario-5-maintain-compliance-audit-trails"&gt;Scenario 5: Maintain compliance audit trails&lt;/h2&gt; 
&lt;p&gt;For regulated industries, extended retention delivers a fully managed audit trail with built-in integrity.&lt;/p&gt; 
&lt;p&gt;&lt;code&gt;SYS_CONNECTION_LOG&lt;/code&gt; records every authentication attempt. &lt;code&gt;SYS_USERLOG&lt;/code&gt; captures user account changes. &lt;code&gt;SYS_QUERY_HISTORY&lt;/code&gt; documents every query executed against your warehouse.&lt;/p&gt; 
&lt;p&gt;Configure retention to match your organization’s data retention policies: whether that is 90 days, one year, or multiple years. The read-only access policy helps prevent records from being altered after they are written, including by administrators.&lt;/p&gt; 
&lt;h2 id="scenario-6-centralize-fleet-observability-across-your-warehouse"&gt;Scenario 6: Centralize fleet observability across your warehouse&lt;/h2&gt; 
&lt;p&gt;If you run multiple Amazon Redshift warehouses, you benefit from a unified view of operational data. The feature supports two deployment patterns to match your organizational structure:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Individual tables per warehouse: Each warehouse writes to its own dedicated Amazon S3 tables, providing complete data isolation for compliance-sensitive environments. To query multiple warehouses, a &lt;code&gt;UNION&lt;/code&gt; operation is required.&lt;/li&gt; 
 &lt;li&gt;Shared tables: Warehouses across the same account and same AWS Region write to a single shared set of Amazon S3 tables, with data distinguished by the &lt;code&gt;warehouse_name&lt;/code&gt; column. Filter by warehouse for instant cross-cluster analysis.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h1&gt;Best practices&lt;/h1&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Identify warehouses with logs requiring isolation for privacy reasons and select the &lt;strong&gt;individual table per warehouse&lt;/strong&gt; option for those. For the remaining warehouses, use the &lt;strong&gt;Shared tables (consolidated)&lt;/strong&gt; option for ease of management.&lt;/li&gt; 
 &lt;li&gt;Align retention duration with your compliance requirements. Configure the minimum retention period that satisfies your compliance requirements to reduce storage costs.&lt;/li&gt; 
 &lt;li&gt;When querying retained system tables, filter on metadata columns such as &lt;code&gt;warehouse_account_id&lt;/code&gt;, &lt;code&gt;warehouse_region_name&lt;/code&gt;, &lt;code&gt;warehouse_namespace_arn&lt;/code&gt;, &lt;code&gt;warehouse_name&lt;/code&gt;, and &lt;code&gt;s3_tables_ingestion_time&lt;/code&gt; to reduce scan scope and improve performance. This is particularly important when querying large volumes of historical data across multiple warehouses.&lt;/li&gt; 
 &lt;li&gt;Rely on the built-in read-only access for audit trail integrity. Use the Amazon S3 Tables configuration APIs to manage retention and encryption settings.&lt;/li&gt; 
 &lt;li&gt;Plan your encryption strategy early. Choose your encryption key carefully at setup, as changes require recreating the integration. If you anticipate consolidating warehouses in the future, choose a shared AWS KMS key from the start.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h1&gt;Conclusion&lt;/h1&gt; 
&lt;p&gt;Amazon Redshift system table integration with Amazon S3 Tables replaces custom ETL pipelines with a fully managed solution to preserve your Amazon Redshift operational data. With automatic Apache Iceberg-based storage, open format queryability, and built-in audit integrity, you get months or years of observability data, fully managed. You can enable it through the &lt;a href="https://aws.amazon.com/console/" target="_blank" rel="noopener"&gt;AWS Management Console&lt;/a&gt;, &lt;a href="https://aws.amazon.com/cli/" target="_blank" rel="noopener"&gt;AWS Command Line Interface (AWS CLI)&lt;/a&gt;, or &lt;a href="https://aws.amazon.com/developer/tools/" target="_blank" rel="noopener"&gt;AWS SDKs&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;To learn more, visit the &lt;a href="https://docs.aws.amazon.com/redshift/latest/dg/cm_chap_system-tables.html" target="_blank" rel="noopener"&gt;Amazon Redshift system tables documentation&lt;/a&gt;.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/14/BDB-5977-3.jpeg" alt="Nidhi Nayak" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Nidhi Nayak&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/nidhi-nayak-51511b21/" target="_blank" rel="noopener"&gt;Nidhi&lt;/a&gt; is a Senior Technical Account Manager with AWS, she helps enterprise customers build scalable, high-performance cloud applications and optimize cloud operations. With over a decade of experience in Data Analytics, Nidhi currently focuses on Redshift &amp;amp; Generative AI integration with Redshift.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/14/BDB-5977-4.jpeg" alt="Raza Hafeez" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Raza Hafeez&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/raza-hafeez/" target="_blank" rel="noopener"&gt;Raza&lt;/a&gt; is a Senior Product Manager, Technical at Amazon Redshift. He has 15+ years of experience building and optimizing enterprise data warehouses and is passionate about making cloud analytics accessible and cost-effective for customers of all sizes.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full wp-image-93693" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/20/BDB-5977-5-1.png" alt="" width="120" height="151"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Shubham Purwar&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/shubham-purwar/" target="_blank" rel="noopener"&gt;Shubham&lt;/a&gt; is an AWS Analytics Specialist Solution Architect. He helps organizations unlock the full potential of their data by designing and implementing scalable, secure, and high-performance analytics solutions on the AWS platform. With deep expertise in AWS analytics services, he collaborates with customers to uncover their distinct business requirements and create customized solutions that deliver actionable insights and drive business growth. In his free time, Shubham loves to spend time with his family and travel around the world.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/14/BDB-5977-6.png" alt="Amrita Singh" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Amrita Singh&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/amrita-singh-2095241a" target="_blank" rel="noopener"&gt;Amrita&lt;/a&gt; is a Senior Technical Account Manager at AWS, based in Salt Lake City, USA. She specializes in Amazon Redshift, helping enterprise customers optimize their data warehouse environments for performance, scalability, and cost efficiency. Amrita works directly with AWS customers to provide guidance and technical assistance on their cloud journeys, helping them achieve higher flexibility, scale, and resiliency with AWS services.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Track SageMaker Unified Studio project costs with custom tags and AWS CUR</title>
		<link>https://aws.amazon.com/blogs/big-data/track-sagemaker-unified-studio-project-costs-with-custom-tags-and-aws-cur/</link>
		
		<dc:creator><![CDATA[Nisha Gambhir]]></dc:creator>
		<pubDate>Thu, 20 Aug 2026 16:19:24 +0000</pubDate>
				<category><![CDATA[Amazon SageMaker Unified Studio]]></category>
		<category><![CDATA[Intermediate (200)]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<guid isPermaLink="false">a594886f66c23caa007a503c2a37a17afc2f9612</guid>

					<description>Learn how to track Amazon SageMaker Unified Studio project costs by custom tags. This serverless solution enriches AWS Cost and Usage Report (CUR) data with custom project tags and visualizes cost by CostCenter, Team, or Environment in an Amazon Quick Sight dashboard.</description>
										<content:encoded>&lt;p&gt;Organizations running machine learning (ML), analytics, and generative AI workloads on &lt;a href="https://aws.amazon.com/sagemaker/unified-studio/" target="_blank" rel="noopener"&gt;Amazon SageMaker Unified Studio&lt;/a&gt; domains and projects face a common cost governance challenge. System tags (&lt;code&gt;AmazonDataZoneDomainId&lt;/code&gt; and &lt;code&gt;AmazonDataZoneProject&lt;/code&gt;) are automatically propagated to all underlying project resources. However, custom tags such as CostCenter, Team, or Environment are not propagated to dynamic resources created through the Studio UI. This creates a gap when you need to report project costs grouped by custom tags.&lt;/p&gt; 
&lt;p&gt;In this post, we walk through a serverless solution that bridges this gap by enriching &lt;a href="https://aws.amazon.com/aws-cost-management/aws-cost-and-usage-reporting/" target="_blank" rel="noopener"&gt;AWS Cost and Usage Report (CUR)&lt;/a&gt; data with custom project tags. By the end of this post, you can build an &lt;a href="https://aws.amazon.com/quicksight/" target="_blank" rel="noopener"&gt;Amazon Quick Sight&lt;/a&gt; dashboard to filter and analyze Amazon SageMaker Unified Studio project costs by any custom tag dimension that you define. This gives your team the visibility to make informed spending decisions.&lt;/p&gt; 
&lt;h2 id="solution-overview"&gt;Solution overview&lt;/h2&gt; 
&lt;p&gt;The solution consists of three automated subsystems:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Event-driven tag lookup management – An &lt;a href="https://aws.amazon.com/eventbridge/" target="_blank" rel="noopener"&gt;Amazon EventBridge&lt;/a&gt; rule captures &lt;a href="https://aws.amazon.com/datazone/" target="_blank" rel="noopener"&gt;Amazon DataZone&lt;/a&gt; project lifecycle events (Create, Update, Delete) and triggers an &lt;a href="https://aws.amazon.com/lambda/" target="_blank" rel="noopener"&gt;AWS Lambda&lt;/a&gt; function. The function maintains an &lt;a href="https://aws.amazon.com/dynamodb/" target="_blank" rel="noopener"&gt;Amazon DynamoDB&lt;/a&gt; lookup table that maps each project’s DomainId and ProjectId to its custom tags.&lt;/li&gt; 
 &lt;li&gt;CUR enrichment pipeline – An &lt;a href="https://aws.amazon.com/glue/" target="_blank" rel="noopener"&gt;AWS Glue&lt;/a&gt; extract, transform, and load (ETL) job reads CUR 2.0 Parquet data from &lt;a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener"&gt;Amazon Simple Storage Service (Amazon S3)&lt;/a&gt;. The job joins each billing line item with the DynamoDB lookup table using the system tags (DomainId, ProjectId), appends the custom tag values as new columns, and writes the enriched data back to Amazon S3.&lt;/li&gt; 
 &lt;li&gt;Cost visualization – An Amazon Quick Sight dashboard backed by a custom SQL dataset over &lt;a href="https://aws.amazon.com/athena/" target="_blank" rel="noopener"&gt;Amazon Athena&lt;/a&gt; provides interactive cost and consumption analytics filtered by custom tags.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt; 
&lt;p&gt;The following diagram shows the end-to-end architecture:&lt;/p&gt; 
&lt;div id="attachment_93543" style="width: 1136px" class="wp-caption aligncenter"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/bdb-5972-arch.png" target="_blank" rel="noopener"&gt;&lt;img aria-describedby="caption-attachment-93543" loading="lazy" class="wp-image-93543 size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/17/bdb-5972-arch.png" alt="" width="1126" height="448"&gt;&lt;/a&gt;
 &lt;p id="caption-attachment-93543" class="wp-caption-text"&gt;Figure 1: SageMaker Unified Studio project custom tag cost reporting&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The workflow is as follows:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An Amazon SageMaker Unified Studio administrator creates or updates a project with custom tags.&lt;/li&gt; 
 &lt;li&gt;AWS CloudTrail captures the API call.&lt;/li&gt; 
 &lt;li&gt;Amazon EventBridge matches the event.&lt;/li&gt; 
 &lt;li&gt;The Lambda orchestrator writes the tag mapping to DynamoDB.&lt;/li&gt; 
 &lt;li&gt;Separately, AWS Data Exports delivers CUR data to Amazon S3.&lt;/li&gt; 
 &lt;li&gt;The AWS Glue ETL job enriches CUR line items with custom tags from DynamoDB.&lt;/li&gt; 
 &lt;li&gt;The AWS Glue Crawler catalogs the enriched data.&lt;/li&gt; 
 &lt;li&gt;Amazon Quick Sight visualizes costs by custom tags.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;p&gt;Before deploying this solution, you need:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An Amazon SageMaker Unified Studio domain (you create projects after deployment).&lt;/li&gt; 
 &lt;li&gt;AWS Cloud Development Kit (AWS CDK) CLI installed.&lt;/li&gt; 
 &lt;li&gt;Python 3.12+.&lt;/li&gt; 
 &lt;li&gt;Amazon Quick Sight Enterprise edition enabled in your account.&lt;/li&gt; 
 &lt;li&gt;An AWS Identity and Access Management (IAM) user or role with permissions to deploy AWS CloudFormation stacks.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="step-1-configure-custom-tags-on-your-project-profile"&gt;Step 1: Configure custom tags on your project profile&lt;/h2&gt; 
&lt;p&gt;You configure custom tags on project profiles through the Amazon DataZone API. First, enable custom tags on your project profile:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws datazone update-project-profile \
  --domain-identifier $DOMAIN_ID \
  --identifier $PROJECT_PROFILE_ID \
  --region $REGION \
  --allow-custom-project-resource-tags \
  --project-resource-tags '[
  {"key": "CostCenter", "value": "default", "isValueEditable": true},
  {"key": "Team", "value": "default", "isValueEditable": true},
  {"key": "Environment", "value": "default", "isValueEditable": true}
]'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;When creating or updating a project, set the tag values:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws datazone update-project \
  --domain-identifier $DOMAIN_ID \
  --identifier $PROJECT_ID \
  --project-profile-version latest \
  --region $REGION \
  --resource-tags '{"CostCenter": "CC-100", "Team": "ML-Platform", "Environment": "Production"}'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The &lt;code&gt;AmazonSageMakerProvisioning-&amp;lt;domainAccountId&amp;gt;&lt;/code&gt; role needs an inline policy that permits your custom tag keys. Without this, project environment deployment fails.&lt;/p&gt; 
&lt;p&gt;The following is the inline policy that’s used for the custom tags shared in this post:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCustomTagKeys",
      "Effect": "Allow",
      "Action": [
        "sagemaker:AddTags",
        "sagemaker:DeleteTags",
        "cloudformation:TagResource",
        "cloudformation:CreateStack",
        "cloudformation:UpdateStack"
      ],
      "Resource": "*",
      "Condition": {
        "ForAnyValue:StringLike": {
          "aws:TagKeys": [
            "AmazonDataZone*",
            "CostCenter",
            "Team",
            "Environment"
          ]
        }
      }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="step-2-activate-cost-allocation-tags"&gt;Step 2: Activate cost allocation tags&lt;/h2&gt; 
&lt;p&gt;Activate the SageMaker Unified Studio system tags as cost allocation tags so they appear in CUR data:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws ce update-cost-allocation-tags-status \
  --cost-allocation-tags-status '[
  {"TagKey": "AWSDataZoneProject", "Status": "Active"},
  {"TagKey": "AmazonDataZoneDomainId", "Status": "Active"}
]'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;These tags take up to &lt;a href="https://docs.aws.amazon.com/cur/latest/userguide/what-is-cur.html#cur-timeline" target="_blank" rel="noopener"&gt;24 hours&lt;/a&gt; to start appearing in CUR reports after activation.&lt;/p&gt; 
&lt;h2 id="step-3-deploy-the-infrastructure"&gt;Step 3: Deploy the infrastructure&lt;/h2&gt; 
&lt;p&gt;The solution is packaged as a CDK application. Clone the &lt;a href="https://github.com/aws-samples/sample-smus-custom-tags-cost-reporting" target="_blank" rel="noopener"&gt;GitHub repository&lt;/a&gt; and deploy:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# Install dependencies
pip install -r requirements.txt

# Bootstrap CDK (first time only)
cdk bootstrap aws://$ACCOUNT_ID/$REGION

# Deploy
cdk deploy&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;This creates the following resources:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;DynamoDB table (smus-project-tag-lookup) – stores project-to-tag mappings.&lt;/li&gt; 
 &lt;li&gt;Lambda function (smus-orchestrator) – processes project lifecycle events.&lt;/li&gt; 
 &lt;li&gt;Amazon EventBridge rule – matches Amazon DataZone &lt;code&gt;CreateProject/UpdateProject/DeleteProject&lt;/code&gt; events.&lt;/li&gt; 
 &lt;li&gt;S3 buckets – for raw CUR and enriched CUR data.&lt;/li&gt; 
 &lt;li&gt;AWS Glue ETL job (smus-cur-enrichment) – enriches CUR with custom tags.&lt;/li&gt; 
 &lt;li&gt;AWS Glue Crawler – catalogs enriched data.&lt;/li&gt; 
 &lt;li&gt;Amazon Simple Notification Service (Amazon SNS) topic – pipeline failure alerts.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The solution uses serverless components (Lambda, DynamoDB on-demand, AWS Glue, Amazon Quick Sight), so you only pay for what you use. The primary cost drivers are AWS Glue ETL job execution time and Amazon Quick Sight SPICE storage.&lt;/p&gt; 
&lt;h2 id="step-4-configure-cur-delivery"&gt;Step 4: Configure CUR delivery&lt;/h2&gt; 
&lt;p&gt;Create a CUR 2.0 export through AWS Data Exports that delivers Parquet files to the CUR S3 bucket created by the stack. The following screenshots show the complete configuration process in the AWS Billing and Cost Management console.&lt;/p&gt; 
&lt;p&gt;To create the export, follow these steps:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Go to AWS Billing and Cost Management and then choose &lt;strong&gt;Data Exports&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Create&lt;/strong&gt; in the upper right corner of the Exports and dashboards page. The Data Exports console shows any existing exports, their status, export type, data table, and last refresh date.&lt;/li&gt; 
 &lt;li&gt;On the Create export page, under &lt;strong&gt;Export details&lt;/strong&gt;, select &lt;strong&gt;Standard data export&lt;/strong&gt; and enter an export name. Under &lt;strong&gt;Data table content settings&lt;/strong&gt;, select &lt;strong&gt;CUR 2.0&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Under &lt;strong&gt;Data table configurations&lt;/strong&gt;, set &lt;strong&gt;Time granularity&lt;/strong&gt; to &lt;strong&gt;Hourly&lt;/strong&gt;. The configuration page also lets you choose additional export content options such as including resource IDs, split cost allocation data, caller identity allocation data, and capacity reservation columns.&lt;/li&gt; 
 &lt;li&gt;Under &lt;strong&gt;Data export delivery options&lt;/strong&gt;, set &lt;strong&gt;Compression type and file format&lt;/strong&gt; to &lt;strong&gt;Parquet&lt;/strong&gt;. Under &lt;strong&gt;Data export storage settings&lt;/strong&gt;, configure the S3 bucket to: smus-cur-report-{account-id}-{region} and set the S3 path prefix as needed. Choose &lt;strong&gt;Create&lt;/strong&gt; to finish.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;div style="width: 810px" class="wp-caption aligncenter"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-2-2.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-2-2.png" alt="Data Exports console listing existing exports with status, type, and last refresh date" width="800" height="1191"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Data Exports page listing existing exports&lt;/p&gt;
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption aligncenter"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-3-2.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-3-2.png" alt="Create export page with Standard data export selected and CUR 2.0 chosen" width="800" height="1154"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: Create export page with Standard data export and CUR 2.0 selected&lt;/p&gt;
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption aligncenter"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-4-2.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-4-2.png" alt="Data table configurations with time granularity set to Hourly" width="800" height="1619"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 4: Data table configurations with time granularity set to Hourly&lt;/p&gt;
&lt;/div&gt; 
&lt;div style="width: 810px" class="wp-caption aligncenter"&gt;
 &lt;a href="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-5-2.png" target="_blank" rel="noopener"&gt;&lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-5-2.png" alt="Data export delivery options with Parquet format and the S3 storage destination configured" width="800" height="1827"&gt;&lt;/a&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 5: Data export delivery options with Parquet format and S3 storage settings&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="step-5-how-the-event-driven-tag-capture-works"&gt;Step 5: How the event-driven tag capture works&lt;/h2&gt; 
&lt;p&gt;When a project is created or updated in Amazon SageMaker Unified Studio (through the Studio UI or API), the following happens automatically:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;CloudTrail logs the Amazon DataZone API call.&lt;/li&gt; 
 &lt;li&gt;Amazon EventBridge matches the event.&lt;/li&gt; 
 &lt;li&gt;Amazon EventBridge invokes the Lambda function.&lt;/li&gt; 
 &lt;li&gt;The Lambda extracts custom tags from the CloudTrail event payload.&lt;/li&gt; 
 &lt;li&gt;The Lambda writes a record to DynamoDB with the DomainId, ProjectId, and all custom tag key-value pairs.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;The Lambda function reads tags directly from the &lt;code&gt;responseElements.resourceTags&lt;/code&gt; field of the CloudTrail event rather than making a separate &lt;code&gt;GetProject&lt;/code&gt; API call. This avoids a race condition where &lt;code&gt;GetProject&lt;/code&gt; might return empty tags while the project is in the &lt;code&gt;UPDATING&lt;/code&gt; state.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;def _extract_tags_from_event(detail):
    tags = {}
    response_elements = detail.get("responseElements") or {}
    for tag_entry in response_elements.get("resourceTags", []):
        if isinstance(tag_entry, dict) and "key" in tag_entry:
            tags[tag_entry["key"]] = tag_entry["value"]
    request_params = detail.get("requestParameters") or {}
    req_tags = request_params.get("resourceTags", {})
    if isinstance(req_tags, dict):
        tags.update(req_tags)
    return tags&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="step-6-how-the-cur-enrichment-works"&gt;Step 6: How the CUR enrichment works&lt;/h2&gt; 
&lt;p&gt;The AWS Glue ETL job runs on a schedule (after each CUR delivery):&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Reads CUR Parquet files from the CUR S3 bucket.&lt;/li&gt; 
 &lt;li&gt;Reads all records from the DynamoDB lookup table.&lt;/li&gt; 
 &lt;li&gt;Performs a left outer join on DomainId and ProjectId.&lt;/li&gt; 
 &lt;li&gt;Appends custom tag columns (CostCenter, Team, Environment, and so on) to each CUR line item.&lt;/li&gt; 
 &lt;li&gt;Writes enriched Parquet to the enriched S3 bucket.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Line items without a matching project in the lookup table retain all original columns with &lt;code&gt;NULL&lt;/code&gt; custom tag values. No data is dropped.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-python"&gt;joined_df = cur_df.join(
    lookup_df,
    on=(
        (cur_df[DOMAIN_COL] == lookup_df["domainId"])
        &amp;amp; (cur_df[PROJECT_COL] == lookup_df["projectId"])
    ),
    how="left_outer",
)&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="step-7-set-up-the-amazon-quick-sight-dashboard"&gt;Step 7: Set up the Amazon Quick Sight dashboard&lt;/h2&gt; 
&lt;p&gt;After the first ETL run and crawler execution, set up the Amazon Quick Sight dashboard:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;python scripts/setup_quicksight.py \
  --account-id $ACCOUNT_ID \
  --region $REGION \
  --quicksight-user $QUICKSIGHT_USER_ARN&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;This creates a dashboard with five visuals:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Cost by Custom Tag (CostCenter) – horizontal bar chart.&lt;/li&gt; 
 &lt;li&gt;Cost by Project – horizontal bar chart.&lt;/li&gt; 
 &lt;li&gt;Daily Cost Trend – line chart.&lt;/li&gt; 
 &lt;li&gt;Cost by Service per Project – stacked bar chart.&lt;/li&gt; 
 &lt;li&gt;Usage by Project &amp;amp; Service – summary table.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;And six interactive list filters: Domain, Project, CostCenter, Team, Environment, Service.&lt;/p&gt; 
&lt;p&gt;The custom SQL includes a CASE statement for service categorization:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT
  line_item_usage_start_date,
  line_item_product_code,
  line_item_usage_amount,
  line_item_unblended_cost,
  resource_tags_user_amazondatazone_domain_id AS domain_id,
  resource_tags_user_amazondatazone_project AS project_id,
  costcenter, team, environment,
  CASE
    WHEN line_item_product_code = 'AmazonSageMaker' THEN 'SageMaker'
    WHEN line_item_product_code = 'AmazonS3' THEN 'S3'
    WHEN line_item_product_code = 'AWSGlue' THEN 'Glue'
    ELSE line_item_product_code
  END AS service_category
FROM "smus_cost_reporting"."enriched_cur"
WHERE line_item_unblended_cost &amp;gt; 0&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="step-8-verifying-the-solution"&gt;Step 8: Verifying the solution&lt;/h2&gt; 
&lt;p&gt;After deploying the infrastructure and setting up the dashboard, verify that each component of the pipeline is functioning correctly.&lt;/p&gt; 
&lt;h3 id="verify-amazon-eventbridge-is-capturing-project-events"&gt;8.1 Verify Amazon EventBridge is capturing project events&lt;/h3&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open the Amazon EventBridge console.&lt;/li&gt; 
 &lt;li&gt;In the navigation pane, choose &lt;strong&gt;Rules&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Select the rule created by the CDK stack (for example, &lt;code&gt;SmusCostReporting-ProjectTagRule&lt;/code&gt;).&lt;/li&gt; 
 &lt;li&gt;Choose the &lt;strong&gt;Monitoring&lt;/strong&gt; tab.&lt;/li&gt; 
 &lt;li&gt;Confirm that the invocations are being recorded in the metrics.&lt;/li&gt; 
 &lt;li&gt;Create or update an Amazon SageMaker Unified Studio project with custom tags using the following command: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-bash"&gt;aws datazone update-project \
  --domain-identifier &amp;lt;domain-id&amp;gt; \
  --identifier &amp;lt;project-id&amp;gt; \
  --custom-tags CostCenter=Engineering Team=DataPlatform Environment=Production&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
 &lt;li&gt;Within a few seconds, the Amazon EventBridge rule should show a new invocation in its metrics.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="verify-dynamodb-schema-and-tag-mappings"&gt;8.2 Verify DynamoDB schema and tag mappings&lt;/h3&gt; 
&lt;p&gt;The DynamoDB lookup table uses a simple key schema:&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Attribute&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Role&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;domainId&lt;/td&gt; 
   &lt;td&gt;String&lt;/td&gt; 
   &lt;td&gt;Partition Key&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;projectId&lt;/td&gt; 
   &lt;td&gt;String&lt;/td&gt; 
   &lt;td&gt;Sort Key&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;CostCenter&lt;/td&gt; 
   &lt;td&gt;String&lt;/td&gt; 
   &lt;td&gt;Custom tag&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Team&lt;/td&gt; 
   &lt;td&gt;String&lt;/td&gt; 
   &lt;td&gt;Custom tag&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Environment&lt;/td&gt; 
   &lt;td&gt;String&lt;/td&gt; 
   &lt;td&gt;Custom tag&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;Custom tags are stored as dynamic attributes. Any tag key set on a project becomes a column in the table.&lt;/p&gt; 
&lt;h4 id="verify-dynamodb-table-contains-tag-mappings"&gt;8.2.1 Verify DynamoDB table contains tag mappings&lt;/h4&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open the DynamoDB console.&lt;/li&gt; 
 &lt;li&gt;Navigate to the table created by the stack (for example, &lt;code&gt;SmusCostReporting-ProjectTagsTable&lt;/code&gt;).&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Explore table items&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Scan for your project with the following keys: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-plaintext"&gt;Partition key (domainId): &amp;lt;your-domain-id&amp;gt;
Sort key (projectId): &amp;lt;your-project-id&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
 &lt;li&gt;Confirm the item contains the expected custom tag attributes (CostCenter, Team, Environment) with the values you assigned.&lt;/li&gt; 
 &lt;li&gt;Alternatively, use the AWS CLI: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-bash"&gt;aws dynamodb get-item \
  --table-name SmusCostReporting-ProjectTagsTable \
  --key '{"domainId": {"S": "&amp;lt;domain-id&amp;gt;"}, "projectId": {"S": "&amp;lt;project-id&amp;gt;"}}'&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="verify-the-aws-glue-etl-job-enriches-cur-data"&gt;8.3 Verify the AWS Glue ETL job enriches CUR data&lt;/h3&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Wait for the next CUR delivery (hourly if configured as described in Step 4).&lt;/li&gt; 
 &lt;li&gt;Wait for the subsequent AWS Glue job execution.&lt;/li&gt; 
 &lt;li&gt;Open the AWS Glue console.&lt;/li&gt; 
 &lt;li&gt;In the navigation pane, choose &lt;strong&gt;ETL Jobs&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Confirm the job completed successfully (status: &lt;strong&gt;Succeeded&lt;/strong&gt;).&lt;/li&gt; 
 &lt;li&gt;Query the enriched data in Amazon Athena to confirm custom tag columns are populated: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT
  line_item_usage_start_date,
  line_item_product_code,
  line_item_unblended_cost,
  costcenter,
  team,
  environment
FROM "smus_cost_reporting"."enriched_cur"
WHERE costcenter IS NOT NULL
LIMIT 10;&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;You should see rows with your custom tag values populated in the &lt;code&gt;costcenter&lt;/code&gt;, &lt;code&gt;team&lt;/code&gt;, and &lt;code&gt;environment&lt;/code&gt; columns.&lt;/p&gt; 
&lt;h3 id="verify-the-amazon-quick-sight-dashboard-displays-enriched-data"&gt;8.4 Verify the Amazon Quick Sight dashboard displays enriched data&lt;/h3&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open the Amazon Quick Sight console and navigate to the dashboard created by the setup script.&lt;/li&gt; 
 &lt;li&gt;Confirm that: 
  &lt;ul&gt; 
   &lt;li&gt;The &lt;strong&gt;Cost by Custom Tag (CostCenter)&lt;/strong&gt; bar chart displays cost data grouped by your CostCenter values.&lt;/li&gt; 
   &lt;li&gt;The list filters for &lt;strong&gt;CostCenter&lt;/strong&gt;, &lt;strong&gt;Team&lt;/strong&gt;, and &lt;strong&gt;Environment&lt;/strong&gt; contain selectable values.&lt;/li&gt; 
   &lt;li&gt;Selecting a filter value correctly narrows the displayed data.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
 &lt;li&gt;If the dashboard shows no data, verify that: 
  &lt;ul&gt; 
   &lt;li&gt;The AWS Glue Crawler has run after the ETL job (check the crawler’s last run status in the AWS Glue console).&lt;/li&gt; 
   &lt;li&gt;The SPICE dataset has been refreshed. In the Amazon Quick Sight console, navigate to &lt;strong&gt;Datasets&lt;/strong&gt;, select the dataset, and then choose &lt;strong&gt;Refresh now&lt;/strong&gt;.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Figure 6 shows the Amazon Quick Sight dashboard with two side-by-side horizontal bar charts: Cost by Cost Center and Cost by Project. Domain Name and Project Name list filters appear at the top.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption aligncenter"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-6-2.png" alt="Amazon Quick Sight dashboard with Cost by Cost Center and Cost by Project bar charts and Domain and Project filters" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 6: Amazon Quick Sight dashboard showing cost data by custom tags, including Cost by Cost Center and Cost by Project bar charts with Domain Name and Project Name filters&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The first end-to-end cycle can take up to 48 hours depending on CUR delivery timing. After the initial cycle completes, subsequent updates will flow automatically on the configured schedule.&lt;/p&gt; 
&lt;h2 id="operational-considerations"&gt;Operational considerations&lt;/h2&gt; 
&lt;p&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt; The Amazon SNS topic &lt;code&gt;smus-cost-reporting-alerts&lt;/code&gt; receives notifications when the AWS Glue ETL job fails or the Lambda orchestrator encounters repeated errors. Subscribe an email address or Slack webhook to stay informed. For instructions on how to create a subscription, see &lt;a href="https://docs.aws.amazon.com/sns/latest/dg/sns-create-subscribe-endpoint-to-topic.html" target="_blank" rel="noopener"&gt;Subscribing to an Amazon SNS topic&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; The solution uses serverless components (Lambda, DynamoDB on-demand, AWS Glue, Amazon Quick Sight, SPICE) so you only pay for what you use. The primary cost drivers are AWS Glue ETL job execution time and Amazon Quick Sight SPICE storage.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Scaling:&lt;/strong&gt; The DynamoDB table uses on-demand capacity and can scale to accommodate your projects. You can scale the AWS Glue ETL job by increasing the number of workers for larger CUR datasets. For more information, see &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.html" target="_blank" rel="noopener"&gt;Managing throughput capacity automatically with DynamoDB auto scaling&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;New tag keys:&lt;/strong&gt; When you add new custom tag keys to projects, the ETL automatically picks them up as new columns. The AWS Glue Crawler’s &lt;code&gt;UPDATE_IN_DATABASE&lt;/code&gt; policy adds new columns to the catalog table without manual intervention.&lt;/p&gt; 
&lt;h2 id="cleanup"&gt;Cleanup&lt;/h2&gt; 
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The following cleanup steps will permanently delete all CUR data, project tag mappings, and Amazon Quick Sight dashboards.&lt;/p&gt; 
&lt;p&gt;To remove all resources:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;# Delete Amazon Quick Sight resources
python scripts/setup_quicksight.py --account-id $ACCOUNT_ID --region $REGION --quicksight-user $QS_USER --clean

# Delete CDK stack
cdk destroy&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Go to AWS Billing and Cost Management, and then choose Data Exports and delete the CUR 2.0 export created in Step 4.&lt;/p&gt; 
&lt;p&gt;Deactivate the cost allocation tags that were activated in Step 2:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws ce update-cost-allocation-tags-status \
  --cost-allocation-tags-status '[
  {"TagKey": "AWSDataZoneProject", "Status": "Inactive"},
  {"TagKey": "AmazonDataZoneDomainId", "Status": "Inactive"}
]'&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;In this post, we showed how to build an end-to-end cost reporting solution for Amazon SageMaker Unified Studio projects using custom tags. This solution combines tag capture driven by Amazon EventBridge, CUR enrichment through AWS Glue ETL, and visualization in Amazon Quick Sight. With it, organizations can track and attribute costs by CostCenter, Team, Environment, or any custom dimension. This works even for resources created through the Studio UI that don’t receive custom tag propagation.&lt;/p&gt; 
&lt;p&gt;This solution serves as an extension to the custom tag propagation feature and reports cost for all project resources. The architecture is fully serverless, automated, and can be deployed to any AWS account using the provided CDK application.&lt;/p&gt; 
&lt;p&gt;To start building your custom tag cost reporting pipeline, visit the &lt;a href="https://github.com/aws-samples/sample-smus-custom-tags-cost-reporting" target="_blank" rel="noopener"&gt;GitHub repository&lt;/a&gt;. To learn more about the underlying services, visit the &lt;a href="https://aws.amazon.com/sagemaker/unified-studio/" target="_blank" rel="noopener"&gt;Amazon SageMaker Unified Studio service page&lt;/a&gt;. For a related approach to custom tag governance, see &lt;a href="https://aws.amazon.com/blogs/big-data/use-amazon-sagemaker-custom-tags-for-project-resource-governance-and-cost-tracking/" target="_blank" rel="noopener"&gt;Use Amazon SageMaker custom tags for project resource governance and cost tracking&lt;/a&gt;&lt;/p&gt; 
&lt;h2 id="references"&gt;References&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/big-data/use-amazon-sagemaker-custom-tags-for-project-resource-governance-and-cost-tracking/" target="_blank" rel="noopener"&gt;Use Amazon SageMaker custom tags for project resource governance and cost tracking&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/machine-learning/automate-modelops-with-sagemaker-unified-studio-part-2-technical-implementation/" target="_blank" rel="noopener"&gt;Automate ModelOps with Amazon SageMaker Unified Studio, Part 2&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/business-intelligence/visualize-amazon-quicksight-costs-using-aws-cur-and-cost-allocation-tags/" target="_blank" rel="noopener"&gt;Visualize Amazon Quick Sight costs using AWS CUR and cost allocation tags&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cur/latest/userguide/cur-s3.html" target="_blank" rel="noopener"&gt;AWS Cost and Usage Reports User Guide&lt;/a&gt;&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p style="clear:both"&gt;&lt;/p&gt; 
&lt;hr style="width: 100%"&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-8-1.png" alt="Nisha Gambhir" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Nisha Gambhir&lt;/h3&gt; 
  &lt;p&gt;Nisha is a Senior AI/ML &amp;amp; Cloud Architect based out of India. She is passionate about helping customers design, architect and develop secure, scalable and reliable applications using AI/ML and Agentic AI. She loves working on latest technologies, providing simple and scalable solutions that drive positive business outcomes.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-9-1.png" alt="Dr Anil Giri" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Dr Anil Giri&lt;/h3&gt; 
  &lt;p&gt;Anil is a Solutions Architect at AWS, based in London, UK, where he helps ISV customers design and deploy agentic AI systems in production. He specializes in multi-agent orchestration, retrieval-augmented generation, and event-driven serverless architectures on Amazon Bedrock, with a focus on building reliable, secure, and scalable solutions that deliver measurable business outcomes.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-10.png" alt="Satish Sarapuri" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Satish Sarapuri&lt;/h3&gt; 
  &lt;p style="text-align: left"&gt;Satish is a Sr. Data Architect, Data Mesh / Data Lake/Gen AI at AWS. He helps enterprise-level customers build high-performance, highly available, cost-effective, resilient, and secure generative AI, data mesh, data lake, and analytics platform solutions on AWS, through which customers can make data-driven decisions to gain impactful outcomes for their business and help them on their digital and data transformation journey. In his spare time, he enjoys trail running and spending quality time with his family.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-5972-7-1.png" alt="Ram Vittal" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ram Vittal&lt;/h3&gt; 
  &lt;p&gt;Ram is a Principal GenAI/ML Specialist at AWS. He has over 3 decades of experience building distributed, hybrid, and cloud applications. He is passionate about building secure, scalable, reliable AI/ML and big data solutions to help customers with their cloud adoption and optimization journey. In his spare time, he rides motorcycle and enjoys the nature with his family.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Secure SageMaker Unified Studio access with SAML and conditional policies</title>
		<link>https://aws.amazon.com/blogs/big-data/secure-sagemaker-unified-studio-access-with-saml-and-conditional-policies/</link>
		
		<dc:creator><![CDATA[Manos Samatas]]></dc:creator>
		<pubDate>Wed, 19 Aug 2026 20:35:54 +0000</pubDate>
				<category><![CDATA[Amazon SageMaker Unified Studio]]></category>
		<category><![CDATA[Expert (400)]]></category>
		<category><![CDATA[Technical How-to]]></category>
		<guid isPermaLink="false">1b742f633abbfdf0e1a5efc3ab93eceeeacf0773</guid>

					<description>Learn how to secure Amazon SageMaker Unified Studio by integrating it with an external SAML identity provider such as Okta. This post shows you how to apply conditional access policies that enforce device compliance, IP-based restrictions, and multi-factor authentication for your data and AI workloads.</description>
										<content:encoded>&lt;p&gt;&lt;a href="https://aws.amazon.com/sagemaker/unified-studio/" target="_blank" rel="noopener"&gt;Amazon SageMaker Unified Studio&lt;/a&gt; is a single data and AI development environment that brings together data preparation, analytics, and machine learning (ML) development in one place. By unifying these workflows, it saves teams from managing multiple tools and makes it straightforward for data scientists, analysts, and developers to build, train, and deploy ML models while collaborating. In Amazon SageMaker Unified Studio, a domain is the organizing entity for connecting your assets, users, and their projects. With Amazon SageMaker unified domains, you have the flexibility to reflect the data and analytics needs of your organizational structure. You can create a single unified domain for your enterprise or multiple domains for different business units.&lt;/p&gt; 
&lt;p&gt;Some enterprises, especially those in regulated industries, might require limiting access to trusted networks (such as VPN CIDRs) or to managed devices that meet compliance standards through device attestation.&lt;/p&gt; 
&lt;p&gt;In this post, we demonstrate how to integrate SageMaker Unified Studio as a custom SAML application and apply conditional access policies for enforcing device compliance, IP-based restrictions, or multi-factor authentication (MFA). For this post, we use Okta as the identity provider (IdP).&lt;/p&gt; 
&lt;h2 id="solution-overview"&gt;Solution overview&lt;/h2&gt; 
&lt;p&gt;This solution demonstrates how to integrate Amazon SageMaker Unified Studio (SMUS) with external SAML identity providers such as Okta. The integration enforces enterprise security controls, including trusted network access, device compliance, and multi-factor authentication. With this integration, organizations in regulated industries can maintain strict access controls while providing single sign-on for their data science and AI development teams. By using SAML 2.0 federation with conditional access policies, you can help make sure that only authenticated users on compliant devices from trusted networks gain access. This access applies to your SageMaker Unified Studio domains and the associated data and AI workloads.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img loading="lazy" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/07/27/BDB-5271-1.png" alt="SAML authentication flow from a corporate device through the identity provider and AWS STS to Amazon SageMaker Unified Studio" width="800" height="431"&gt;
 &lt;p class="wp-caption-text"&gt;Authentication flow for accessing SageMaker Unified Studio through SAML&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;The architecture diagram illustrates the secure authentication flow for accessing SageMaker Unified Studio through SAML integration:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Users typically initiate access from corporate-managed devices through VPN or trusted network connections.&lt;/li&gt; 
 &lt;li&gt;The IdP authenticates the user and evaluates conditional access policies defined by your organization. Based on these policies, it checks for trusted devices, approved source IP ranges, and MFA completion. If any policy fails, the login is rejected. Otherwise, authentication proceeds.&lt;/li&gt; 
 &lt;li&gt;Upon successful authentication and policy validation, the IdP generates a digitally signed SAML assertion containing user attributes and group memberships, securely delivering it to the user’s browser through HTTP POST binding.&lt;/li&gt; 
 &lt;li&gt;The client browser automatically posts the SAML assertion to the AWS Security Token Service (AWS STS) sign-in endpoint. There, the AWS IAM Identity Provider validates the trust relationship with your corporate IdP through pre-configured SAML federation settings.&lt;/li&gt; 
 &lt;li&gt;AWS STS validates the SAML assertion signature and authenticity. It then maps the user attributes to a specifically configured IAM role with SageMaker Unified Studio permissions, including the &lt;code&gt;datazone:GetIamPortalLoginUrl&lt;/code&gt; permission required for domain access.&lt;/li&gt; 
 &lt;li&gt;AWS STS confirms successful role assumption and generates temporary AWS credentials with a defined session duration. It then issues an HTTP redirect that returns the browser to the SageMaker Unified Studio domain with authenticated session tokens.&lt;/li&gt; 
 &lt;li&gt;Users gain access to the unified environment for data preparation, analytics, and machine learning development. All activities are governed by the assumed IAM role permissions and logged for comprehensive audit trails.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h2 id="walkthrough"&gt;Walkthrough&lt;/h2&gt; 
&lt;p&gt;In this walkthrough, you create a SAML application in Okta, connect it to AWS, and configure a SageMaker Unified Studio domain to use it for authentication.&lt;/p&gt; 
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt; 
&lt;p&gt;Before you get started, make sure you have the following:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Familiarity with Amazon SageMaker Unified Studio.&lt;/li&gt; 
 &lt;li&gt;A basic understanding of SAML 2.0.&lt;/li&gt; 
 &lt;li&gt;AWS Identity and Access Management (IAM) permissions to create a domain in Amazon SageMaker Unified Studio.&lt;/li&gt; 
 &lt;li&gt;Access to your SAML IdP (such as Okta or Entra ID) to create and configure a SAML application.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="step-1-create-an-application-in-okta"&gt;Step 1: Create an application in Okta&lt;/h3&gt; 
&lt;p&gt;The first step is to set up a new SAML application in Okta that manages authentication for SMUS.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;In Okta, go to &lt;strong&gt;Applications&lt;/strong&gt; → &lt;strong&gt;Create App Integration&lt;/strong&gt;, and choose &lt;strong&gt;SAML 2.0&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Provide an &lt;strong&gt;App name&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Set the &lt;strong&gt;Single sign-on URL&lt;/strong&gt; to &lt;code&gt;https://signin.aws.amazon.com/saml&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;Set &lt;strong&gt;Name ID format&lt;/strong&gt; to &lt;strong&gt;Persistent&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Set the &lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt; to &lt;code&gt;https://signin.aws.amazon.com/saml&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Next&lt;/strong&gt;, and finish creating the application.&lt;/li&gt; 
 &lt;li&gt;Once created, copy the &lt;strong&gt;Metadata URL&lt;/strong&gt; and &lt;strong&gt;Sign On URL&lt;/strong&gt;. You need these in later steps.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="step-2-create-an-identity-provider-in-iam"&gt;Step 2: Create an identity provider in IAM&lt;/h3&gt; 
&lt;p&gt;Now, let’s connect Okta to AWS by creating an IAM identity provider. This allows AWS to trust authentication responses from Okta.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open the &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home?region=us-east-1#/home" target="_blank" rel="noopener"&gt;IAM console&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;Go to &lt;strong&gt;Identity providers&lt;/strong&gt; → &lt;strong&gt;Add provider&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Select &lt;strong&gt;SAML&lt;/strong&gt; as the provider type.&lt;/li&gt; 
 &lt;li&gt;Provide a &lt;strong&gt;Provider name&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;In Okta, go to your application’s &lt;strong&gt;Sign On&lt;/strong&gt; tab, choose &lt;strong&gt;Identity Provider metadata&lt;/strong&gt;, and save the XML file. Upload it here.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Add provider&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Copy the &lt;strong&gt;ARN&lt;/strong&gt; of this provider. You need it when you create the role.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="step-3-create-an-iam-role-for-okta"&gt;Step 3: Create an IAM role for Okta&lt;/h3&gt; 
&lt;p&gt;Next, create an IAM role that Okta can assume. This role defines what access users have when they sign in through Okta.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;In IAM, go to &lt;strong&gt;Roles&lt;/strong&gt; → &lt;strong&gt;Create role&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Use the following trust policy (replace both instances of “{Replace with Identity provider ARN}” with the ARN you copied in Step 2):&lt;/li&gt; 
&lt;/ol&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "{Replace with Identity provider ARN}"
            },
            "Action": "sts:AssumeRoleWithSAML",
            "Condition": {
                "StringEquals": {
                    "SAML:aud": "https://signin.aws.amazon.com/saml"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "{Replace with Identity provider ARN}"
            },
            "Action": "sts:TagSession",
            "Condition": {
                "StringLike": {
                    "aws:RequestTag/Email": "*"
                }
            }
        }
    ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;ol start="3" type="1"&gt; 
 &lt;li&gt;Attach a &lt;strong&gt;permission policy&lt;/strong&gt;. For example:&lt;/li&gt; 
&lt;/ol&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-json"&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "datazone:GetIamPortalLoginUrl",
            "Resource": "arn:aws:datazone:&amp;lt;REGION&amp;gt;:&amp;lt;ACCOUNT-ID&amp;gt;:domain/&amp;lt;DOMAIN-ID&amp;gt;"
        }
    ]
}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Replace &lt;code&gt;&amp;lt;REGION&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ACCOUNT-ID&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;DOMAIN-ID&amp;gt;&lt;/code&gt; with the corresponding values from your SageMaker Unified Studio domain ARN (&lt;code&gt;arn:aws:sagemaker:&amp;lt;REGION&amp;gt;:&amp;lt;ACCOUNT-ID&amp;gt;:domain/&amp;lt;DOMAIN-ID&amp;gt;&lt;/code&gt;). You can find the domain ARN in the SageMaker console under &lt;b&gt;Domains&lt;/b&gt;.&lt;/p&gt; 
&lt;h3 id="step-4-configure-saml-assertions"&gt;Step 4: Configure SAML assertions&lt;/h3&gt; 
&lt;p&gt;To make sure AWS understands who is signing in, configure the SAML assertions in Okta.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open your application in Okta.&lt;/li&gt; 
 &lt;li&gt;Go to &lt;strong&gt;General&lt;/strong&gt; → &lt;strong&gt;SAML Settings&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Next&lt;/strong&gt; until you reach &lt;strong&gt;Attribute Statements&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Add the following mappings: 
  &lt;ul&gt; 
   &lt;li&gt;&lt;code&gt;https://aws.amazon.com/SAML/Attributes/PrincipalTag:Email&lt;/code&gt; → &lt;code&gt;user.email&lt;/code&gt;.&lt;/li&gt; 
   &lt;li&gt;&lt;code&gt;https://aws.amazon.com/SAML/Attributes/Role&lt;/code&gt; → &lt;code&gt;{IAMROLEARN,IdentityProviderARN}&lt;/code&gt;.&lt;/li&gt; 
   &lt;li&gt;&lt;code&gt;https://aws.amazon.com/SAML/Attributes/RoleSessionName&lt;/code&gt; → &lt;code&gt;user.email&lt;/code&gt;.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="step-5-create-an-smus-domain"&gt;Step 5: Create an SMUS domain&lt;/h3&gt; 
&lt;p&gt;Finally, let’s set up the SMUS domain and tie it all together.&lt;/p&gt; 
&lt;p&gt;Note: Creating a SageMaker Unified Studio domain incurs charges. For pricing details, see the &lt;a href="https://aws.amazon.com/sagemaker/pricing/" target="_blank" rel="noopener"&gt;Amazon SageMaker pricing page&lt;/a&gt;.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open the &lt;strong&gt;Amazon SageMaker console&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Create domain&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Manual setup&lt;/strong&gt; (this allows for SAML integration).&lt;/li&gt; 
 &lt;li&gt;Enter a domain name, then choose &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;In &lt;strong&gt;Configure SSO user access&lt;/strong&gt;, select &lt;strong&gt;SAML&lt;/strong&gt;, then choose &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Set the &lt;strong&gt;IdP SSO URL&lt;/strong&gt; to the Sign On URL from Step 1.&lt;/li&gt; 
 &lt;li&gt;Select &lt;strong&gt;Do not require assignments&lt;/strong&gt;. (Access is instead managed by your IdP team through Okta or Entra.)&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Next&lt;/strong&gt;, then choose &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;To verify the integration works, open your SMUS domain and choose &lt;strong&gt;Sign in with SSO&lt;/strong&gt;. You are redirected to Okta, and conditional access policies such as VPN, device attestation, or MFA apply automatically.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Open your SMUS domain URL in a browser.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Sign in with SSO&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Confirm that you are redirected to Okta for authentication.&lt;/li&gt; 
 &lt;li&gt;Sign in with your Okta credentials.&lt;/li&gt; 
 &lt;li&gt;Verify that you are redirected back to the SMUS domain with access to your projects.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="step-6-assign-users-to-the-okta-application"&gt;Step 6: Assign users to the Okta application&lt;/h3&gt; 
&lt;p&gt;Before users can authenticate through Okta to access SMUS, you must assign them to the application.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;In Okta, navigate to your SAML application.&lt;/li&gt; 
 &lt;li&gt;Go to the &lt;strong&gt;Assignments&lt;/strong&gt; tab.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Assign&lt;/strong&gt;, and select &lt;strong&gt;Assign to People&lt;/strong&gt; or &lt;strong&gt;Assign to Groups&lt;/strong&gt;.&lt;/li&gt; 
 &lt;li&gt;Select the users or groups who need access to SMUS.&lt;/li&gt; 
 &lt;li&gt;Choose &lt;strong&gt;Save and Go Back&lt;/strong&gt;, then choose &lt;strong&gt;Done&lt;/strong&gt;.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;h3 id="step-7-apply-conditional-access-policies"&gt;Step 7: Apply conditional access policies&lt;/h3&gt; 
&lt;p&gt;Up to Step 5, we configured SMUS with an external SAML IdP. At this point, anyone assigned to the new application in your IdP can sign in and access the SMUS domain.&lt;/p&gt; 
&lt;p&gt;This is where conditional access policies come into play. Based on your organization’s governance model, you can add policies in your IdP to further control how and when users gain access. For example:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Restricting access to specific corporate IP address ranges (for example, only through VPN).&lt;/li&gt; 
 &lt;li&gt;Enforcing device compliance so that only managed or secure devices can connect.&lt;/li&gt; 
 &lt;li&gt;Adding MFA requirements for sensitive actions.&lt;/li&gt; 
 &lt;li&gt;Applying device attestation to help assess whether the endpoint conforms to security baselines.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Most major IdPs, including Okta and Entra ID, support conditional access. You can find more details in their documentation:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-conditions" target="_blank" rel="noopener"&gt;Entra ID conditional access policies&lt;/a&gt;.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://support.okta.com/help/s/article/Conditional-access-based-on-device-security-posture?language=en_US" target="_blank" rel="noopener"&gt;Okta conditional access based on device security posture&lt;/a&gt;.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;These policies allow you to enforce the right level of protection, from something as simple as requiring users to connect through corporate networks to something as advanced as verifying device attestation across your fleet.&lt;/p&gt; 
&lt;h2 id="clean-up"&gt;Clean up&lt;/h2&gt; 
&lt;p&gt;To avoid incurring ongoing charges, delete the resources you created during this walkthrough:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Delete the Amazon SageMaker Unified Studio domain from the SageMaker console.&lt;/li&gt; 
 &lt;li&gt;Delete the IAM role you created for Okta.&lt;/li&gt; 
 &lt;li&gt;Delete the IAM identity provider.&lt;/li&gt; 
 &lt;li&gt;Delete the SAML application in Okta.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Important: Deleting the SMUS domain permanently removes all projects, assets, and data within it. Back up any important work before proceeding.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;By integrating SMUS with an external IdP through SAML, you can help enforce modern access controls based on your organization’s security requirements. This post walked through how to configure SMUS with a custom SAML application and pointed you toward resources for setting up conditional access policies.&lt;/p&gt; 
&lt;p&gt;With conditional access in place, you can decide, based on your organization’s needs, whether access should be limited to trusted users on trusted networks, trusted devices, or both. This approach can help provide a more secure and compliant login experience that aligns SMUS access with your company’s broader identity and security strategy.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/07/27/BDB-5271-2.png" alt="Amit Samal" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Amit Samal&lt;/h3&gt; 
  &lt;p&gt;Amit is a Sr.&amp;nbsp;Delivery Consultant in World Wide Public Sector, Professional Services at AWS working with UKGI Customers. Amit has been with AWS for about 4 years and has been helping customers across the UKGI to design &amp;amp; implement secure, resilient and cost-effective workloads on AWS. Amit is passionate about all areas of technology, but has focus areas in Networking, Migrations, and Application Modernizations.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/07/27/BDB-5271-3.jpeg" alt="Manos Samatas" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Manos Samatas&lt;/h3&gt; 
  &lt;p&gt;Manos is a Principal Solutions Architect in Data and AI with Amazon Web Services. He works with government, non-profit, education and healthcare customers in the UK on data and AI projects, helping build solutions using AWS. Manos lives and works in London. In his spare time, he enjoys reading, watching sports, playing video games and socialising with friends.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Querying raw log data using SQL and PPL with the optimized engine in Amazon OpenSearch Service</title>
		<link>https://aws.amazon.com/blogs/big-data/querying-raw-log-data-using-sql-and-ppl-with-the-optimized-engine-in-amazon-opensearch-service/</link>
		
		<dc:creator><![CDATA[Kaushik Krishnan]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 18:32:05 +0000</pubDate>
				<category><![CDATA[Advanced (300)]]></category>
		<category><![CDATA[Amazon OpenSearch Service]]></category>
		<category><![CDATA[How-To]]></category>
		<guid isPermaLink="false">d1a20c446c65296022075d5265d39d33a69fd5b7</guid>

					<description>Learn how to run fast analytical queries directly against raw log and trace data in Amazon OpenSearch Service using PPL and SQL. Follow a single incident investigation, one query at a time, and see how the new optimized engine answers each question directly from raw spans.</description>
										<content:encoded>&lt;p&gt;&lt;em&gt;In this post, you learn how to run fast analytical queries directly against raw log and trace data in Amazon OpenSearch Service using PPL and SQL.&lt;/em&gt;&lt;/p&gt; 
&lt;p&gt;&lt;a href="https://aws.amazon.com/opensearch-service" target="_blank" rel="noopener"&gt;Amazon OpenSearch Service&lt;/a&gt; is a fully managed service that helps you deploy, scale, and operate &lt;a href="https://opensearch.org/" target="_blank" rel="noopener"&gt;OpenSearch&lt;/a&gt;, the open source suite for search, analytics, and observability in the AWS Cloud. OpenSearch Service powers search and real-time analytics workloads, from lexical and hybrid search to log analytics and observability. This post focuses on log analytics, and on a practical question: how much analytical work can you do directly against raw log and trace data, without moving it or reshaping it first?&lt;/p&gt; 
&lt;p&gt;The new optimized engine in OpenSearch Service answers that question: you can point Piped Processing Language (PPL) and Structured Query Language (SQL) queries at raw log and trace data. The engine returns aggregations, filters, and scans over billions of events on the data exactly as you ingested it. In this post, you follow a single incident investigation, one query at a time. You see how the engine answers each new question, from multi-dimensional breakdowns and latency distributions to error rates and fleet sizing. No precomputed structure sits behind the results.&lt;/p&gt; 
&lt;h2 id="how-the-optimized-engine-queries-raw-data"&gt;How the optimized engine queries raw data&lt;/h2&gt; 
&lt;p&gt;The optimized engine stores data in the columnar Apache Parquet format and runs queries through Apache DataFusion, a vectorized execution engine, with Apache Calcite planning each query. Because the engine stores data in columns, an analytical query reads only the columns it touches and processes their values in batches, instead of reading each matching document in full. Alongside the columnar format, the engine also keeps an inverted index on the same data, so the query planner routes each operation to the path that serves it best: the columnar engine for aggregations and analytical scans, and the inverted index for selective search and filtering.&lt;/p&gt; 
&lt;p&gt;You ingest your logs and traces through the same Bulk API and clients you use today, and you write PPL or SQL against them as they land.&lt;/p&gt; 
&lt;h2 id="an-investigation-one-query-at-a-time"&gt;An investigation, one query at a time&lt;/h2&gt; 
&lt;p&gt;The following walkthrough traces a common observability use case, root-cause analysis during a live incident, from the perspective of a site reliability engineer (SRE). The engineer notices elevated latency and a handful of error alerts, with nothing that points to a clear cause. No existing dashboard covers this particular shape of problem, so the engineer opens Amazon OpenSearch Service and starts asking questions of the raw trace data, letting each answer decide the next one. PPL suits this work well. Each command transforms the data and passes it to the next, so the engineer reads a query left to right the same way they think through the investigation.&lt;/p&gt; 
&lt;p&gt;The walkthrough uses generated OpenTelemetry (OTEL) data from a synthetic load generator, at billion-document scale. The focus is the query capability, that is, what the engineer can express and retrieve directly from raw spans, rather than the specific values in each result.&lt;/p&gt; 
&lt;h3 id="step-1-assess-the-scope"&gt;Step 1: Assess the scope&lt;/h3&gt; 
&lt;p&gt;The first question in any investigation is how widespread the signal is. The engineer breaks errors down across service, HTTP method, and cloud Region in a single pass over roughly 1.1 billion spans.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;source=otel-traces
| where @timestamp &amp;gt;= timestamp("2026-05-15 00:00:00") and @timestamp &amp;lt; timestamp("2026-05-18 00:00:00")
| eval e = if(status_code = 2, 1, 0)
| stats sum(e) as errors, avg(durationInNanos) as avg_ns, count() as total_count
  by serviceName, http_method, cloud_region
| sort - errors
| head 8&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;In plain terms, this query answers the engineer’s first question: where are the failures happening? It counts the error spans and breaks them down by service, HTTP method, and AWS Region in a single pass. Rather than guessing which service to open first, the engineer gets a ranked list of the hardest-hit combinations to investigate.&lt;/p&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;errors&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;total_count&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;avg_ns&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;serviceName&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;http_method&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;cloud_region&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;730&lt;/td&gt; 
   &lt;td&gt;112,436&lt;/td&gt; 
   &lt;td&gt;41,246,806&lt;/td&gt; 
   &lt;td&gt;export-service&lt;/td&gt; 
   &lt;td&gt;GET&lt;/td&gt; 
   &lt;td&gt;us-west-2&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;722&lt;/td&gt; 
   &lt;td&gt;111,215&lt;/td&gt; 
   &lt;td&gt;41,000,227&lt;/td&gt; 
   &lt;td&gt;catalog-service&lt;/td&gt; 
   &lt;td&gt;PUT&lt;/td&gt; 
   &lt;td&gt;eu-central-1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;704&lt;/td&gt; 
   &lt;td&gt;112,051&lt;/td&gt; 
   &lt;td&gt;41,295,539&lt;/td&gt; 
   &lt;td&gt;image-service&lt;/td&gt; 
   &lt;td&gt;PATCH&lt;/td&gt; 
   &lt;td&gt;us-west-2&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;612&lt;/td&gt; 
   &lt;td&gt;93,214&lt;/td&gt; 
   &lt;td&gt;41,451,145&lt;/td&gt; 
   &lt;td&gt;healthcheck-service&lt;/td&gt; 
   &lt;td&gt;PUT&lt;/td&gt; 
   &lt;td&gt;us-east-1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;609&lt;/td&gt; 
   &lt;td&gt;94,314&lt;/td&gt; 
   &lt;td&gt;41,418,897&lt;/td&gt; 
   &lt;td&gt;auth-service&lt;/td&gt; 
   &lt;td&gt;POST&lt;/td&gt; 
   &lt;td&gt;us-east-1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;609&lt;/td&gt; 
   &lt;td&gt;94,414&lt;/td&gt; 
   &lt;td&gt;41,447,444&lt;/td&gt; 
   &lt;td&gt;email-service&lt;/td&gt; 
   &lt;td&gt;PATCH&lt;/td&gt; 
   &lt;td&gt;ap-northeast-1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;593&lt;/td&gt; 
   &lt;td&gt;89,726&lt;/td&gt; 
   &lt;td&gt;41,047,114&lt;/td&gt; 
   &lt;td&gt;payment-service&lt;/td&gt; 
   &lt;td&gt;PUT&lt;/td&gt; 
   &lt;td&gt;eu-central-1&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;581&lt;/td&gt; 
   &lt;td&gt;89,854&lt;/td&gt; 
   &lt;td&gt;41,195,643&lt;/td&gt; 
   &lt;td&gt;file-service&lt;/td&gt; 
   &lt;td&gt;PUT&lt;/td&gt; 
   &lt;td&gt;ap-northeast-1&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The errors spread across services, methods, and Regions, which points to a systemic pattern rather than a single misbehaving service.&lt;/p&gt; 
&lt;h3 id="step-2-check-whether-one-host-concentrates-the-failures"&gt;Step 2: Check whether one host concentrates the failures&lt;/h3&gt; 
&lt;p&gt;The spread could still reflect one saturated node or a fleet-wide condition. To tell the two apart, the engineer groups failures by exception type, service, and host across the entire index, with no time filter to narrow the scan.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;source=otel-traces
| where isnotnull(exception_type)
| stats count() as total_count by exception_type, serviceName, host_name
| sort - total_count
| head 8&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;total_count&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;exception_type&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;serviceName&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;host_name&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;6&lt;/td&gt; 
   &lt;td&gt;DeadlockDetectedException&lt;/td&gt; 
   &lt;td&gt;notification-service&lt;/td&gt; 
   &lt;td&gt;ip-10-0-16-34&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;6&lt;/td&gt; 
   &lt;td&gt;IllegalStateException&lt;/td&gt; 
   &lt;td&gt;api-gateway&lt;/td&gt; 
   &lt;td&gt;ip-10-0-180-234&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;6&lt;/td&gt; 
   &lt;td&gt;FileNotFoundException&lt;/td&gt; 
   &lt;td&gt;cart-service&lt;/td&gt; 
   &lt;td&gt;ip-10-0-90-162&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;6&lt;/td&gt; 
   &lt;td&gt;ConnectionRefusedException&lt;/td&gt; 
   &lt;td&gt;feature-flag-service&lt;/td&gt; 
   &lt;td&gt;ip-10-0-8-123&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;5&lt;/td&gt; 
   &lt;td&gt;TimeoutException&lt;/td&gt; 
   &lt;td&gt;auth-service&lt;/td&gt; 
   &lt;td&gt;ip-10-0-97-78&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;5&lt;/td&gt; 
   &lt;td&gt;ConcurrentModificationException&lt;/td&gt; 
   &lt;td&gt;order-service&lt;/td&gt; 
   &lt;td&gt;ip-10-0-165-15&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;5&lt;/td&gt; 
   &lt;td&gt;TimeoutException&lt;/td&gt; 
   &lt;td&gt;coupon-service&lt;/td&gt; 
   &lt;td&gt;ip-10-0-158-25&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;In this sample the counts are low and every row lands on a different host, so no single node stands out. This points to a fleet-wide pattern rather than one bad machine. On production data the same query makes the distinction directly: a code-level bug shows up across many hosts, whereas a single failing node concentrates its errors on one &lt;code&gt;host_name&lt;/code&gt;.&lt;/p&gt; 
&lt;h3 id="step-3-quantify-the-latency-distribution-per-service"&gt;Step 3: Quantify the latency distribution per service&lt;/h3&gt; 
&lt;p&gt;Next, the engineer pulls a latency profile for each service. This includes count, average, minimum, and maximum duration, to see how each one behaves and how wide the spread runs.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;source=otel-traces
| where @timestamp &amp;gt;= timestamp("2026-05-15 00:00:00") and @timestamp &amp;lt; timestamp("2026-05-18 00:00:00")
| stats count() as total_count, avg(durationInNanos) as avg_ns, min(durationInNanos) as min_ns, max(durationInNanos) as max_ns
  by serviceName
| sort - total_count
| head 8&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;serviceName&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;total_count&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;avg (ns)&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;min (ns)&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;max (ns)&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;event-bus&lt;/td&gt; 
   &lt;td&gt;11,087,263&lt;/td&gt; 
   &lt;td&gt;41,249,552&lt;/td&gt; 
   &lt;td&gt;26,113&lt;/td&gt; 
   &lt;td&gt;9,304,132,159&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;scheduler-service&lt;/td&gt; 
   &lt;td&gt;9,175,964&lt;/td&gt; 
   &lt;td&gt;41,251,927&lt;/td&gt; 
   &lt;td&gt;21,919&lt;/td&gt; 
   &lt;td&gt;13,432,040,933&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;cdn-service&lt;/td&gt; 
   &lt;td&gt;9,173,572&lt;/td&gt; 
   &lt;td&gt;41,225,385&lt;/td&gt; 
   &lt;td&gt;23,468&lt;/td&gt; 
   &lt;td&gt;13,768,293,306&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;ml-inference&lt;/td&gt; 
   &lt;td&gt;9,036,753&lt;/td&gt; 
   &lt;td&gt;41,289,101&lt;/td&gt; 
   &lt;td&gt;40,410&lt;/td&gt; 
   &lt;td&gt;14,625,084,517&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;compliance-service&lt;/td&gt; 
   &lt;td&gt;8,274,635&lt;/td&gt; 
   &lt;td&gt;41,294,694&lt;/td&gt; 
   &lt;td&gt;41,915&lt;/td&gt; 
   &lt;td&gt;7,462,983,016&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;metrics-collector&lt;/td&gt; 
   &lt;td&gt;7,804,234&lt;/td&gt; 
   &lt;td&gt;41,334,728&lt;/td&gt; 
   &lt;td&gt;16,535&lt;/td&gt; 
   &lt;td&gt;23,228,217,669&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;notification-service&lt;/td&gt; 
   &lt;td&gt;7,688,714&lt;/td&gt; 
   &lt;td&gt;41,204,635&lt;/td&gt; 
   &lt;td&gt;51,562&lt;/td&gt; 
   &lt;td&gt;8,695,311,374&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;image-service&lt;/td&gt; 
   &lt;td&gt;7,674,406&lt;/td&gt; 
   &lt;td&gt;41,248,069&lt;/td&gt; 
   &lt;td&gt;47,473&lt;/td&gt; 
   &lt;td&gt;15,350,500,299&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;This gives the engineer a latency fingerprint for each service: the averages sit near 41 milliseconds. But the multi-second maxima reveal a long tail consistent with requests queuing behind a slow dependency.&lt;/p&gt; 
&lt;h3 id="step-4-measure-the-error-rate-per-service"&gt;Step 4: Measure the error rate per service&lt;/h3&gt; 
&lt;p&gt;To track a service-level objective, the engineer computes the error rate (errors against total requests) per service. The query uses an inline conditional, followed by a grouped sum and count, and a final division to produce the error rate.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;source=otel-traces
| eval is_err = if(status_code = 2, 1, 0)
| stats sum(is_err) as errors, count() as total_count by serviceName
| eval error_pct = round(100.0 * errors / total_count, 2)
| sort - error_pct
| head 8&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;errors&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;total_count&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;error_pct&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;serviceName&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;699,358&lt;/td&gt; 
   &lt;td&gt;22,415,308&lt;/td&gt; 
   &lt;td&gt;3.12&lt;/td&gt; 
   &lt;td&gt;payment-service&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;647,811&lt;/td&gt; 
   &lt;td&gt;26,880,140&lt;/td&gt; 
   &lt;td&gt;2.41&lt;/td&gt; 
   &lt;td&gt;checkout-service&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;562,811&lt;/td&gt; 
   &lt;td&gt;30,096,860&lt;/td&gt; 
   &lt;td&gt;1.87&lt;/td&gt; 
   &lt;td&gt;auth-service&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;316,192&lt;/td&gt; 
   &lt;td&gt;24,510,990&lt;/td&gt; 
   &lt;td&gt;1.29&lt;/td&gt; 
   &lt;td&gt;cart-service&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;288,314&lt;/td&gt; 
   &lt;td&gt;30,671,704&lt;/td&gt; 
   &lt;td&gt;0.94&lt;/td&gt; 
   &lt;td&gt;order-service&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;202,612&lt;/td&gt; 
   &lt;td&gt;28,140,552&lt;/td&gt; 
   &lt;td&gt;0.72&lt;/td&gt; 
   &lt;td&gt;search-service&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;186,012&lt;/td&gt; 
   &lt;td&gt;33,820,415&lt;/td&gt; 
   &lt;td&gt;0.55&lt;/td&gt; 
   &lt;td&gt;catalog-service&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;134,722&lt;/td&gt; 
   &lt;td&gt;35,453,247&lt;/td&gt; 
   &lt;td&gt;0.38&lt;/td&gt; 
   &lt;td&gt;image-service&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The engineer defines the error-rate metric in the query itself, and the engine computes it across the full index. The busiest paths, payment and checkout, run near 3 percent, whereas some services stay below 1 percent.&lt;/p&gt; 
&lt;h3 id="step-5-size-the-fleet-footprint-with-sql"&gt;Step 5: Size the fleet footprint with SQL&lt;/h3&gt; 
&lt;p&gt;Finally, the engineer sizes how much of the fleet each service spans, a capacity and impact question, and switches from PPL to SQL to express it.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-sql"&gt;SELECT serviceName,
       COUNT(*) AS total_count,
       COUNT(DISTINCT host_name) AS hosts
FROM otel-traces
GROUP BY serviceName
ORDER BY total_count DESC
LIMIT 8&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;serviceName&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;total_count&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;hosts&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;ml-inference&lt;/td&gt; 
   &lt;td&gt;35,481,688&lt;/td&gt; 
   &lt;td&gt;2,535&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;image-service&lt;/td&gt; 
   &lt;td&gt;35,453,247&lt;/td&gt; 
   &lt;td&gt;2,491&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;email-service&lt;/td&gt; 
   &lt;td&gt;35,443,569&lt;/td&gt; 
   &lt;td&gt;2,517&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;shipping-service&lt;/td&gt; 
   &lt;td&gt;30,700,372&lt;/td&gt; 
   &lt;td&gt;2,438&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;translation-service&lt;/td&gt; 
   &lt;td&gt;30,490,570&lt;/td&gt; 
   &lt;td&gt;2,502&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;auth-service&lt;/td&gt; 
   &lt;td&gt;30,096,860&lt;/td&gt; 
   &lt;td&gt;2,466&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;chat-service&lt;/td&gt; 
   &lt;td&gt;25,564,111&lt;/td&gt; 
   &lt;td&gt;2,449&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;recommendation-service&lt;/td&gt; 
   &lt;td&gt;25,366,844&lt;/td&gt; 
   &lt;td&gt;2,483&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The query runs a &lt;code&gt;COUNT(DISTINCT)&lt;/code&gt; over a high-cardinality field at billion-row scale, and switching languages mid-investigation costs the engineer nothing more than writing SQL instead of PPL. The host counts cluster in the approximately 2,400–2,540 range, so each service runs across a broad slice of the fleet. That confirms the earlier finding: the errors reflect a fleet-wide pattern, not a single node.&lt;/p&gt; 
&lt;p&gt;The engineer asked five questions and ran five queries, and each answer shaped the next. The optimized engine served every query directly from raw trace data, across both PPL and SQL, without a rollup table or precomputed summary behind any result.&lt;/p&gt; 
&lt;h2 id="run-these-queries-where-you-already-work"&gt;Run these queries where you already work&lt;/h2&gt; 
&lt;p&gt;You don’t need a separate tool to run the queries in this walkthrough.&lt;/p&gt; 
&lt;div id="attachment_93281" style="width: 2780px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93281" loading="lazy" class="size-full wp-image-93281" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/12/bdb-6125-img2.png" alt="" width="2770" height="1742"&gt;
 &lt;p id="caption-attachment-93281" class="wp-caption-text"&gt;Figure 1: Investigation queries and results grid in Query Workbench&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;&lt;a href="https://docs.opensearch.org/latest/dashboards/query-workbench/"&gt;Query Workbench&lt;/a&gt; in OpenSearch Dashboards UI gives you a dedicated editor for PPL and SQL. You write a query, run it, and read the results in a grid, using the same queries shown throughout this post. When you want to move from a written query to interactive exploration, Discover runs the same PPL and SQL against your indexes. In Discover, you can filter, expand fields, and drill into individual documents without leaving the page. The same query language works in both places, so you can start an investigation in Discover and carry it into Query Workbench, or the reverse, without rewriting anything.&lt;/p&gt; 
&lt;div id="attachment_93282" style="width: 2690px" class="wp-caption alignleft"&gt;
 &lt;img aria-describedby="caption-attachment-93282" loading="lazy" class="size-full wp-image-93282" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/12/bdb-6125-img1.png" alt="" width="2680" height="1396"&gt;
 &lt;p id="caption-attachment-93282" class="wp-caption-text"&gt;Figure 2: PPL query and field list in Discover&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="keep-all-your-data-and-query-it-as-it-is"&gt;Keep all your data and query it as it is&lt;/h2&gt; 
&lt;p&gt;Querying raw data directly only helps if you can afford to keep the raw data. The optimized engine compresses observability data up to 70 percent more efficiently than the default General Purpose engine. That compression turns “keep everything and query it directly” into a practical default. You retain full-fidelity data for the questions you cannot predict in advance. You also pay less to store it than you would to store the raw JSON.&lt;/p&gt; 
&lt;h2 id="get-started"&gt;Get started&lt;/h2&gt; 
&lt;p&gt;To try the optimized engine, create an Amazon OpenSearch Service domain running OpenSearch 3.5 or later. Then &lt;a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/optimized-log-analytics-prerequisites.html#optimized-log-analytics-configuration" target="_blank" rel="noopener"&gt;select the Observability use case during setup&lt;/a&gt;, which provisions the domain with the optimized engine.&lt;/p&gt; 
&lt;p&gt;To learn more about configuring and using the optimized engine, see &lt;a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/optimized-log-analytics.html" target="_blank" rel="noopener"&gt;Optimized for Log Analytics&lt;/a&gt; in the Amazon OpenSearch Service documentation. For an overview of the service, visit &lt;a href="https://aws.amazon.com/opensearch-service/features/loganalytics/" target="_blank" rel="noopener"&gt;Amazon OpenSearch Service Log Analytics&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;For more information, see the blog post &lt;a href="https://aws.amazon.com/blogs/big-data/run-log-analytics-for-a-fraction-of-the-cost-with-the-new-engine-for-amazon-opensearch-service/" target="_blank" rel="noopener"&gt;Run log analytics for a fraction of the cost with the new engine for Amazon OpenSearch Service&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;Give it a try and send feedback to &lt;a href="https://repost.aws/selections/KPke2SCsxiQEGwNbtw_lAnTw/aws-re-post-knowledge-center-spotlight-opensearch-service" target="_blank" rel="noopener"&gt;AWS re:Post for Amazon OpenSearch Service&lt;/a&gt; or through your usual AWS Support contacts.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="alignleft size-full wp-image-93280" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/12/pkkgshn.jpg" alt="" width="100" height="133"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Kaushik Krishnan&lt;/h3&gt; 
  &lt;p&gt;Kaushik is a Technical Account Manager at Amazon Web Services with a focus on Amazon OpenSearch Service. He is based in the Washington, D.C. area and specializes in troubleshooting critical operational and performance issues as well as conducting architectural reviews of OpenSearch clusters for customers. Outside of work, he enjoys playing soccer and is an avid traveler.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="alignleft size-full wp-image-93334" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/luis-tiani-1.png" alt="" width="100" height="138"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Luis Tiani&lt;/h3&gt; 
  &lt;p&gt;Luis is a Sr Solutions Architect at AWS. He specializes in data and analytics topics, with extensive focus on Amazon OpenSearch Service for search, log analytics, and vector environments. Tiani has helped numerous customers across financial services, DNB, SMB, and enterprise segments in their OpenSearch adoption journey, reviewing use cases and providing architecture design and cluster sizing guidance.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt;
   &lt;img loading="lazy" class="alignleft size-full wp-image-28068" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2022/04/20/kujaga.jpg" alt="" width="100" height="142"&gt;
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Jagadish Kumar&lt;/h3&gt; 
  &lt;p&gt;Jagadish is a Senior Solutions Architect at Amazon Web Services, focused on OpenSearch and analytics workloads.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Fresher insights, faster decisions: talabat’s near-real-time analytics across AWS and Google Cloud</title>
		<link>https://aws.amazon.com/blogs/big-data/fresher-insights-faster-decisions-talabats-near-real-time-analytics-across-aws-and-google-cloud/</link>
		
		<dc:creator><![CDATA[Harish Ramesh]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 16:56:53 +0000</pubDate>
				<category><![CDATA[Amazon S3 Tables]]></category>
		<category><![CDATA[Customer Solutions]]></category>
		<guid isPermaLink="false">af2a0da27d15ca689dc64632be402f4b0a1cc6c9</guid>

					<description>Leading everyday app across the Middle East and North Africa, talabat, built a hybrid multi-cloud lakehouse that keeps a single Apache Iceberg copy of streaming data on Amazon S3 Tables while letting Google BigQuery query it in place, eliminating cross-cloud data duplication and schema-synchronization overhead.</description>
										<content:encoded>&lt;p&gt;&lt;a href="https://www.talabat.com/uae" target="_blank" rel="noopener"&gt;talabat&lt;/a&gt; is the leading everyday app in the Middle East and North Africa (MENA) region, offering customers a convenient and personalized way to order food, groceries, and other everyday essentials from a wide selection of restaurants and retailers. Founded in Kuwait in 2004, talabat has expanded its operations to the United Arab Emirates, Oman, Qatar, Bahrain, Jordan, Iraq, and Egypt, serving over seven million monthly active customers as of December 2025. talabat is headquartered in Dubai, United Arab Emirates, and in December 2024 successfully completed its initial public offering on the Dubai Financial Market (DFM). As a subsidiary of Delivery Hero SE, talabat uses global expertise to continuously enhance its service, expand its landscape, and drive innovation. With a strong network of partners and riders, talabat connects customers to what they need, when they need it – powering everyday convenience across the region.&lt;/p&gt; 
&lt;p&gt;In this post, we show how talabat built a hybrid, multi-cloud lakehouse that keeps a single Apache Iceberg copy of streaming data on &lt;a href="https://aws.amazon.com/" target="_blank" rel="noopener"&gt;AWS&lt;/a&gt; while enabling governed, near-real-time analytics from &lt;a href="https://cloud.google.com/" target="_blank" rel="noopener"&gt;Google Cloud&lt;/a&gt; Platform (GCP).&lt;/p&gt; 
&lt;h1&gt;Data at talabat&lt;/h1&gt; 
&lt;p&gt;Data is the nervous system of talabat’s business. From the moment a customer hits “order” to the second their doorbell rings, talabat’s systems make split-second, data-driven decisions, instantaneously optimizing pricing, dispatch, routing, and order security. Over the years, talabat’s application grew into a landscape spanning two public clouds. Our transactional and operational backbone matured on AWS, where the engineering teams build and operate services. In parallel, a large population of analysts, data scientists, and analytics-engineering pipelines standardized on the Google Cloud Platform warehouse, &lt;a href="https://cloud.google.com/bigquery" target="_blank" rel="noopener"&gt;Google BigQuery&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;Both investments are deep, and both deliver value. So the strategic question wasn’t “which cloud do we consolidate on,” but rather “how do we make our data flow cleanly across the boundary between them.” That framing shaped everything that follows. The challenge isn’t only cross-cloud but cross-Region as well, with AWS services hosted in the EU region and the data in the GCP US region.&lt;/p&gt; 
&lt;p&gt;The following diagram shows how talabat’s data flows between the operational plane on AWS and the analytics plane on GCP.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-6102-1-1.png" alt="Data flow between talabat’s operational plane on AWS and analytics plane on Google Cloud" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Data flow between the operational plane on AWS and the analytics plane on Google Cloud&lt;/p&gt;
&lt;/div&gt; 
&lt;p&gt;Historically, the data engineering team orchestrated the data movement between the two clouds, mandating a physical movement from AWS to GCP, EU to US. Moving this using conventional extract, transform, and load (ETL) tools and frameworks delayed and duplicated the data through multiple hops: &lt;a href="https://aws.amazon.com/rds/" target="_blank" rel="noopener"&gt;Amazon Relational Database Service&lt;/a&gt; (Amazon RDS) to &lt;a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener"&gt;Amazon Simple Storage Service&lt;/a&gt; (Amazon S3) EU AWS Region, Amazon S3 EU to Amazon S3 US Region, and finally Amazon S3 US to BigQuery US.&lt;/p&gt; 
&lt;p&gt;Each hop was a copy, and every copy compounded risk: multiple failure points, compounding latency, redundant compute and storage, type fidelity, and most importantly, cross-Region and cross-cloud egress cost.&lt;/p&gt; 
&lt;p&gt;In short, the old design paid in dollars, latency, and reliability to solve a problem it had created for itself: it moved data so that BigQuery could read it. A classic data warehouse bottleneck. Could we use an open data lake instead? Yes. But the analytics usage is heavy on BigQuery, which limits access through an open source data lake layer. So the redesign started from the opposite premise: keep one copy on AWS and let BigQuery read it in place. That is what the rest of this post describes: a lakehouse for talabat.&lt;/p&gt; 
&lt;h2 id="challenges"&gt;Challenges&lt;/h2&gt; 
&lt;p&gt;Operational systems emit a continuous stream of business events like order lifecycle changes, vendor, menu, logistics and rider signals, and payments information published to &lt;a href="https://kafka.apache.org/" target="_blank" rel="noopener"&gt;Apache Kafka&lt;/a&gt; on &lt;a href="https://aws.amazon.com/msk/" target="_blank" rel="noopener"&gt;Amazon Managed Streaming for Apache Kafka&lt;/a&gt; (Amazon MSK). These events are encoded as Protocol Buffers and governed by backward-compatible schemas registered in &lt;a href="https://docs.confluent.io/platform/current/schema-registry/index.html" target="_blank" rel="noopener"&gt;Confluent Schema Registry&lt;/a&gt;, so producers and consumers can evolve safely over time.&lt;/p&gt; 
&lt;p&gt;The requirement on the analytics side is straightforward to state and hard to meet: make these events queryable, correctly typed, within minutes of being produced, and make them queryable from the tools each team already uses.&lt;/p&gt; 
&lt;p&gt;It’s tempting to view a two-cloud footprint as technical debt. For a real-time business like talabat, it’s simply the terrain, and each side plays to a genuine strength:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;The event backbone lives on AWS. Our transactional and streaming systems publish to Amazon MSK. The lowest-latency, lowest-risk place to consume and process those events is next to them, in the same AWS Region.&lt;/li&gt; 
 &lt;li&gt;The analytics estate lives on Google Cloud. Thousands of downstream models and dashboards, and the people who build them, assume BigQuery as the query surface.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Consolidating either side would mean a multi-year migration and a significant regression in capability for one group of users, all to remove a seam between ingestion and analytics. Data engineers decided to engineer the seam instead. The design goal became a single sentence: keep one physical copy of the data on AWS, and read it natively from both clouds. A hybrid data lakehouse makes the “which cloud” question an access-path detail rather than an architectural fork.&lt;/p&gt; 
&lt;h2 id="what-we-tried-first-cross-cloud-writes-on-the-hot-path"&gt;What we tried first: Cross-cloud writes on the hot path&lt;/h2&gt; 
&lt;p&gt;Our first attempt inverted the flow we eventually shipped. Raw (also called Bronze) layer data was written from AWS directly into BigQuery-managed Iceberg tables on &lt;a href="https://cloud.google.com/storage" target="_blank" rel="noopener"&gt;Google Cloud Storage&lt;/a&gt;. On paper, this placed the data closest to the largest consumer base. In practice, writing across clouds on an always-on streaming path introduced a class of problems we did not want to live with:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;A cross-cloud dependency on the ingestion path. Every micro-batch was coupled to the availability and latency of a remote cloud’s write API.&lt;/li&gt; 
 &lt;li&gt;Streaming write-API failures surfaced as ingestion incidents. The remote write became the fragile link, turning read-side concerns into write-side outages, the worst place to absorb them.&lt;/li&gt; 
 &lt;li&gt;Preview-gated capabilities constrained the physical layout. Certain partitioning behaviors and features were not generally available, limiting how we could organize the data for cost and performance.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The lesson was clear: Shift left. The write path should be short, local, and straightforward. The cross-cloud concern belongs on the read path, where it can be made read-only, cached, and retried without affecting the ingestion. That reframing led directly to the architecture we run today.&lt;/p&gt; 
&lt;h2 id="choosing-how-bigquery-would-read-aws-resident-data"&gt;Choosing how BigQuery would read AWS resident data&lt;/h2&gt; 
&lt;p&gt;With the flow inverted (raw data on AWS, read from Google Cloud), we evaluated three ways for BigQuery to read tables that physically live on AWS. We assessed each against four criteria:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;No data movement.&lt;/li&gt; 
 &lt;li&gt;An open table format.&lt;/li&gt; 
 &lt;li&gt;A governable trust model.&lt;/li&gt; 
 &lt;li&gt;Minimal operational surface.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;table border="1px" width="100%" cellpadding="10px"&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt; 
   &lt;td&gt;&lt;strong&gt;Assessment&lt;/strong&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Cross-cloud write to Google Cloud Storage&lt;/td&gt; 
   &lt;td&gt;Continue writing bronze into BigQuery-managed Iceberg on Google Cloud Storage. We rejected this for the preceding reasons: it puts a cross-cloud dependency and cross-Region latency on the ingestion hot path.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;a href="https://docs.cloud.google.com/bigquery/docs/omni-introduction" target="_blank" rel="noopener"&gt;BigQuery Omni&lt;/a&gt;&lt;/td&gt; 
   &lt;td&gt;Query AWS resident data through the managed cross-cloud compute of BigQuery Omni. This introduced more managed surface and more constraints than we needed for a read-only bronze layer, and we wanted to own the catalog and trust model directly.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;&lt;a href="https://cloud.google.com/products/lakehouse" target="_blank" rel="noopener"&gt;Lakehouse federated Apache Iceberg REST catalog&lt;/a&gt; (authenticated by IAM)&lt;/td&gt; 
   &lt;td&gt;Let BigQuery read data in &lt;a href="https://aws.amazon.com/de/s3/features/tables/" target="_blank" rel="noopener"&gt;Amazon S3 Tables&lt;/a&gt;, a capability of Amazon S3 that provides managed &lt;a href="https://iceberg.apache.org/" target="_blank" rel="noopener"&gt;Apache Iceberg&lt;/a&gt; tables, through a federated catalog that synchronizes AWS Glue Data Catalog metadata, with access authenticated by cross-cloud IAM trust. This met all four criteria, and we chose it.&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;p&gt;The deciding properties were that the raw data doesn’t leave AWS, the format is open Apache Iceberg (so &lt;a href="https://aws.amazon.com/athena/" target="_blank" rel="noopener"&gt;Amazon Athena&lt;/a&gt;, Spark, and Iceberg-compatible engines read the same tables), and the cross-cloud relationship is expressed as identity and trust rather than as a recurring copy job.&lt;/p&gt; 
&lt;h2 id="why-amazon-s3-tables"&gt;Why Amazon S3 Tables&lt;/h2&gt; 
&lt;p&gt;With the architecture settled on a single Iceberg copy living on AWS, we needed a storage layer purpose-built for Iceberg at scale. Amazon S3 Tables met the requirements without adding operational surface. Table maintenance (compaction, snapshot expiration, and unreferenced file removal) runs automatically as a service-managed policy, avoiding the need for external orchestration jobs that would otherwise grow linearly with table count. Equally important, every table is an Amazon Resource Name (ARN)-addressable resource. That means IAM policies can grant or deny access for individual tables, the same least-privilege model we apply to any other AWS resource, and &lt;a href="https://aws.amazon.com/cloudtrail/" target="_blank" rel="noopener"&gt;AWS CloudTrail&lt;/a&gt; records every access decision. For a cross-cloud design where the trust boundary is expressed entirely through IAM, having tables that are first-class IAM resources isn’t a convenience but a prerequisite. S3 Tables gave us managed Iceberg housekeeping and fine-grained, auditable access control in a single construct, so the engineering team could focus on the streaming logic rather than the storage plumbing beneath it.&lt;/p&gt; 
&lt;h1&gt;Solution overview&lt;/h1&gt; 
&lt;p&gt;The system has two halves that meet at an open table format:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;A short, local write path on AWS.&lt;/li&gt; 
 &lt;li&gt;A read-only cross-cloud handshake that lets BigQuery consume the data.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;The single source of truth is Apache Iceberg data in Amazon S3 Tables. Every consumer reads that one physical copy.&lt;/p&gt; 
&lt;p&gt;The following diagram shows the end-to-end architecture, from event ingestion through storage to consumption paths.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-6102-2-1.png" alt="End-to-end architecture from event ingestion through Amazon S3 Tables storage to BigQuery, Athena, and Spark consumers" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: End-to-end architecture from event ingestion through storage to consumption paths&lt;/p&gt;
&lt;/div&gt; 
&lt;h3 id="the-write-path-short-local-and-reliable"&gt;The write path: Short, local, and reliable&lt;/h3&gt; 
&lt;p&gt;We run one &lt;a href="https://aws.amazon.com/emr/" target="_blank" rel="noopener"&gt;Amazon EMR Serverless&lt;/a&gt; Spark Structured Streaming job per Kafka topic (with a prebaked Docker image, &lt;code&gt;emr-7.13.0&lt;/code&gt; on ARM64/Graviton) in the same AWS Region (eu-west-2) as Amazon MSK. Co-locating compute with the event backbone minimizes the data transferred per micro-batch, saving cost and latency. Each job runs the Spark &lt;code&gt;foreachBatch&lt;/code&gt; operation with a trigger interval of roughly one to five minutes and at-least-once delivery. Every micro-batch performs five steps:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Consume&lt;/strong&gt; from Kafka.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Decode&lt;/strong&gt; Protocol Buffers using the registered schema.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Transform&lt;/strong&gt; to the target Iceberg schema.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Append&lt;/strong&gt; to the Iceberg table in Amazon S3 Tables.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Commit&lt;/strong&gt; offsets.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;The cycle repeats without interruption.&lt;/p&gt; 
&lt;p&gt;This path touches only AWS. There is no cross-cloud dependency, only one deliberate cross-Region hop: compute in the Europe (London) Region (eu-west-2), storage in the US East (N. Virginia) Region (us-east-1). This incurs standard AWS inter-Region data transfer cost, a deliberate choice so that the cross-cloud read from BigQuery stays within the same Region.&lt;/p&gt; 
&lt;p&gt;Bad records don’t block the stream. They land in a dedicated dead-letter queue (DLQ) table (&lt;code&gt;&amp;lt;table&amp;gt;_dlq&lt;/code&gt;) in a separate S3 Tables bucket, storing the raw payload (&lt;code&gt;raw_value_b64&lt;/code&gt;) and a &lt;code&gt;skip_reason&lt;/code&gt;. Nothing is silently dropped. The DLQ tables are registered with the AWS Glue Data Catalog through Lakehouse, so engineers can inspect failures from Amazon Athena or BigQuery.&lt;/p&gt; 
&lt;p&gt;From this point on, Amazon S3 Tables is the source of truth.&lt;/p&gt; 
&lt;h3 id="the-crux-cross-cloud-handshake"&gt;The crux: Cross-cloud handshake&lt;/h3&gt; 
&lt;p&gt;This is the heart of the design. BigQuery reads the S3 Tables Iceberg data through a &lt;a href="https://cloud.google.com/products/lakehouse" target="_blank" rel="noopener"&gt;Lakehouse federated Apache Iceberg REST catalog&lt;/a&gt;, a read-only catalog on the Google Cloud side that points at the AWS resident tables. Three mechanisms make it work.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;An open catalog contract (Iceberg REST)&lt;/strong&gt;.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;blockquote&gt;
 &lt;p&gt;Amazon S3 Tables exposes an &lt;a href="https://iceberg.apache.org/rest-catalog-spec/" target="_blank" rel="noopener"&gt;Apache Iceberg REST catalog&lt;/a&gt; interface, and &lt;a href="https://cloud.google.com/products/lakehouse" target="_blank" rel="noopener"&gt;Google Lakehouse&lt;/a&gt; speaks that same standard. Because both sides agree on the Iceberg on-disk format and REST catalog protocol, no translation layer or data copy is required. BigQuery reads the identical Iceberg data files that Athena and Spark read.&lt;/p&gt; 
 &lt;p&gt;On the Google Cloud side this is a single &lt;a href="https://docs.cloud.google.com/lakehouse/docs/about-lakehouse-catalogs" target="_blank" rel="noopener"&gt;Lakehouse federated catalog&lt;/a&gt;. A table surfaces to analysts as &lt;code&gt;talabat-data.s3tables-glue.catalog.orders&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;ol start="2" type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Cross-cloud identity and trust (IAM and OIDC)&lt;/strong&gt;.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;blockquote&gt;
 &lt;p&gt;The Lakehouse catalog authenticates to AWS as a Google-managed service identity (the Lakehouse REST-catalog service account) that an &lt;a href="https://aws.amazon.com/iam/" target="_blank" rel="noopener"&gt;AWS Identity and Access Management&lt;/a&gt; (IAM) role trusts through OpenID Connect (OIDC) federation with &lt;code&gt;accounts.google.com&lt;/code&gt;, using &lt;code&gt;sts:AssumeRoleWithWebIdentity&lt;/code&gt; with the service account’s numeric ID pinned in the role’s trust policy. Requests to the S3 Tables Iceberg endpoint are SigV4-signed. It’s the same AWS request-signing scheme that any AWS SDK uses, scoped to the S3 Tables service. In other words, the handshake isn’t a proprietary connector. It’s standard AWS request signing performed by a trusted external identity.&lt;/p&gt; 
 &lt;p&gt;The trust is codified as infrastructure as code (IaC) on the AWS side: granted least-privilege, and revocable at any time. The following diagram shows this authentication sequence.&lt;/p&gt; 
 &lt;div style="width: 810px" class="wp-caption alignnone"&gt;
  &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-6102-3-1.png" alt="Cross-cloud authentication sequence in which the Lakehouse service account presents a Google OIDC token that AWS IAM validates to return read-only Amazon S3 Tables credentials" width="800"&gt;
  &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: Cross-cloud authentication sequence between the Lakehouse catalog and AWS IAM&lt;/p&gt;
 &lt;/div&gt; 
 &lt;p&gt;For a step-by-step walkthrough of this trust relationship, creating the IAM role, validating the token’s audience and subject, and pinning the Lakehouse service-account identity in the trust policy, see &lt;a href="https://docs.cloud.google.com/bigquery/docs/glue-federated-datasets" target="_blank" rel="noopener"&gt;Create and manage AWS Glue federated datasets&lt;/a&gt; and &lt;a href="https://docs.cloud.google.com/lakehouse/docs/set-up-cross-cloud-lakehouse-aws-glue" target="_blank" rel="noopener"&gt;Set up cross-cloud Lakehouse for AWS Glue&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;ol start="3" type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Metadata synchronization&lt;/strong&gt; &lt;strong&gt;(approximately five-minute refresh)&lt;/strong&gt;.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;blockquote&gt;
 &lt;p&gt;The federated catalog periodically synchronizes table metadata from the &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/catalog-and-crawler.html" target="_blank" rel="noopener"&gt;AWS Glue Data Catalog&lt;/a&gt; that fronts S3 Tables. Newly created tables and new data become visible to BigQuery on a short refresh cycle (approximately 300 seconds). Reads are served against the live Iceberg data. Only the catalog pointers are synchronized.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;p&gt;The result is that a table written once on AWS appears in BigQuery as an ordinary catalog object and can be queried with standard SQL, while the bytes don’t leave AWS and the format stays open.&lt;/p&gt; 
&lt;h4 id="infrastructure-as-code-the-cross-cloud-trust-surface"&gt;Infrastructure as code: The cross-cloud trust surface&lt;/h4&gt; 
&lt;p&gt;The following section explains the authentication handshake shown in the architecture diagram. The Lakehouse catalog service account presents a Google OIDC JSON Web Token (JWT), which AWS validates through the IAM OIDC provider, returning short-lived credentials scoped to read-only S3 Tables access.&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Register Google as a trusted identity provider. Scoped to our Lakehouse catalog’s service account: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-plaintext"&gt;resource "aws_iam_openid_connect_provider" "google" {
  url = "https://accounts.google.com"
  client_id_list = [var.lakehouse_sa_audience] #Lakehouse REST-catalog serviceaccount
}&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
 &lt;li&gt;Pin the trust to exactly that one identity. This is the security crux. The role can only be assumed through a Google-signed token whose subject matches our service account. A condition on the sub claim closes the door to every other principal: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-plaintext"&gt;data "aws_iam_policy_document" "trust" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    principals {
      type = "Federated"
      identifiers = [aws_iam_openid_connect_provider.google.arn]
    }
    condition {
      test = "StringEquals"
      variable = "accounts.google.com:sub"
      values = [var.lakehouse_sa_subject_id] # nobody else can assume the role
    }
  }
}

resource "aws_iam_role" "lakehouse_read" {
  name = "bq-lakehouse-read"
  assume_role_policy = data.aws_iam_policy_document.trust.json
  max_session_duration = 43200 # 12-hour sessions, then re-issued
}&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
 &lt;li&gt;Grant read-only, least privilege. The assumed role carries only enough to read the catalog metadata through AWS Glue and access the Iceberg data through S3 Tables, secured entirely by IAM policy and nothing writable: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-plaintext"&gt;statement {
  actions = [
    "glue:Get*",
    "s3tables:GetTable", "s3tables:GetTableData", "s3tables:ListTables", "s3tables:ListTableBuckets", "s3tables:GetTableMetadataLocation", "s3tables:ListNamespaces", "s3tables:GetNamespace","s3tables:GetTableBucket"
  ]
  resources = [var.s3tables_bucket_arn, "${var.s3tables_bucket_arn}/*"]
}&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
 &lt;li&gt;The Google-side catalog is bound to this role. The Lakehouse federated catalog itself is created out of band (a one-time gcloud call), pointed at the preceding role so that every read presents that trusted identity. No AWS keys ever live in Google Cloud: 
  &lt;div class="hide-language"&gt; 
   &lt;pre&gt;&lt;code class="language-plaintext"&gt;gcloud iceberg catalogs create s3tables-glue \
  --federated-catalog-type=GLUE --glue-aws-region=us-east-1 \
  --glue-aws-role-arn=arn:aws:iam::&amp;lt;account&amp;gt;:role/bq-lakehouse-read&lt;/code&gt;&lt;/pre&gt; 
  &lt;/div&gt; &lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Together these four steps are the whole handshake: a trusted issuer, a role that only our service account can assume, a least-privilege read grant, and a catalog bound to that role.&lt;/p&gt; 
&lt;h3 id="operational-lessons-metadata-as-a-first-class-concern"&gt;Operational lessons: Metadata as a first-class concern&lt;/h3&gt; 
&lt;p&gt;Operating an open, federated catalog across clouds taught us to treat table metadata as a first-class operational concern. In practice this means:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Snapshot retention&lt;/strong&gt;: Keeping Iceberg snapshot retention short so that per-table metadata stays compact and synchronizes reliably.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Compaction&lt;/strong&gt;: Standardizing table maintenance (compaction and snapshot expiry) as a uniform, service-managed policy through the S3 Tables built-in maintenance configuration.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Schema evolution&lt;/strong&gt;: When a Protobuf schema evolves (backward-compatible additions), the Spark job appends or removes columns in the Iceberg schema in S3 Tables. The federated catalog picks up the change on its next sync cycle, and BigQuery reflects the changes without manual intervention.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;These are small, well-understood settings once we know how to set them, and they are the difference between a catalog that simply works and one that drifts.&lt;/p&gt; 
&lt;h3 id="consuming-the-data-is-a-choice-of-engine-not-a-choice-of-copy"&gt;Consuming the data is a choice of engine, not a choice of copy&lt;/h3&gt; 
&lt;p&gt;After a source is live, the same Iceberg table is available three ways over one physical dataset.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;A &lt;em&gt;BigQuery user&lt;/em&gt; queries it in standard SQL and joins it to the rest of the Google Cloud warehouse.&lt;/li&gt; 
 &lt;li&gt;An &lt;em&gt;infrastructure engineer&lt;/em&gt; runs the identical query in Amazon Athena for ad hoc checks and continuous integration (CI) validation.&lt;/li&gt; 
 &lt;li&gt;A &lt;em&gt;data scientist&lt;/em&gt; reads the table directly with Spark, with no BigQuery or Athena in the path.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Nobody waits for a nightly export, and nobody reconciles three divergent copies. There is only one.&lt;/p&gt; 
&lt;h2 id="performance-and-cost-impact"&gt;Performance and cost impact&lt;/h2&gt; 
&lt;p&gt;The qualitative benefits are already clear:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Minutes-fresh raw data for near-real-time analytics. The previous architecture’s latency was not a volume problem. It was a design constraint. Ingestion ran every five minutes, but a downstream hourly batch job gated end-to-end freshness to 60–90 minutes. With catalog federation, that same data is queryable within minutes of being produced: under five minutes for 95 percent of events, with the option to tune the pipeline to cover 100% of events for latency-sensitive or mission-critical workloads.&lt;/li&gt; 
 &lt;li&gt;One storage copy in S3 Tables, three compute engines. BigQuery, Athena, and Spark or another Iceberg-compatible engine read a single physical Iceberg dataset in Amazon S3 Tables, avoiding duplicate storage and the reconciliation tax of keeping copies in sync.&lt;/li&gt; 
 &lt;li&gt;No cross-cloud egress on the hot path. Ingestion is local to AWS. The only cross-cloud traffic is read-time metadata synchronization and query reads, not a continuous write stream. Based on an internal comparison of monthly AWS and Google Cloud data-transfer charges, orchestration overhead, multi-layered ETL workflow costs, and storage backup charges, talabat reduced data-movement costs by approximately 40 percent for comparable data volumes. The comparison spanned a two-month period before and after removing the continuous replication pipeline, and the change eliminated hundreds of terabytes of recurring cross-Region and cross-cloud data transfer per month.&lt;/li&gt; 
 &lt;li&gt;Open table format, no lock-in. Because the raw bronze data layer is Apache Iceberg in Amazon S3 Tables, the data isn’t captive to any single query engine or cloud. New consumers adopt it by speaking Iceberg, not by requesting an export.&lt;/li&gt; 
 &lt;li&gt;Governable cross-cloud access. The cross-cloud boundary is secured by an IAM trust relationship (least-privilege, auditable, and revocable) rather than a standing data pipeline. End-user access control within BigQuery is managed separately through the native role-based access control (RBAC) in GCP and fine-grained access controls on the federated catalog.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2 id="future-enhancements"&gt;Future enhancements&lt;/h2&gt; 
&lt;p&gt;Looking ahead, we plan to broaden source coverage by onboarding the remaining high-value event streams and batch stores onto a hybrid one-configuration pattern. We’re formalizing end-to-end freshness objectives and the observability around them: batch-level metrics, dead-letter monitoring, and catalog-synchronization health. We will continue tuning snapshot retention and compaction so the cross-cloud catalog stays fast and reliable as the number of tables grows. More broadly, we intend to make “written once, read by any engine” the default for new datasets beyond the bronze layer, leaning further into open table formats as the connective tissue between cloud service providers.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Being on two clouds is often framed as a problem to migrate away from. It’s simply the terrain for talabat. The event backbone is prominent on AWS, and the analytics community operates on BigQuery. By making Amazon S3 Tables with Apache Iceberg the single source of truth on AWS and letting BigQuery consume it read-only through a Lakehouse federated Iceberg REST catalog secured by cross-cloud IAM trust, we turned a two-cloud constraint into a single governed dataset that engines can read within minutes. The write path stays short, local, and reliable. The cross-cloud concern lives on the read path, where it belongs, expressed as open standards and identity, not as data movement.&lt;/p&gt; 
&lt;p&gt;That is the handshake: one copy of the data on AWS, an open catalog contract, and a signed, trusted, revocable identity reaching across the cloud boundary to read it.&lt;/p&gt; 
&lt;p&gt;This post focuses on reading AWS resident data from BigQuery. For the broader multi-cloud Lakehouse pattern, including federating catalogs from other systems into the AWS Glue Data Catalog, see &lt;a href="https://aws.amazon.com/blogs/big-data/multi-cloud-lakehouse-architecture-on-aws-for-agentic-ai-part-1-architecture-and-best-practices/" target="_blank" rel="noopener"&gt;Multi-cloud Lakehouse architecture on AWS for agentic AI&lt;/a&gt;.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-6102-4-1.png" alt="Harish Ramesh" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Harish Ramesh&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/harish-chander-ramesh/" target="_blank" rel="noopener"&gt;Harish&lt;/a&gt; is a Staff Data Engineer at talabat. His background spreads across building large scale data products for businesses ranging from Retail, HealthCare, Media, Logistics, Hospitality and FMCG. Harish focuses on building and managing data platforms at talabat.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-6102-5-1.png" alt="Raghunandana Krishna Murthy Sanur" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Raghunandana Krishna Murthy Sanur&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/raghunandana-krishnamurthy-664264b7/" target="_blank" rel="noopener"&gt;Raghu&lt;/a&gt; is a Senior Manager for Data Engineering and Machine Learning Platform at talabat. He specializes in leading teams developing Applications, Infrastructure for Data and Machine Learning Platforms.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/13/BDB-6102-6.jpg" alt="Lakshmi Nair" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Lakshmi Nair&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/lakshmi--nair/" target="_blank" rel="noopener"&gt;Lakshmi&lt;/a&gt; is a Principal Analytics Specialist Solutions Architect at AWS. She specializes in designing advanced analytics systems across industries. She focuses on crafting cloud-based data platforms, enabling real-time streaming, big data processing, and robust data governance.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Powering agentic AI with real-time streaming data on AWS</title>
		<link>https://aws.amazon.com/blogs/big-data/powering-agentic-ai-with-real-time-streaming-data-on-aws/</link>
		
		<dc:creator><![CDATA[Mazrim Mehrtens]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 16:42:15 +0000</pubDate>
				<category><![CDATA[Intermediate (200)]]></category>
		<category><![CDATA[Thought Leadership]]></category>
		<guid isPermaLink="false">3458602720f2adebe4ed188cfd0b6cf688a42c3f</guid>

					<description>Agentic AI applications now observe, reason, and act on streaming data in production. This post presents three architecture patterns that form a unified streaming backbone for the agentic AI era: streaming feature engineering with real-time inference, event-driven agent invocation, and real-time context synchronization.</description>
										<content:encoded>&lt;p&gt;Two years ago, the conversation about streaming data and generative AI centered on a straightforward question: how do you feed real-time context into a large language model (LLM) so it can answer questions using fresh data? We explored that question in our 2024 blog post, &lt;a href="https://aws.amazon.com/blogs/big-data/exploring-real-time-streaming-for-generative-ai-applications/" target="_blank" rel="noopener"&gt;“Exploring real-time streaming for generative AI applications,”&lt;/a&gt; which introduced patterns for connecting streaming pipelines to foundation models.&lt;/p&gt; 
&lt;p&gt;The landscape has shifted. Today’s generative AI systems don’t only answer questions. They observe, reason, and act. Agentic AI applications have moved from research prototype to production reality. Agentic AI-powered data pipelines now monitor streaming telemetry, detect anomalies, decide on remediation strategies, and execute actions without human intervention. They maintain memory across sessions, query live data sources on demand, and coordinate with other agents to solve complex problems.&lt;/p&gt; 
&lt;p&gt;This shift demands a fundamentally different relationship between streaming infrastructure and AI. It’s no longer enough to inject context into a prompt. You need architectures where streaming data continuously powers autonomous agent action and keeps a real-time lakehouse fresh for training and retrieval. That data also flows into multiple consumption patterns, such as generative business intelligence (BI) for humans, standardized protocols for agent queries, and proactive memory hydration for low-latency agent context.&lt;/p&gt; 
&lt;p&gt;This post introduces three architectural patterns that together form a unified streaming backbone for the agentic AI era:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Streaming feature engineering → real-time inference → action:&lt;/strong&gt; Continuous data flows build features, invoke AI models, and act in a single pipeline.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Event-driven agent invocation:&lt;/strong&gt; Streaming pipelines detect patterns across millions of events and trigger agentic workflows with full context already assembled.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Real-time context synchronization:&lt;/strong&gt; Change data capture (CDC) and streaming pipelines keep agents’ memory current, so agents can respond instantly rather than making expensive external calls.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;The following sections explore each pattern in depth.&lt;/p&gt; 
&lt;h2 id="pattern-1-streaming-feature-engineering-real-time-inference-action"&gt;Pattern 1: Streaming feature engineering → real-time inference → action&lt;/h2&gt; 
&lt;p&gt;&lt;em&gt;You’re watching a live football match. As a striker receives the ball in the box, AI-generated commentary appears on screen: “This is Smith’s third touch in the penalty area in the last 3 minutes. His conversion rate from this zone is 34% this season.” That insight was computed from streaming event data, passed through a feature pipeline, and fed to a generative AI model. All of this happened within the time it takes the striker to turn and shoot.&lt;/em&gt;&lt;/p&gt; 
&lt;p&gt;This pattern combines two capabilities that are often treated separately: using real-time data to continuously improve AI models, and using real-time data to invoke those models for immediate action. The streaming pipeline does both: it builds the features that train the model &lt;em&gt;and&lt;/em&gt; the features that drive inference.&lt;/p&gt; 
&lt;p&gt;Streaming events (user interactions, sensor readings, game events, and transaction records) flow into &lt;a href="https://aws.amazon.com/msk/" target="_blank" rel="noopener"&gt;Amazon Managed Streaming for Apache Kafka&lt;/a&gt; (Amazon MSK) or &lt;a href="https://aws.amazon.com/kinesis/data-streams/" target="_blank" rel="noopener"&gt;Amazon Kinesis Data Streams&lt;/a&gt;. &lt;a href="https://aws.amazon.com/managed-service-apache-flink/" target="_blank" rel="noopener"&gt;Amazon Managed Service for Apache Flink&lt;/a&gt; processes these events through windowed aggregations (tumbling windows, sliding windows, or session windows) to produce features: rolling averages, counts, ratios, behavioral sequences, or other derived signals relevant to your use case.&lt;/p&gt; 
&lt;p&gt;These features serve two paths simultaneously:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;The inference path:&lt;/strong&gt; At the end of each window (or on each event, depending on your latency requirements), features are passed to a generative AI or machine learning (ML) inference endpoint: &lt;a href="https://aws.amazon.com/bedrock" target="_blank" rel="noopener"&gt;Amazon Bedrock&lt;/a&gt; for generative output, or &lt;a href="https://aws.amazon.com/sagemaker/" target="_blank" rel="noopener"&gt;Amazon SageMaker&lt;/a&gt; for custom models. The model produces a result (commentary, a recommendation, a personalization decision, or a risk score) and the pipeline acts: posting content to a user, updating a recommendation feed, sending a notification, or writing to a downstream system.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;The training path&lt;/strong&gt;: The same streaming features are continuously written to a real-time data warehouse or lakehouse such as Apache Iceberg tables on &lt;a href="https://aws.amazon.com/s3/features/tables/" target="_blank" rel="noopener"&gt;Amazon S3 Tables&lt;/a&gt;, a capability of &lt;a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener"&gt;Amazon Simple Storage Service (Amazon S3)&lt;/a&gt;, that keeps training datasets fresh. &lt;a href="https://aws.amazon.com/sagemaker/lakehouse/" target="_blank" rel="noopener"&gt;Amazon SageMaker lakehouse architecture&lt;/a&gt; provides unified access for training jobs and fine-tuning pipelines. As new data streams in, your models can be retrained or fine-tuned on data that’s minutes old rather than days old. This matters for domains where patterns shift quickly, such as fraud detection, personalization, and industry dynamics.&lt;/p&gt; 
&lt;p&gt;Amazon S3 Tables handles the Iceberg table management automatically, including compaction, snapshot management, and metadata optimization. Your team focuses on feature logic rather than storage operations. The &lt;a href="https://docs.aws.amazon.com/glue/latest/dg/catalog-and-crawler.html" target="_blank" rel="noopener"&gt;AWS Glue Data Catalog&lt;/a&gt; makes these tables discoverable across training jobs, inference pipelines, and analytics consumers. Glue Data Catalog supports business context and semantic search. This context helps models discover and select the right data asset for any given task.&lt;/p&gt; 
&lt;h3 id="scenarios"&gt;Scenarios&lt;/h3&gt; 
&lt;p&gt;&lt;strong&gt;Real-time sports commentary:&lt;/strong&gt; Streaming game events (passes, shots, player positions) flow through Apache Flink on Managed Service for Apache Flink, which computes rolling features (possession percentage, shot frequency by zone, player heat maps). These features feed a generative AI model through Amazon Bedrock that produces natural-language commentary and statistical insights in real time. Simultaneously, the features are written to S3 Tables to improve the model’s understanding of game patterns over time.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Streaming personalization:&lt;/strong&gt; User clickstream data flows through Managed Service for Apache Flink, which computes behavioral features (session duration, category affinity scores, recency-weighted purchase history). These features invoke a personalization model that updates the user’s experience in real time by reranking product recommendations, adjusting content feeds, or triggering targeted offers. The same features feed the lakehouse to retrain the personalization model nightly.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/12/BDB-5947-1.png" alt="Streaming data flows through Managed Service for Apache Flink, then forks into a real-time inference path and a training path" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: Streaming feature engineering feeding a real-time inference path and a continuous training path&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="pattern-2-event-driven-agent-invocation"&gt;Pattern 2: Event-driven agent invocation&lt;/h2&gt; 
&lt;p&gt;&lt;em&gt;At 2:47 AM, a pressure sensor on a manufacturing line begins drifting. Within seconds, a streaming pipeline detects the anomaly, assembles full context (device history, maintenance schedule, correlated sensor readings), and invokes an agent that opens a maintenance work order, adjusts the device’s sampling rate, and notifies the on-call engineer. All of this happens before a human sees an alert.&lt;/em&gt;&lt;/p&gt; 
&lt;p&gt;Pattern 1 invokes inference on every window or event. It runs continuously. Pattern 2 adds to this approach: the streaming pipeline continuously &lt;em&gt;analyzes&lt;/em&gt; data and invokes an agentic workflow when specific conditions are met or a pattern is detected. The pipeline is the sensor. The agent is the responder. Dynamic rules are the bridge between them.&lt;/p&gt; 
&lt;p&gt;The key distinction is that the events and triggers are dynamic. They’re defined by rules programmed into the streaming pipeline or traditional ML models for prediction or detection. The pipeline determines &lt;em&gt;when&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; the agent is triggered, making the system fluid and adaptive. You can update detection logic without redeploying the agent. You can add new anomaly patterns without changing the response logic.&lt;/p&gt; 
&lt;p&gt;Streaming telemetry flows into Amazon MSK or &lt;a href="https://aws.amazon.com/pm/kinesis/" target="_blank" rel="noopener"&gt;Amazon Kinesis Data Streams&lt;/a&gt;. Managed Service for Apache Flink runs continuous anomaly-detection logic, such as statistical models, windowed aggregations, threshold-based rules, or ML-based scoring. Critically, when Flink detects an anomaly, it doesn’t only publish a raw alert. It assembles a &lt;em&gt;context package&lt;/em&gt;: the anomaly details, relevant historical data, correlated signals from other streams, and metadata the agent needs to act immediately.&lt;/p&gt; 
&lt;p&gt;This context package is published to a downstream topic and consumed by an &lt;a href="https://aws.amazon.com/bedrock/agentcore/" target="_blank" rel="noopener"&gt;Amazon Bedrock AgentCore&lt;/a&gt; agent. Because the pipeline has already assembled full context, the agent doesn’t waste time gathering information. It can reason and act immediately. AgentCore Runtime hosts the agent, AgentCore Observability provides tracing and logging, and AgentCore Memory maintains state across invocations (so the agent knows, for example, that this is the third anomaly from this device this week).&lt;/p&gt; 
&lt;p&gt;The benefit of this pattern over a polling-based or scheduled approach is twofold:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Latency:&lt;/strong&gt; The agent is invoked within seconds of the anomaly, not at the next polling interval.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Context richness:&lt;/strong&gt; The pipeline has already done the work of correlating signals and assembling context. A polling-based agent would need to make multiple queries to reconstruct what the pipeline already knows.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;The rules that trigger invocation are a powerful abstraction. They can be simple thresholds (“temperature exceeds 95°C”), statistical (“value deviates more than 3σ from the rolling mean”), or ML-based (“anomaly score from an embedded model exceeds 0.85”). You can update these rules dynamically by adding new detection patterns, adjusting sensitivity, or routing different anomaly types to different agents.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/12/BDB-5947-2.png" alt="Managed Service for Apache Flink detects anomalies and sends a context package to an Amazon Bedrock AgentCore agent that acts on them" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 2: Event-driven agent invocation triggered by anomaly detection in the streaming pipeline&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="pattern-3-real-time-agent-context"&gt;Pattern 3: Real-time agent context&lt;/h2&gt; 
&lt;p&gt;&lt;em&gt;A customer messages their bank: “Was that $847 charge at the airport legitimate?” The agent responds in under two seconds with full context (the customer’s recent travel pattern, the merchant’s fraud-risk score, and the transaction details) because all of this was already loaded into the agent’s context layer through streaming CDC. A reactive agent without this synchronization would need to make five separate API calls across three systems, taking 8–12 seconds and risking timeout failures.&lt;/em&gt;&lt;/p&gt; 
&lt;p&gt;This pattern addresses a fundamental question: how proactive should your agent be about gathering context?&lt;/p&gt; 
&lt;p&gt;A proactive agent has the full context, continuously synchronized with the state of the world. When a user asks a question, the agent already has the relevant knowledge from context. It responds from memory rather than making expensive external calls. A reactive agent starts cold. It knows nothing until it queries for information, making multiple calls across security boundaries, handling authentication, and stitching together data from disparate sources. For latency-sensitive use cases, where a user sends a prompt and expects a fast response, this difference is critical.&lt;/p&gt; 
&lt;p&gt;Real-time context synchronization uses CDC and streaming pipelines to keep agent memory current. The agent’s knowledge graph becomes a synchronized replica of the distributed systems it needs to reason about.&lt;/p&gt; 
&lt;p&gt;No agent is purely proactive or purely reactive. The design decision is: &lt;em&gt;what data should be pre-loaded, and what should be fetched on demand?&lt;/em&gt; This is a spectrum, and where you land depends on three factors:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Latency sensitivity:&lt;/strong&gt; If users expect fast, contextually relevant responses, pre-load the data the agent needs most frequently.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Data volume:&lt;/strong&gt; Synchronizing everything is impractical. An efficient, fast search that still produces accurate results matters more than exhaustive pre-loading. Be selective about what you push.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Data freshness requirements:&lt;/strong&gt; Some data changes every second (stock prices, session state). Other data changes rarely (customer preferences, account configuration). Load what changes frequently and matters immediately.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Streaming pipelines (Managed Flink reading from Amazon MSK, Kinesis Data Streams, or CDC streams from operational databases) continuously process events and write aggregated results to the agent’s knowledge graph, or the context layer. These stores can take multiple forms depending on your access patterns:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/machine-learning/context-intelligence-for-your-data-and-ai-agents-at-scale/" target="_blank" rel="noopener"&gt;&lt;strong&gt;AWS Context&lt;/strong&gt;&lt;/a&gt; automatically maps relationships across your existing data into a knowledge graph and supports agentic search so AI agents can access governed data relationships, business rules, and domain knowledge at runtime. Data stewards manage the graph through an intuitive console, reviewing inferred relationships, promoting them to production, and attaching domain-specific knowledge like business definitions and usage rules.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/bedrock/agentcore/" target="_blank" rel="noopener"&gt;&lt;strong&gt;Amazon Bedrock AgentCore Memory&lt;/strong&gt;&lt;/a&gt; for structured agent context that persists across sessions.&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/dynamodb" target="_blank" rel="noopener"&gt;&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt;&lt;/a&gt; for low-latency key-value lookups (customer profiles, account state).&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/opensearch-service/features/serverless/" target="_blank" rel="noopener"&gt;&lt;strong&gt;Amazon OpenSearch Serverless&lt;/strong&gt;&lt;/a&gt; for semantic search over unstructured context (past conversations, documents).&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/neptune/" target="_blank" rel="noopener"&gt;&lt;strong&gt;Amazon Neptune&lt;/strong&gt;&lt;/a&gt; for relationship-rich data (knowledge graph).&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://aws.amazon.com/s3/features/tables/" target="_blank" rel="noopener"&gt;&lt;strong&gt;Amazon S3 Tables&lt;/strong&gt;&lt;/a&gt; fully managed Apache Iceberg tables in Amazon S3, for interoperability between multiple query engines.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;For data that isn’t pre-loaded, the agent falls back to on-demand retrieval. This applies when the data is too large, changes too rarely to justify streaming, or is needed only in edge cases. The Model Context Protocol (MCP) provides a standardized interface for this. MCP servers expose heterogeneous data sources through a uniform protocol. The agent queries MCP when it needs context that isn’t in its synchronized memory.&lt;/p&gt; 
&lt;p&gt;This same real-time context synchronization pattern serves different consumers:&lt;/p&gt; 
&lt;p&gt;AI agents access fresh context through a real-time knowledge graph or a context layer, and MCP servers (pull tier), as in the preceding sections.&lt;/p&gt; 
&lt;p&gt;Human analysts and executives access the same context layer, which can directly query Apache Iceberg tables on S3 Tables through its direct query mode. Amazon Quick chat provides natural-language access to real-time lakehouse data. No intermediate warehouse is required. This is the generative BI expression of the same underlying pattern: streaming data keeps the lakehouse current, and Amazon Quick gives humans conversational access to it.&lt;/p&gt; 
&lt;p&gt;Training and fine-tuning pipelines access the synchronized lakehouse through Amazon SageMaker Lakehouse, keeping models fresh (as described in Pattern 1).&lt;/p&gt; 
&lt;p&gt;The underlying principle is the same across consumers: streaming pipelines synchronize distributed data into accessible stores, and each consumer accesses those stores through the interface that fits their needs.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/12/BDB-5947-3.png" alt="A streaming synchronization layer feeds multiple stores that serve AI agents, human analysts, and training pipelines" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 3: Real-time context synchronization serving agents, analysts, and training pipelines from shared stores&lt;/p&gt;
&lt;/div&gt; 
&lt;h2 id="bringing-it-together"&gt;Bringing it together&lt;/h2&gt; 
&lt;p&gt;The three patterns in this post form a unified architecture built on a single streaming backbone:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Pattern 1&lt;/strong&gt; uses streaming pipelines to build features that simultaneously drive real-time inference and keep training data fresh. Your models improve continuously while serving predictions in real time.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Pattern 2&lt;/strong&gt; uses streaming pipelines as intelligent sensors that detect anomalies and invoke agents with full context already assembled. This separates detection logic from response logic for maximum flexibility.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Pattern 3&lt;/strong&gt; uses streaming pipelines to synchronize distributed system state into the agent’s context layer, making agents more proactive and serving multiple consumers (agents, humans, and training jobs) from the same pre-loaded data.&lt;/p&gt; 
&lt;p&gt;The streaming infrastructure you build (Amazon MSK, Amazon Kinesis Data Streams, Amazon Managed Service for Apache Flink, and Amazon S3 Tables) serves all three patterns simultaneously. A Flink application can compute features for inference (Pattern 1), detect anomalies that trigger agents (Pattern 2), and synchronize state into agent memory (Pattern 3).&lt;/p&gt; 
&lt;p&gt;To get hands on with the patterns described in this post, refer to &lt;a href="https://catalog.workshops.aws/spot-anomalies-flink-lab" target="_blank" rel="noopener"&gt;Agentic AI-Powered anomaly detection: Spotting anomalies in real-time&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;You don’t need to implement all three patterns at once. Start with the one that addresses your most pressing need. But design your streaming infrastructure knowing it will serve multiple patterns. In the agentic AI era, every stream is a potential input to an agent, a model, and a human decision-maker.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/03/24/bdb-5775-mmehrten-headshot.png" alt="Mazrim Mehrtens" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Mazrim Mehrtens&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/mmehrtens/" target="_blank" rel="noopener"&gt;Mazrim&lt;/a&gt; is a Sr.&amp;nbsp;Specialist Solutions Architect for messaging and streaming workloads. Mazrim works with customers to build and support systems that process and analyze terabytes of streaming data in real time, run enterprise Machine Learning pipelines, and create systems to share data across teams seamlessly with varying data toolsets and software stacks.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2025/08/31/BDB-4572-Ali-Alemi.png" alt="Ali Alemi" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ali Alemi&lt;/h3&gt; 
  &lt;p&gt;&lt;a href="https://www.linkedin.com/in/ali-alemi-11869b53/" target="_blank" rel="noopener"&gt;Ali&lt;/a&gt; is a Principal Streaming Solutions Architect at AWS. Ali advises AWS customers with architectural best practices and helps them design real-time analytics data systems which are reliable, secure, efficient, and cost-effective. Prior to joining AWS, Ali supported several public sector customers and AWS consulting partners in their application modernization journey and migration to the Cloud.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
		<item>
		<title>Amazon MSK simplifies configuring custom domain names</title>
		<link>https://aws.amazon.com/blogs/big-data/amazon-msk-simplifies-configuring-custom-domain-names/</link>
		
		<dc:creator><![CDATA[Ali Alemi]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 16:35:06 +0000</pubDate>
				<category><![CDATA[Amazon Managed Streaming for Apache Kafka (Amazon MSK)]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Expert (400)]]></category>
		<guid isPermaLink="false">6cffbe31d12305c5a6a5ca7a9a07c7c21cca96f0</guid>

					<description>With Amazon MSK, you can now configure custom domain names for provisioned clusters using a single configuration property that works identically on ZooKeeper and KRaft. Define the domain once and Amazon MSK applies it across every broker, so custom domain names keep working as the cluster scales.</description>
										<content:encoded>&lt;p&gt;Previously, &lt;a href="https://aws.amazon.com/blogs/big-data/configure-a-custom-domain-name-for-your-amazon-msk-cluster/" target="_blank" rel="noopener"&gt;you had to manually override the advertised listener on each broker&lt;/a&gt; and repeat it every time a broker was added. This approach was operationally heavy and could not be implemented on a cluster in KRaft mode. With &lt;a href="https://aws.amazon.com/msk/" target="_blank" rel="noopener"&gt;Amazon Managed Streaming for Apache Kafka (Amazon MSK)&lt;/a&gt;, you can now configure custom domain names for your Provisioned clusters using a single property. This works for clusters in both ZooKeeper and KRaft mode. Now you define the domain once and Amazon MSK applies it across every broker, so custom domain names keep working through scaling of the MSK cluster.&lt;/p&gt; 
&lt;h2 id="custom-domain-names-on-amazon-msk"&gt;Custom domain names on Amazon MSK&lt;/h2&gt; 
&lt;p&gt;Amazon MSK is a fully managed service for building and running applications that use Apache Kafka to process streaming data. By default, Amazon MSK brokers advertise addresses that AWS generates (for example, &lt;code&gt;b-1.cluster-name.kafka.us-east-1.amazonaws.com&lt;/code&gt;) to connecting clients. These addresses are unique to each cluster and change when a cluster is recreated.&lt;/p&gt; 
&lt;p&gt;Many organizations need a static, customer-controlled endpoint that stays the same regardless of the underlying cluster. They achieve this with a custom domain name, so that they can:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Route traffic through Network Load Balancers (NLBs) when IP exhaustion forces clusters into non-routable subnets.&lt;/li&gt; 
 &lt;li&gt;Avoid client reconfiguration during cluster migrations, so clients keep the same endpoint even when the underlying cluster changes.&lt;/li&gt; 
 &lt;li&gt;Simplify disaster recovery (DR) failover, where the same domain fronts both primary and standby clusters.&lt;/li&gt; 
 &lt;li&gt;Align with organizational naming, security, and compliance conventions.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Until now, the only way to do this was to override the &lt;code&gt;advertised.listeners&lt;/code&gt; on each broker using the &lt;code&gt;kafka-configs.sh --alter&lt;/code&gt; tool. It required carefully preserving every internal listener and re-running that override every time a broker was added. This works, but it accepts any string with no validation. A single typo can cause an outage. It requires manual, per-broker steps with no cluster-wide mechanism. It cannot be managed through infrastructure as code, and it could not be implemented on Amazon MSK brokers in KRaft mode. This blocked customers who rely on custom domain names from using them on KRaft-based clusters. With this launch, a single configuration property replaces all of that.&lt;/p&gt; 
&lt;h2 id="what-you-set-up-and-what-amazon-msk-manages"&gt;What you set up, and what Amazon MSK manages&lt;/h2&gt; 
&lt;p&gt;A working custom domain name has two parts, and understanding this split up front helps the rest of this post make sense. You own the client connectivity and trust layer. Amazon MSK owns the cluster-side advertised listener configuration. The following diagram shows the client connectivity and trust layer.&lt;/p&gt; 
&lt;div style="width: 810px" class="wp-caption alignnone"&gt;
 &lt;img src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/14/BDB-6143-1.png" alt="Diagram of the client connectivity and trust layer you manage and the advertised listener configuration Amazon MSK manages" width="800"&gt;
 &lt;p class="wp-caption-text"&gt;&lt;br&gt;Figure 1: The client connectivity and trust layer (left) is a prerequisite you own and manage. The advertised listener configuration on the cluster (right) is what Amazon MSK manages for you&lt;/p&gt;
&lt;/div&gt; 
&lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; When you apply &lt;code&gt;custom.advertised.listeners&lt;/code&gt;, your custom domain name replaces the default addresses that clients use to connect to broker nodes. If the networking and trust layer is not already in place, resolvable, reachable, and trusted from the client, the client cannot reconnect, even though it was connected moments earlier.&lt;/p&gt;
&lt;/blockquote&gt; 
&lt;h3 id="part-1-the-client-connectivity-and-trust-layer-you-manage"&gt;Part 1: The client connectivity and trust layer (you manage)&lt;/h3&gt; 
&lt;p&gt;The &lt;a href="#prerequisites"&gt;Prerequisites&lt;/a&gt; section below shows the key requirements. You can find the detailed setup in an existing post, &lt;a href="https://aws.amazon.com/blogs/big-data/configure-a-custom-domain-name-for-your-amazon-msk-cluster/" target="_blank" rel="noopener"&gt;Configure a custom domain name for your Amazon MSK cluster&lt;/a&gt;, which includes a diagrammed walkthrough of the NLB, Amazon Route 53, and AWS Certificate Manager (ACM) topology.&lt;/p&gt; 
&lt;h3 id="part-2-the-advertised-listener-configuration-amazon-msk-managed"&gt;Part 2: The advertised listener configuration (Amazon MSK managed)&lt;/h3&gt; 
&lt;p&gt;After the connectivity layer exists, you tell the brokers which custom address to advertise to clients. This is the part that used to require a per-broker CLI override, and it is what this launch simplifies. This next section describes how it works.&lt;/p&gt; 
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt; 
&lt;p&gt;Before a client can reach your brokers through a custom domain, the connectivity and trust path must exist. You create and manage this layer. It covers three things:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Networking&lt;/strong&gt;: A network gateway, like a Network Load Balancer (NLB), TLS certificate, DNS records, and security groups that route traffic from your custom domain to your broker IPs.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Certificate trust&lt;/strong&gt;: The client’s truststore must include certificate authorities in the path (the load balancer’s custom-domain cert).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;DNS resolution&lt;/strong&gt;: Clients must resolve the custom domain to your NLB, typically through an Amazon Route 53 private hosted zone associated with the client virtual private cloud (VPC).&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;This layer must be in place for custom domain names to function. It is a prerequisite for this feature to work.&lt;/p&gt; 
&lt;h2 id="how-it-works"&gt;How it works&lt;/h2&gt; 
&lt;p&gt;You add a property to your Amazon MSK configuration. The value takes the form:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;custom.advertised.listeners=&amp;lt;LISTENER&amp;gt;://&amp;lt;hostname&amp;gt;:&amp;lt;port&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;where &lt;code&gt;&amp;lt;LISTENER&amp;gt;&lt;/code&gt; is one of your cluster’s client listeners and &lt;code&gt;&amp;lt;hostname&amp;gt;:&amp;lt;port&amp;gt;&lt;/code&gt; is the custom address pattern. For example, on an IAM cluster:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;custom.advertised.listeners=CLIENT_IAM://b-{broker_id}.example.com:9000+{broker_id}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;The property specifies two things:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;Each listener corresponds to an authentication type on your cluster. Custom advertised endpoints can be set only for client listeners: &lt;code&gt;CLIENT&lt;/code&gt;, &lt;code&gt;CLIENT_SECURE&lt;/code&gt;, &lt;code&gt;CLIENT_SECURE_PUBLIC&lt;/code&gt;, &lt;code&gt;CLIENT_SASL_SCRAM&lt;/code&gt;, &lt;code&gt;CLIENT_SASL_SCRAM_PUBLIC&lt;/code&gt;, &lt;code&gt;CLIENT_IAM&lt;/code&gt;, and &lt;code&gt;CLIENT_IAM_PUBLIC&lt;/code&gt;. Internal listeners (&lt;code&gt;REPLICATION&lt;/code&gt;, &lt;code&gt;CONTROLLER&lt;/code&gt;) are not supported and are rejected at validation. The listener you specify must also be bound (active) on your cluster. For example, if your cluster uses only IAM authentication, specifying &lt;code&gt;CLIENT_SECURE&lt;/code&gt; is rejected, and the error message lists the valid client listeners for your cluster.&lt;/li&gt; 
 &lt;li&gt;A custom &lt;code&gt;hostname:port&lt;/code&gt; pattern that includes the &lt;code&gt;{broker_id}&lt;/code&gt; template variable. Each broker resolves to a unique address. In this pattern, the &lt;code&gt;{broker_id}&lt;/code&gt; template variable is replaced with each broker’s numeric ID. The port number &lt;code&gt;9000+{broker_id}&lt;/code&gt; means the broker ID is added to the base port 9000, so broker 1 resolves to 9001, broker 2 to 9002, broker 10 to 9010, and so on. The base port 9000 is only an example. You can use any base port, as long as the resulting ports match the TLS listeners you provisioned on your NLB.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;blockquote&gt;
 &lt;p&gt;&lt;code&gt;{broker_id}&lt;/code&gt; can appear in the hostname, the port, or both, as long as each broker’s resolved &lt;code&gt;host:port&lt;/code&gt; is unique. Placing it in the port alone is valid, so a shared hostname with a per-broker port also works:&lt;/p&gt; 
 &lt;div class="hide-language"&gt; 
  &lt;pre&gt;&lt;code class="language-plaintext"&gt;custom.advertised.listeners=CLIENT_IAM://example.com:9000+{broker_id}&lt;/code&gt;&lt;/pre&gt; 
 &lt;/div&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;Before you begin, you need an MSK configuration to hold this property. You create one with the &lt;code&gt;CreateConfiguration&lt;/code&gt; API (or the AWS Management Console), passing your server properties as the configuration body. MSK returns a configuration ARN and a revision number, which together identify the exact configuration you apply to the cluster.&lt;/p&gt; 
&lt;p&gt;&lt;code&gt;custom.advertised.listeners&lt;/code&gt; does not need its own standalone configuration. You can include it alongside any other broker-level properties MSK already supports, such as &lt;code&gt;auto.create.topics.enable&lt;/code&gt;, &lt;code&gt;num.partitions&lt;/code&gt;, or log-retention settings, within a single configuration revision. If you already manage an MSK configuration for your cluster, add &lt;code&gt;custom.advertised.listeners&lt;/code&gt; to it and create a new revision using the &lt;code&gt;UpdateConfiguration&lt;/code&gt; API. No separate configuration is needed.&lt;/p&gt; 
&lt;p&gt;You then apply the configuration to your cluster with the &lt;code&gt;UpdateClusterConfiguration&lt;/code&gt; API. Amazon MSK then performs three actions:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Validates the configuration.&lt;/li&gt; 
 &lt;li&gt;Resolves the pattern for each broker.&lt;/li&gt; 
 &lt;li&gt;Applies it through a rolling restart across the cluster.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;These safeguards prevent you from accidentally removing or modifying the internal listeners that Amazon MSK manages. Validation is synchronous. The listener must be a client-facing listener, the pattern must include &lt;code&gt;{broker_id}&lt;/code&gt;, and each broker’s resolved &lt;code&gt;host:port&lt;/code&gt; must be unique. If any check fails, the API returns a descriptive error and makes no change.&lt;/p&gt; 
&lt;p&gt;The override affects only the advertised address of the named listener. Replication, authentication, multi-VPC (&lt;code&gt;CLIENT_IAM_VPCE&lt;/code&gt;), and AWS PrivateLink connectivity remain unaffected. The change is also fully reversible: remove the &lt;code&gt;custom.advertised.listeners&lt;/code&gt; property and re-apply the configuration, and Amazon MSK reverts the listener to its original address.&lt;/p&gt; 
&lt;p&gt;You can track progress with the &lt;code&gt;DescribeOperation&lt;/code&gt; API, which shows state transitions from &lt;code&gt;UPDATE_IN_PROGRESS&lt;/code&gt; to &lt;code&gt;UPDATE_COMPLETE&lt;/code&gt; or &lt;code&gt;UPDATE_FAILED&lt;/code&gt;. If a broker fails to start, the rollout halts at that broker, the remaining brokers keep their previous configuration, and you can fix the property and re-apply to recover.&lt;/p&gt; 
&lt;h2 id="setting-up-a-custom-domain-name-end-to-end"&gt;Setting up a custom domain name end to end&lt;/h2&gt; 
&lt;p&gt;When you apply &lt;code&gt;custom.advertised.listeners&lt;/code&gt;, your custom domain name replaces the default addresses that clients use to connect to broker nodes. If the networking and trust layer is not already in place, resolvable, reachable, and trusted from the client, the client cannot reconnect, even though it was connected moments earlier.&lt;/p&gt; 
&lt;p&gt;The networking layer, the Network Load Balancer (NLB), DNS, and TLS certificate that route traffic from your custom domain to your broker IPs, is a prerequisite you own. It is not specific to this launch. The existing post &lt;a href="https://aws.amazon.com/blogs/big-data/configure-a-custom-domain-name-for-your-amazon-msk-cluster/" target="_blank" rel="noopener"&gt;Configure a custom domain name for your Amazon MSK cluster&lt;/a&gt; covers it in detail, with a diagrammed walkthrough of the NLB, Route 53, and ACM topology. With the networking in place, the following steps cover the cluster-side setup this launch introduces.&lt;/p&gt; 
&lt;h3 id="step-1-add-the-custom-domain-to-your-amazon-msk-configuration"&gt;Step 1: Add the custom domain to your Amazon MSK configuration&lt;/h3&gt; 
&lt;p&gt;Create or update an Amazon MSK configuration that includes the &lt;code&gt;custom.advertised.listeners&lt;/code&gt; property, matching the hostnames and ports you provisioned on the NLB. For a three-broker IAM cluster fronted by an NLB with ports 9001–9003, put the property in a file:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-plaintext"&gt;custom.advertised.listeners=CLIENT_IAM://b-{broker_id}.example.com:9000+{broker_id}&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Then create the configuration, passing the file as the server properties:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws kafka create-configuration \
    --name "custom-domain-iam" \
    --description "Custom advertised listeners for CLIENT_IAM" \
    --server-properties fileb://custom-domain-config.txt&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;Use &lt;code&gt;fileb://&lt;/code&gt; (not &lt;code&gt;file://&lt;/code&gt;) so the CLI reads the file as bytes and base64-encodes it. Passing the value inline is fragile because of the &lt;code&gt;{broker_id}&lt;/code&gt; braces. Leave &lt;code&gt;{broker_id}&lt;/code&gt; literal in the file. Amazon MSK resolves it per broker at apply time. The response returns the configuration ARN and &lt;code&gt;LatestRevision.Revision&lt;/code&gt;, which you use in the next step.&lt;/p&gt; 
&lt;h3 id="step-2-apply-the-configuration"&gt;Step 2: Apply the configuration&lt;/h3&gt; 
&lt;p&gt;Apply the configuration to your cluster with &lt;code&gt;UpdateClusterConfiguration&lt;/code&gt;, using the console, AWS Command Line Interface (AWS CLI), AWS CloudFormation, CDK, or Terraform. This is the same workflow you already use for broker configuration changes.&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws kafka update-cluster-configuration \
    --cluster-arn &amp;lt;your-cluster-arn&amp;gt; \
    --configuration-info arn=&amp;lt;configuration-arn&amp;gt;,revision=&amp;lt;revision&amp;gt; \
    --current-version &amp;lt;current-cluster-version&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;If the configuration fails to apply, review the errors. For details, see the troubleshooting section in the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/what-is-msk.html" target="_blank" rel="noopener"&gt;Amazon MSK Developer Guide&lt;/a&gt;.&lt;/p&gt; 
&lt;h3 id="step-3-track-the-rollout"&gt;Step 3: Track the rollout&lt;/h3&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;aws kafka describe-cluster-operation-v2 \
    --cluster-operation-arn &amp;lt;operation-arn&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;After the configuration is accepted, Amazon MSK applies it through a rolling restart. Wait until the operation reports &lt;code&gt;SUCCESS&lt;/code&gt;. If it reports &lt;code&gt;FAILED&lt;/code&gt;, a broker could not apply the change. The rollout halts at that broker, the remaining brokers keep their previous configuration, and you can fix the configuration and re-apply to recover.&lt;/p&gt; 
&lt;h3 id="step-4-verify"&gt;Step 4: Verify&lt;/h3&gt; 
&lt;p&gt;Confirm clients can connect through the custom domain:&lt;/p&gt; 
&lt;div class="hide-language"&gt; 
 &lt;pre&gt;&lt;code class="language-bash"&gt;kafka-topics.sh --list --bootstrap-server b-1.example.com:9001&lt;/code&gt;&lt;/pre&gt; 
&lt;/div&gt; 
&lt;p&gt;If your topic list is returned, clients are successfully connecting through your custom domain. If the operation reported &lt;code&gt;SUCCESS&lt;/code&gt; but clients cannot connect, the cluster-side configuration is correct, but your networking layer likely needs attention.&lt;/p&gt; 
&lt;h2 id="client-connectivity-during-rollout"&gt;Client connectivity during rollout&lt;/h2&gt; 
&lt;p&gt;This step is important. Clients can be disconnected if the networking is not ready. Kafka clients do not keep using the original address they bootstrapped with. On a periodic &lt;a href="https://kafka.apache.org/documentation/#producerconfigs_metadata.max.age.ms" target="_blank" rel="noopener"&gt;metadata refresh&lt;/a&gt;, each client learns the broker’s &lt;a href="https://kafka.apache.org/documentation/#brokerconfigs_advertised.listeners" target="_blank" rel="noopener"&gt;advertised listener&lt;/a&gt;. The client uses that address for all subsequent connections. When you apply a custom domain name, that advertised address changes from the default name that Amazon MSK generates to your custom domain, so at the next metadata refresh every client connects over the custom domain. For this reason, the connectivity and trust layer described in &lt;a href="#what-you-set-up-and-what-amazon-msk-manages"&gt;What you set up, and what Amazon MSK manages&lt;/a&gt; is a prerequisite, not a follow-up task.&lt;/p&gt; 
&lt;p&gt;The safe sequence, which is also how customers move from Amazon DNS to a custom domain today, is two phases:&lt;/p&gt; 
&lt;ol type="1"&gt; 
 &lt;li&gt;&lt;strong&gt;Build the networking path first:&lt;/strong&gt; Stand up the NLB, DNS, and certificate, and point your clients at the custom bootstrap endpoint, but do not set the advertised listener yet. Clients bootstrap through the custom endpoint while still connecting to brokers over the addresses that Amazon MSK generates.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Configure the advertised listener:&lt;/strong&gt; With the path already in place, applying &lt;code&gt;custom.advertised.listeners&lt;/code&gt; changes what the brokers advertise. At the next metadata refresh, clients pick up the custom domain and cut over to it automatically.&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Because the path already exists, this cutover is transparent: as Amazon MSK applies the change broker by broker, clients reconnect on their own, with no restart or reconfiguration.&lt;/p&gt; 
&lt;h2 id="scaling-and-replacement-of-brokers"&gt;Scaling and replacement of brokers&lt;/h2&gt; 
&lt;p&gt;When you scale the cluster or a broker is replaced during automated healing, Amazon MSK automatically applies the configuration to the new broker, resolving &lt;code&gt;{broker_id}&lt;/code&gt; for its ID, with no manual steps required on the cluster side. Remember to add the corresponding NLB listener, target group, and DNS record for any new broker, because the networking layer does not auto-scale.&lt;/p&gt; 
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt; 
&lt;p&gt;Custom domain name configuration turns a per-broker CLI workaround into a single, validated, cluster-wide Amazon MSK configuration property. It works identically on ZooKeeper and KRaft, persists through scaling and failover, and flows through your existing Terraform, CloudFormation, and CLI workflows. If you rely on custom domain names, we recommend adopting the static configuration now.&lt;/p&gt; 
&lt;p&gt;This capability is available on all Amazon MSK Provisioned clusters with Standard and Express brokers, in all AWS Regions where Amazon MSK Provisioned is available. To get started, see the &lt;a href="https://docs.aws.amazon.com/msk/latest/developerguide/what-is-msk.html" target="_blank" rel="noopener"&gt;Amazon MSK Developer Guide&lt;/a&gt; and the end-to-end networking walkthrough in &lt;a href="https://aws.amazon.com/blogs/big-data/configure-a-custom-domain-name-for-your-amazon-msk-cluster/" target="_blank" rel="noopener"&gt;Configure a custom domain name for your Amazon MSK cluster&lt;/a&gt;.&lt;/p&gt; 
&lt;hr&gt; 
&lt;h2&gt;About the authors&lt;/h2&gt; 
&lt;footer&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/14/BDB-6143-2.jpeg" alt="Ali Alemi" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Ali Alemi&lt;/h3&gt; 
  &lt;p&gt;Ali is a Streaming Specialist Solutions Architect at AWS. Ali advises AWS customers with architectural best practices and helps them design real-time analytics data systems. Prior to joining AWS, Ali supported several public sector customers and AWS consulting partners in their application modernization journey and migration to the cloud.&lt;/p&gt; 
 &lt;/div&gt; 
 &lt;div class="blog-author-box"&gt; 
  &lt;div class="blog-author-image"&gt; 
   &lt;p&gt;&lt;img loading="lazy" class="alignleft size-full" src="https://d2908q01vomqb2.cloudfront.net/b6692ea5df920cad691c20319a6fffd7a4a766b8/2026/08/14/BDB-6143-3.jpeg" alt="Subham Rakshit" width="100" height="100"&gt;&lt;/p&gt; 
  &lt;/div&gt; 
  &lt;h3 class="lb-h4"&gt;Subham Rakshit&lt;/h3&gt; 
  &lt;p&gt;Subham is a Streaming Specialist Solutions Architect for Analytics at AWS based in the UK. He works with customers to design and build search and streaming data platforms that help them achieve their business objective. Outside of work, he enjoys spending time solving jigsaw puzzles with his daughter.&lt;/p&gt; 
 &lt;/div&gt; 
&lt;/footer&gt;</content:encoded>
					
		
		
			</item>
	</channel>
</rss>