<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</title>
    <description>The latest articles on Forem by Yoshiki Fujiwara(藤原 善基)@AWS Community Builder (@yoshikifujiwara).</description>
    <link>https://forem.com/yoshikifujiwara</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1143688%2F2e0886ff-292c-4e8f-a588-bc7629c2321b.jpeg</url>
      <title>Forem: Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</title>
      <link>https://forem.com/yoshikifujiwara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/yoshikifujiwara"/>
    <language>en</language>
    <item>
      <title>Why Delta, Iceberg, and Hudi Can't Write to FSx S3 Access Points — And What Works Instead</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Tue, 26 May 2026 17:00:49 +0000</pubDate>
      <link>https://forem.com/aws-builders/why-delta-iceberg-and-hudi-cant-write-to-fsx-s3-access-points-and-what-works-instead-5be3</link>
      <guid>https://forem.com/aws-builders/why-delta-iceberg-and-hudi-cant-write-to-fsx-s3-access-points-and-what-works-instead-5be3</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In Parts &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;1&lt;/a&gt;–&lt;a href="//./part6-redshift-lake-formation.md"&gt;6&lt;/a&gt; of this series, we validated read paths across six engines. This Part 7 answers the write question: &lt;strong&gt;Can you use Delta Lake, Apache Iceberg, or Apache Hudi on FSx for ONTAP S3 Access Points?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No.&lt;/strong&gt; All three transactional table formats fail to write on FSx S3 AP due to fundamental S3 API limitations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Failure Point&lt;/th&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delta Lake&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commit (write)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;501 Not Implemented&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No conditional writes (If-None-Match)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apache Hudi&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Timeline commit&lt;/td&gt;
&lt;td&gt;Not Supported (by design)&lt;/td&gt;
&lt;td&gt;No atomic rename (.inflight → .commit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apache Iceberg&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metadata write&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NullPointerException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;S3FileIO cannot handle AP alias for metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What DOES work for write on FSx S3 AP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Flat Parquet append (PutObject)&lt;/li&gt;
&lt;li&gt;✅ Athena CTAS (write-back)&lt;/li&gt;
&lt;li&gt;✅ DuckDB COPY TO (write-back)&lt;/li&gt;
&lt;li&gt;✅ EMR Spark &lt;code&gt;df.write.parquet()&lt;/code&gt; (flat Parquet)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick Decision Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Need transactional table format&lt;/strong&gt; → Write to native S3, read FSxN data via S3 AP separately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need write-back to FSxN&lt;/strong&gt; → Use flat Parquet (append-only, no transactions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need ACID on NAS data&lt;/strong&gt; → Use NFS/SMB protocol directly (not S3 AP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Read This Article
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This article is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A consolidated "Not Supported" evidence report for transactional writes&lt;/li&gt;
&lt;li&gt;Root cause analysis for each table format&lt;/li&gt;
&lt;li&gt;Architecture guidance for working alternatives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read by role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data engineer&lt;/strong&gt;: Failure Evidence → What Works Instead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architect&lt;/strong&gt;: Root Cause Analysis → Architecture Patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner / SA&lt;/strong&gt;: Partner Decision Card → Discovery Questions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage engineer&lt;/strong&gt;: S3 API Compatibility → Why This Is Fundamental&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisite Concepts
&lt;/h2&gt;

&lt;p&gt;Before reading this article, it helps to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delta Lake&lt;/strong&gt; — Databricks' open table format using &lt;code&gt;_delta_log/&lt;/code&gt; JSON commits with conditional writes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache Iceberg&lt;/strong&gt; — Netflix's table format using metadata files with atomic commit protocol&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apache Hudi&lt;/strong&gt; — Uber's table format using &lt;code&gt;.hoodie/&lt;/code&gt; timeline with atomic rename&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic rename&lt;/strong&gt; — renaming a file in one atomic operation (S3 does not support this)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional writes&lt;/strong&gt; — writing only if a condition is met (e.g., &lt;code&gt;If-None-Match&lt;/code&gt;); FSx S3 AP returns 501&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PutObject&lt;/strong&gt; — S3's basic write operation (supported by FSx S3 AP for files ≤ 5 GB)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Transactional Table Formats Need Special S3 Operations
&lt;/h2&gt;

&lt;p&gt;All three formats solve the same problem: &lt;strong&gt;concurrent write safety on object storage&lt;/strong&gt;. They each use a commit protocol that requires operations beyond basic PutObject:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Delta Lake:    PutObject with If-None-Match header (conditional write)
               → Prevents two writers from creating the same commit file

Apache Hudi:   Rename .inflight → .commit (atomic rename)
               → Marks a commit as complete atomically

Apache Iceberg: PutObject + HeadObject/GetObject for metadata verification
                → Verifies metadata was written correctly before commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;FSx for ONTAP S3 AP does NOT support:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Conditional writes (&lt;code&gt;If-None-Match&lt;/code&gt; → returns &lt;code&gt;501 Not Implemented&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Atomic rename (S3 API has no rename operation)&lt;/li&gt;
&lt;li&gt;Reliable metadata verification on AP alias (NullPointerException in S3FileIO)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not a configuration issue — it's a fundamental API limitation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture: What's Supported vs What's Not
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    FSx for ONTAP S3 Access Point
                              │
              ┌───────────────┼───────────────┐
              │               │               │
         ✅ READ          ✅ WRITE         ❌ WRITE
         (all engines)    (flat Parquet)   (transactional)
              │               │               │
    ┌─────────┤         ┌─────┤         ┌─────┤
    │         │         │     │         │     │
  Athena   Redshift   EMR  DuckDB   Delta  Iceberg  Hudi
  Snowflake Spectrum  Spark Lambda   Lake
  DuckDB
  EMR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Failure Evidence: Delta Lake
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt;: delta-rs (Rust) write to FSx S3 AP&lt;br&gt;
&lt;strong&gt;Date&lt;/strong&gt;: 2026-05-23&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: &lt;code&gt;501 Not Implemented&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Generic S3 error: Error performing put request to
s3://verification-tes-...-ext-s3alias/_delta_log/00000000000000000000.json:
response error "501 Not Implemented"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause&lt;/strong&gt;: Delta Lake's commit protocol uses &lt;code&gt;If-None-Match&lt;/code&gt; header on PutObject to ensure only one writer creates each commit file. FSx for ONTAP S3 AP does not implement conditional writes and returns 501.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i0dcv1b2osuofigpu6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i0dcv1b2osuofigpu6k.png" alt="Delta Lake 501 Not Implemented error on CloudShell" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CloudShell reproduction: delta-rs write to FSx S3 AP returns "501 Not Implemented" — conditional writes (If-None-Match) are not supported.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spark Delta fallback&lt;/strong&gt;: Spark's Delta writer uses CopyObject + DeleteObject as a rename fallback, but this is not atomic — two concurrent writers can corrupt the log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;: Delta Lake write is &lt;strong&gt;Not Supported&lt;/strong&gt; on FSx S3 AP. This is a fundamental limitation, not a configuration issue.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure Evidence: Apache Hudi
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt;: Logical deduction from Delta Lake verification + Hudi architecture analysis&lt;br&gt;
&lt;strong&gt;Date&lt;/strong&gt;: 2026-05-24&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: Not Supported (by design)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause&lt;/strong&gt;: Apache Hudi's commit protocol requires atomic rename for its timeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.hoodie/[instant].inflight → .hoodie/[instant].commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rename must be atomic to prevent partial commits from being visible. S3 has no rename operation — the only way to "rename" is CopyObject + DeleteObject, which is not atomic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempted verification&lt;/strong&gt;: EMR Serverless with Hudi write — Hudi catalog plugin not available in EMR 7.1.0 default configuration. However, the fundamental constraint (no atomic rename) makes the outcome deterministic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;: Apache Hudi write is &lt;strong&gt;Not Supported&lt;/strong&gt; on FSx S3 AP. Same root cause as Delta Lake.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure Evidence: Apache Iceberg
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt;: EMR Serverless (emr-7.1.0) with Iceberg S3FileIO + Glue Catalog&lt;br&gt;
&lt;strong&gt;Date&lt;/strong&gt;: 2026-05-24&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: &lt;code&gt;NullPointerException&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java.lang.NullPointerException: Cannot invoke
"org.apache.iceberg.TableMetadata.metadataFileLocation()"
because "metadata" is null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause&lt;/strong&gt;: Iceberg's S3FileIO attempts to write metadata files to the warehouse path on FSx S3 AP. The NullPointerException occurs during the commit phase when Iceberg tries to verify the metadata file was written successfully. Possible causes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;S3FileIO may not correctly handle S3 AP alias as bucket name&lt;/li&gt;
&lt;li&gt;The metadata write (PutObject) may succeed but subsequent HeadObject/GetObject to verify fails due to AP alias resolution&lt;/li&gt;
&lt;li&gt;Iceberg's commit protocol may require operations not fully supported by FSx S3 AP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Iceberg READ of pre-existing tables (metadata in Glue, data files on S3 AP) may still work — this was not tested in this run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;: Apache Iceberg write is &lt;strong&gt;Not Supported&lt;/strong&gt; on FSx S3 AP. The failure is in metadata write/verify, not data file write.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary: Why All Three Fail
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Delta Lake&lt;/th&gt;
&lt;th&gt;Apache Hudi&lt;/th&gt;
&lt;th&gt;Apache Iceberg&lt;/th&gt;
&lt;th&gt;FSx S3 AP Support&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic PutObject&lt;/td&gt;
&lt;td&gt;✅ Uses&lt;/td&gt;
&lt;td&gt;✅ Uses&lt;/td&gt;
&lt;td&gt;✅ Uses&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional write (If-None-Match)&lt;/td&gt;
&lt;td&gt;✅ Required&lt;/td&gt;
&lt;td&gt;❌ Not used&lt;/td&gt;
&lt;td&gt;❌ Not used&lt;/td&gt;
&lt;td&gt;❌ 501 Not Implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atomic rename&lt;/td&gt;
&lt;td&gt;❌ Not used&lt;/td&gt;
&lt;td&gt;✅ Required&lt;/td&gt;
&lt;td&gt;❌ Not used&lt;/td&gt;
&lt;td&gt;❌ No S3 rename API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata write + verify on AP alias&lt;/td&gt;
&lt;td&gt;❌ Not needed&lt;/td&gt;
&lt;td&gt;❌ Not needed&lt;/td&gt;
&lt;td&gt;✅ Required&lt;/td&gt;
&lt;td&gt;❌ NullPointerException&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write result&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;❌ Failed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;❌ Not Supported&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;❌ Failed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Common thread&lt;/strong&gt;: Each format requires at least one operation beyond basic PutObject that FSx S3 AP does not support. This is not a bug — it's a design boundary of the S3 AP interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Works Instead
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Flat Parquet Append (PutObject)
&lt;/h3&gt;

&lt;p&gt;All engines can write flat Parquet files to FSx S3 AP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# EMR Spark
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;append&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;AP&amp;gt;/gold/output/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# DuckDB
&lt;/span&gt;&lt;span class="nc"&gt;COPY &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;TO&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;AP&amp;gt;/gold/output.parquet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Limitations&lt;/strong&gt;: No ACID transactions, no schema evolution, no time travel. Append-only pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Athena CTAS (Write-back)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;fsxn_gold&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aggregated_sensors&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;external_location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'s3://&amp;lt;AP&amp;gt;/gold/athena-output/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PARQUET'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;fsxn_athena_verification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sensor_readings&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ DuckDB COPY TO
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    COPY (SELECT * FROM read_parquet(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;AP&amp;gt;/sensor-data/*.parquet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
          WHERE temperature &amp;gt; 30)
    TO &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;AP&amp;gt;/gold/hot_sensors.parquet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; (FORMAT PARQUET)
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ EMR Spark Write (Flat Parquet)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agg_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overwrite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;AP&amp;gt;/gold/emr_output/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For regulated workloads (Takizawa-san lens)&lt;/strong&gt;: Flat Parquet on FSx for ONTAP S3 AP does NOT provide ACID guarantees, schema evolution, or time travel. If your compliance framework requires transactional consistency (e.g., SOX audit trail, HIPAA data integrity), flat Parquet is insufficient. Use &lt;strong&gt;DataSync → S3 → Delta/Iceberg with Lake Formation governance&lt;/strong&gt; (Part 6) for regulated workloads that need both FSx for ONTAP as source and ACID guarantees on the analytics layer.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Architecture Patterns for Transactional Workloads
&lt;/h2&gt;

&lt;p&gt;If you need transactional table formats AND FSx for ONTAP data:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sync mechanism note (verified May 2026)&lt;/strong&gt;: SnapMirror S3 (ONTAP S3 bucket → AWS S3 replication) is &lt;strong&gt;not available on FSx for ONTAP&lt;/strong&gt; — the &lt;code&gt;snapmirror object-store&lt;/code&gt; commands are disabled as a managed service restriction. &lt;strong&gt;AWS DataSync (NFS → S3)&lt;/strong&gt; is the only validated sync mechanism for moving FSx for ONTAP data to standard S3 buckets where Delta/Iceberg/Hudi can write safely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Pattern 1: Read from FSx for ONTAP, Write to Native S3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSx for ONTAP (source) ──S3 AP──▶ EMR Spark (read + transform)
                                        │
                                        ▼
                              Native S3 (Delta/Iceberg table)
                                        │
                                        ▼
                              Athena / Databricks / Redshift (query)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use when&lt;/strong&gt;: You need Delta/Iceberg for downstream analytics but source data lives on FSxN.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 2: Write via NFS, Read via S3 AP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application ──NFS/SMB──▶ FSx for ONTAP Volume (write files)
                                        │
                              S3 Access Point (read-only)
                                        │
                                        ▼
                              Athena / Redshift / DuckDB (query)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use when&lt;/strong&gt;: Applications write via NFS/SMB and analytics engines read via S3 AP. No transactional format needed because NFS provides POSIX semantics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 3: Hybrid (FSxN for raw, S3 for curated)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSx for ONTAP (raw/bronze) ──S3 AP──▶ EMR Spark ──▶ S3 (silver/gold, Iceberg)
         │                                                    │
         └── NFS/SMB access for apps                          └── Athena + Lake Formation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use when&lt;/strong&gt;: Raw data stays on FSxN (multi-protocol access), curated data goes to S3 with full lakehouse capabilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison with Other Engines in This Series
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Read from FSxN S3 AP&lt;/th&gt;
&lt;th&gt;Write flat Parquet&lt;/th&gt;
&lt;th&gt;Write Delta/Iceberg/Hudi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Athena (Part 1)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ CTAS&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks (Part 2)&lt;/td&gt;
&lt;td&gt;⚠️ Partial&lt;/td&gt;
&lt;td&gt;❌ (UC blocks)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake (Part 3)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ TBD&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB Lambda (Part 4)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ COPY TO&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMR Spark (Part 5)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ df.write&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redshift Spectrum (Part 6)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ (read-only)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: ALL engines can read from FSx S3 AP. MOST can write flat Parquet. NONE can write transactional table formats. This is a property of the S3 AP interface, not the engines.&lt;/p&gt;




&lt;h2&gt;
  
  
  Partner Decision Card
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Customer requirement&lt;/th&gt;
&lt;th&gt;FSx S3 AP path&lt;/th&gt;
&lt;th&gt;Recommended alternative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read NAS data from analytics engines&lt;/td&gt;
&lt;td&gt;✅ Works (all engines)&lt;/td&gt;
&lt;td&gt;Use any engine from Parts 1-6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write flat Parquet back to NAS&lt;/td&gt;
&lt;td&gt;✅ Works (EMR, DuckDB, Athena CTAS)&lt;/td&gt;
&lt;td&gt;Use EMR Spark or DuckDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta Lake on NAS data&lt;/td&gt;
&lt;td&gt;❌ Not Supported&lt;/td&gt;
&lt;td&gt;Write Delta to native S3; read FSxN separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg on NAS data&lt;/td&gt;
&lt;td&gt;❌ Not Supported&lt;/td&gt;
&lt;td&gt;Write Iceberg to native S3; read FSxN separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hudi on NAS data&lt;/td&gt;
&lt;td&gt;❌ Not Supported&lt;/td&gt;
&lt;td&gt;Write Hudi to native S3; read FSxN separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACID transactions on NAS&lt;/td&gt;
&lt;td&gt;❌ Not via S3 AP&lt;/td&gt;
&lt;td&gt;Use NFS/SMB protocol directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema evolution on NAS data&lt;/td&gt;
&lt;td&gt;❌ Not via S3 AP&lt;/td&gt;
&lt;td&gt;Use Glue Catalog for schema management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Discovery Questions for Partners
&lt;/h2&gt;

&lt;p&gt;When a customer asks about transactional table formats on FSx for ONTAP S3 AP:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the requirement for transactional WRITE or just READ? (Read of pre-existing tables may work for Iceberg)&lt;/li&gt;
&lt;li&gt;Can the transactional table live on native S3 while source data stays on FSxN? (Pattern 1)&lt;/li&gt;
&lt;li&gt;Is the write pattern append-only or does it require updates/deletes? (Append-only works with flat Parquet)&lt;/li&gt;
&lt;li&gt;Does the application already write via NFS/SMB? (Pattern 2 — no S3 AP write needed)&lt;/li&gt;
&lt;li&gt;Is schema evolution required? (Use Glue Catalog for schema management without table format)&lt;/li&gt;
&lt;li&gt;What is the concurrency requirement? (Single-writer flat Parquet is safe; multi-writer needs transactions)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Governance Impact
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Write pattern&lt;/th&gt;
&lt;th&gt;Governance model&lt;/th&gt;
&lt;th&gt;Concurrency safety&lt;/th&gt;
&lt;th&gt;Production suitability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Flat Parquet (single writer)&lt;/td&gt;
&lt;td&gt;IAM + S3 AP + Glue Catalog&lt;/td&gt;
&lt;td&gt;✅ Safe (single writer)&lt;/td&gt;
&lt;td&gt;Production-ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flat Parquet (multi-writer)&lt;/td&gt;
&lt;td&gt;IAM + S3 AP + Glue Catalog&lt;/td&gt;
&lt;td&gt;⚠️ Risk (no conflict detection)&lt;/td&gt;
&lt;td&gt;Use with caution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta/Iceberg/Hudi&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;❌ Not Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS/SMB write + S3 AP read&lt;/td&gt;
&lt;td&gt;POSIX + IAM + S3 AP&lt;/td&gt;
&lt;td&gt;✅ Safe (POSIX locking)&lt;/td&gt;
&lt;td&gt;Production-ready&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;For multi-writer scenarios&lt;/strong&gt;: If multiple processes need to write to the same prefix on FSxN via S3 AP, use a coordination mechanism (e.g., Step Functions, SQS queue) to serialize writes. Without transactional table formats, there is no built-in conflict detection.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Readiness Score
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;AI Capability&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Operational Simplicity&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flat Parquet + Glue Catalog&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NFS write + S3 AP read&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hybrid (FSxN raw + S3 Iceberg)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scoring methodology&lt;/strong&gt;: Flat Parquet + Glue Catalog scores highest on Cost and Simplicity (no additional infrastructure). Hybrid pattern scores highest on Governance and Performance (full lakehouse on S3) but lower on Simplicity (two storage tiers to manage).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cost Analysis
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Additional cost beyond FSxN&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Flat Parquet (append-only)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;Just PutObject to existing FSxN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid (FSxN + S3 Iceberg)&lt;/td&gt;
&lt;td&gt;S3 storage + Glue Catalog&lt;/td&gt;
&lt;td&gt;Duplicate storage for curated layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS write + S3 AP read&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;Same FSxN volume, two access paths&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: The cheapest write pattern is flat Parquet directly to FSxN via S3 AP. If you need transactional capabilities, the cost is maintaining a separate S3 tier for the curated layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Known Failure Signatures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Resolution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;501 Not Implemented&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delta Lake&lt;/td&gt;
&lt;td&gt;Conditional write (If-None-Match) not supported&lt;/td&gt;
&lt;td&gt;Use flat Parquet instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;NullPointerException&lt;/code&gt; on metadata&lt;/td&gt;
&lt;td&gt;Iceberg&lt;/td&gt;
&lt;td&gt;S3FileIO cannot handle AP alias&lt;/td&gt;
&lt;td&gt;Write Iceberg to native S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rename fails / timeline corrupt&lt;/td&gt;
&lt;td&gt;Hudi&lt;/td&gt;
&lt;td&gt;No atomic rename in S3 API&lt;/td&gt;
&lt;td&gt;Write Hudi to native S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;CopyObject&lt;/code&gt; + &lt;code&gt;DeleteObject&lt;/code&gt; (non-atomic)&lt;/td&gt;
&lt;td&gt;Delta (Spark fallback)&lt;/td&gt;
&lt;td&gt;Spark uses copy+delete as rename&lt;/td&gt;
&lt;td&gt;Not safe for concurrent writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write succeeds but table is corrupt&lt;/td&gt;
&lt;td&gt;Any format (if forced)&lt;/td&gt;
&lt;td&gt;Missing concurrency control&lt;/td&gt;
&lt;td&gt;Do not force transactional writes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;This article concludes the core engine validation series (Parts 1-7). The series has validated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ 6 read engines (Athena, Databricks, Snowflake, DuckDB, EMR, Redshift)&lt;/li&gt;
&lt;li&gt;✅ 3 write patterns (flat Parquet via EMR, DuckDB, Athena CTAS)&lt;/li&gt;
&lt;li&gt;❌ 3 table formats that don't work (Delta, Iceberg, Hudi)&lt;/li&gt;
&lt;li&gt;✅ Enterprise governance (Lake Formation fine-grained: column, row, tag)&lt;/li&gt;
&lt;li&gt;✅ AI/ML integration (Snowflake Cortex: 8/10 functions, Bedrock KB: zero-copy RAG)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Start Here: 3 Steps to Validate in Your Environment
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose your engine&lt;/strong&gt; using the comparison tables in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cheapest: DuckDB Lambda (Part 4) — $0.00001/query&lt;/li&gt;
&lt;li&gt;Most governed: Redshift Spectrum + Lake Formation (Part 6)&lt;/li&gt;
&lt;li&gt;Best AI: Snowflake External Table + Cortex (Part 3)&lt;/li&gt;
&lt;li&gt;Best ETL: EMR Serverless (Part 5)&lt;/li&gt;
&lt;li&gt;Best for Databricks customers: DataSync → S3 → UC (Part 2)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy the verification template&lt;/strong&gt; from &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — each engine has a CloudFormation template and setup guide&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Record evidence&lt;/strong&gt; using the &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack" rel="noopener noreferrer"&gt;verification-pack&lt;/a&gt; templates — consistent, reviewable results across environments&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  PoC Cost Summary (1-day validation)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;PoC Cost (1 day)&lt;/th&gt;
&lt;th&gt;What you validate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB Lambda&lt;/td&gt;
&lt;td&gt;~$0.01&lt;/td&gt;
&lt;td&gt;Read + write Parquet, sub-second queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena&lt;/td&gt;
&lt;td&gt;~$0.05&lt;/td&gt;
&lt;td&gt;Serverless SQL, Glue catalog integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMR Serverless&lt;/td&gt;
&lt;td&gt;~$0.50&lt;/td&gt;
&lt;td&gt;Spark ETL, write-back, distributed processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redshift Serverless&lt;/td&gt;
&lt;td&gt;~$1.50&lt;/td&gt;
&lt;td&gt;DWH JOINs, Lake Formation governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake&lt;/td&gt;
&lt;td&gt;~$5 (1 credit)&lt;/td&gt;
&lt;td&gt;External Table, Cortex AI, governance tags&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Previously in this series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Athena — Query NAS Data In Place&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Databricks — A Layer-by-Layer Validation of Observed Boundaries&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: &lt;a href="https://dev.to/yoshiki0705/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-4f5g"&gt;Snowflake — From 'Access Denied' to Working External Tables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: &lt;a href="//./part4-duckdb-lambda.md"&gt;DuckDB Lambda — Serverless Analytics for $0.00001/Query&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: &lt;a href="//./part5-emr-spark.md"&gt;EMR Spark — Read-Write ETL on NAS Data&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6&lt;/strong&gt;: &lt;a href="//./part6-redshift-lake-formation.md"&gt;Redshift Spectrum + Lake Formation — Enterprise Governance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/delta-io/delta/blob/master/PROTOCOL.md" rel="noopener noreferrer"&gt;Delta Lake protocol specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://iceberg.apache.org/docs/latest/aws/" rel="noopener noreferrer"&gt;Apache Iceberg spec — S3 compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hudi.apache.org/docs/timeline" rel="noopener noreferrer"&gt;Apache Hudi timeline architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/access-points-for-fsxn-object-api-support.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 AP — Supported API operations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/08/amazon-s3-conditional-writes/" rel="noopener noreferrer"&gt;S3 conditional writes announcement (2024)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Key achievement&lt;/strong&gt;: This validation conclusively established the write boundary for FSx for ONTAP S3 Access Points — transactional table formats (Delta, Iceberg, Hudi) are not supported due to fundamental S3 API limitations (no conditional writes, no atomic rename). The working alternative is flat Parquet append via PutObject, which is supported by EMR Spark, DuckDB, and Athena CTAS. For teams that need transactional capabilities, the recommended pattern is hybrid: raw data on FSxN (multi-protocol access) with curated Iceberg/Delta tables on native S3.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Evidence from verification-pack: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack/delta-lake" rel="noopener noreferrer"&gt;delta-lake/&lt;/a&gt;, &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack/iceberg" rel="noopener noreferrer"&gt;iceberg/&lt;/a&gt;, &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack/hudi" rel="noopener noreferrer"&gt;hudi/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article is an independent validation report and does not represent AWS, NetApp, Databricks, or Apache Software Foundation official guidance. Product behavior and platform capabilities may change. Always validate in your own environment.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>deltalake</category>
      <category>iceberg</category>
      <category>amazonfsxfornetappontap</category>
    </item>
    <item>
      <title>Redshift Spectrum + Lake Formation — Enterprise Governance on NAS Data</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Tue, 26 May 2026 16:58:02 +0000</pubDate>
      <link>https://forem.com/aws-builders/redshift-spectrum-lake-formation-enterprise-governance-on-nas-data-2pik</link>
      <guid>https://forem.com/aws-builders/redshift-spectrum-lake-formation-enterprise-governance-on-nas-data-2pik</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1&lt;/a&gt;, Athena provided serverless SQL. In &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Part 2&lt;/a&gt;, Databricks hit boundaries. In &lt;a href="https://dev.to/yoshiki0705/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-4f5g"&gt;Part 3&lt;/a&gt;, Snowflake works with config. In &lt;a href="//./part4-duckdb-lambda.md"&gt;Part 4&lt;/a&gt;, DuckDB Lambda was cheapest. In &lt;a href="//./part5-emr-spark.md"&gt;Part 5&lt;/a&gt;, EMR Spark delivered full ETL. This Part 6 adds &lt;strong&gt;enterprise governance&lt;/strong&gt;: Redshift Spectrum + Lake Formation provides 4-layer authorization on NAS data.&lt;/p&gt;

&lt;p&gt;Redshift Serverless (8 RPU) successfully queries FSx for ONTAP data via S3 Access Points using the same Glue Catalog tables as Athena — no additional data registration needed. Add Lake Formation on top for table-level, column-level, and tag-based access control.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Comparison with Athena&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;COUNT(*) 10K rows&lt;/td&gt;
&lt;td&gt;3,231 ms&lt;/td&gt;
&lt;td&gt;Athena: ~1,500 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GROUP BY aggregation&lt;/td&gt;
&lt;td&gt;2,580 ms&lt;/td&gt;
&lt;td&gt;Athena: ~1,800 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COUNT(*) 5M rows&lt;/td&gt;
&lt;td&gt;4,277 ms&lt;/td&gt;
&lt;td&gt;Athena: 2,196 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;~2x slower than Athena for simple scans (Redshift Serverless cold start overhead), but Redshift adds DWH capabilities: federated JOINs with local tables, materialized views, and stored procedures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick Decision Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Need DWH JOINs with NAS data&lt;/strong&gt; → Redshift Spectrum (this article)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need enterprise governance (table/column/tag)&lt;/strong&gt; → Add Lake Formation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need serverless SQL only (no DWH)&lt;/strong&gt; → Use Athena (Part 1) — faster and cheaper&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Read This Article
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This article is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reproduction-focused validation report&lt;/li&gt;
&lt;li&gt;Evidence from one environment (Redshift Serverless 8 RPU, ap-northeast-1)&lt;/li&gt;
&lt;li&gt;A governance architecture guide for Lake Formation + FSx S3 AP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read by role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DWH engineer&lt;/strong&gt;: Architecture → Setup → Benchmark Results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security / governance reviewer&lt;/strong&gt;: 4-Layer Authorization → Governance Impact&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data engineer&lt;/strong&gt;: When to Use → Comparison with Athena&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner / SA&lt;/strong&gt;: Partner Decision Card → Discovery Questions&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisite Concepts
&lt;/h2&gt;

&lt;p&gt;Before reading this article, it helps to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redshift Spectrum&lt;/strong&gt; — Redshift's ability to query data in S3 via external schemas (Glue Catalog)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redshift Serverless&lt;/strong&gt; — pay-per-query Redshift without cluster management (measured in RPU)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lake Formation&lt;/strong&gt; — AWS's centralized governance layer for data lakes (table/column/tag permissions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue Catalog&lt;/strong&gt; — AWS's metadata catalog (shared by Athena, Redshift Spectrum, EMR, Glue)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Schema&lt;/strong&gt; — a Redshift schema that maps to a Glue Catalog database&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│  Redshift Serverless (8 RPU)                                     │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │  SQL Query                                                │   │
│  │  SELECT * FROM fsxn_spectrum.sensor_readings              │   │
│  │         JOIN local_table ON ...                            │   │
│  └──────────────────────────────────────────────────────────┘   │
│                          │                                       │
│              External Schema (Glue Catalog)                       │
└──────────────────────────┼───────────────────────────────────────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
    Lake Formation    IAM Role    S3 Access Point
    (table/column     (API        (resource
     permissions)      access)     policy)
              │            │            │
              └────────────┼────────────┘
                           │
                           ▼
              FSx for ONTAP Volume (Parquet files)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4-Layer Authorization:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lake Formation&lt;/strong&gt; — Who can access which tables/columns (fine-grained)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; — Who can call which AWS APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Access Point Policy&lt;/strong&gt; — Which principals can access this access point&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File System&lt;/strong&gt; — UNIX permissions on the underlying files&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Benchmark Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Duration (ms)&lt;/th&gt;
&lt;th&gt;Rows&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CREATE EXTERNAL SCHEMA&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;One-time setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COUNT(*) 10K rows&lt;/td&gt;
&lt;td&gt;3,231&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;Cold start overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GROUP BY + AVG aggregation&lt;/td&gt;
&lt;td&gt;2,580&lt;/td&gt;
&lt;td&gt;3 groups&lt;/td&gt;
&lt;td&gt;Status grouping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COUNT(*) 5M rows&lt;/td&gt;
&lt;td&gt;4,277&lt;/td&gt;
&lt;td&gt;5,000,000&lt;/td&gt;
&lt;td&gt;Large scan&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Environment: Redshift Serverless 8 RPU, ap-northeast-1. FSx for ONTAP Single-AZ, 128 MB/s.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance note&lt;/strong&gt;: Redshift Serverless has cold start overhead (~2-3s for first query). Warm queries on provisioned Redshift clusters would be faster. For simple scans, Athena is ~2x faster because it has no DWH initialization overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Evidence Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Evidence&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Redshift Serverless&lt;/td&gt;
&lt;td&gt;Workgroup creation (8 RPU)&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Serverless endpoint available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM role&lt;/td&gt;
&lt;td&gt;Spectrum role with S3 AP permissions&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;GetObject + ListBucket on AP ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Schema&lt;/td&gt;
&lt;td&gt;CREATE EXTERNAL SCHEMA from Glue&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Same catalog as Athena&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spectrum read (small)&lt;/td&gt;
&lt;td&gt;COUNT(*) 10K rows&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;3,231ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spectrum read (aggregation)&lt;/td&gt;
&lt;td&gt;GROUP BY + AVG&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;2,580ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spectrum read (large)&lt;/td&gt;
&lt;td&gt;COUNT(*) 5M rows&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;4,277ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lake Formation admin&lt;/td&gt;
&lt;td&gt;put-data-lake-settings&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Admin configured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lake Formation grant&lt;/td&gt;
&lt;td&gt;Table-level SELECT grant&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Fine-grained permission works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LF column-level&lt;/td&gt;
&lt;td&gt;SELECT on 3 permitted columns&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Non-permitted column returns "cannot be resolved"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LF column deny&lt;/td&gt;
&lt;td&gt;SELECT on denied column (humidity)&lt;/td&gt;
&lt;td&gt;✅ Pass (denied)&lt;/td&gt;
&lt;td&gt;"Column cannot be resolved or requester is not authorized"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LF row filter&lt;/td&gt;
&lt;td&gt;Data cells filter creation&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Row filter (status='normal') + column filter combined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LF-Tag creation&lt;/td&gt;
&lt;td&gt;sensitivity tag (public/internal/confidential)&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Tag created and assigned to table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LF-Tag permission&lt;/td&gt;
&lt;td&gt;Tag-based DESCRIBE+ASSOCIATE grant&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Scalable governance via classification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena under LF&lt;/td&gt;
&lt;td&gt;Query with LF permissions active&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Same governance applies to Athena&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create External Schema (reuses Glue Catalog)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;EXTERNAL&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;fsxn_spectrum&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;CATALOG&lt;/span&gt;
&lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="s1"&gt;'fsxn_athena_verification'&lt;/span&gt;
&lt;span class="n"&gt;IAM_ROLE&lt;/span&gt; &lt;span class="s1"&gt;'arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/fsxn-redshift-spectrum-role'&lt;/span&gt;
&lt;span class="n"&gt;REGION&lt;/span&gt; &lt;span class="s1"&gt;'ap-northeast-1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: This uses the same Glue Catalog database that Athena uses. No additional table registration needed — if Athena can query it, Redshift Spectrum can too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Query FSx for ONTAP Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Simple count&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;fsxn_spectrum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sensor_readings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Result: 10000 (3,231ms)&lt;/span&gt;

&lt;span class="c1"&gt;-- Aggregation&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;fsxn_spectrum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sensor_readings&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Result: 3 groups (2,580ms)&lt;/span&gt;

&lt;span class="c1"&gt;-- JOIN with local Redshift table (DWH capability)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;fsxn_spectrum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sensor_readings&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;device_master&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Add Lake Formation Governance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set Lake Formation admin&lt;/span&gt;
aws lakeformation put-data-lake-settings &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-lake-settings&lt;/span&gt; &lt;span class="s1"&gt;'{"DataLakeAdmins": [{"DataLakePrincipalIdentifier": "arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:user/&amp;lt;admin&amp;gt;"}]}'&lt;/span&gt;

&lt;span class="c"&gt;# Grant table-level SELECT to a role&lt;/span&gt;
aws lakeformation grant-permissions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--principal&lt;/span&gt; &lt;span class="s1"&gt;'{"DataLakePrincipalIdentifier": "arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/fsxn-analyst-role"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource&lt;/span&gt; &lt;span class="s1"&gt;'{"Table": {"DatabaseName": "fsxn_athena_verification", "Name": "sensor_readings"}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--permissions&lt;/span&gt; &lt;span class="s1"&gt;'["SELECT", "DESCRIBE"]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhjukw0to8yucnepi07d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhjukw0to8yucnepi07d.png" alt="Lake Formation Data Permissions — table-level SELECT grant" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lake Formation Data Permissions: fine-grained table-level SELECT grant for &lt;code&gt;fsxn-athena-glue-role&lt;/code&gt; on &lt;code&gt;sensor_readings&lt;/code&gt; table.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Lake Formation Governance Value
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Without Lake Formation&lt;/th&gt;
&lt;th&gt;With Lake Formation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Table-level access&lt;/td&gt;
&lt;td&gt;S3 AP policy (all-or-nothing per prefix)&lt;/td&gt;
&lt;td&gt;Per-table SELECT/DESCRIBE grants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column-level security&lt;/td&gt;
&lt;td&gt;❌ Not possible&lt;/td&gt;
&lt;td&gt;✅ Column-level grants + masking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row-level filtering&lt;/td&gt;
&lt;td&gt;❌ Not possible&lt;/td&gt;
&lt;td&gt;✅ Data Cells Filter (row filter expressions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tag-based access control&lt;/td&gt;
&lt;td&gt;❌ Not possible&lt;/td&gt;
&lt;td&gt;✅ Classify data → auto-grant by tag (LF-Tags)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Centralized audit&lt;/td&gt;
&lt;td&gt;CloudTrail (API-level)&lt;/td&gt;
&lt;td&gt;Lake Formation audit (table/column-level)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-account sharing&lt;/td&gt;
&lt;td&gt;Share S3 AP (complex)&lt;/td&gt;
&lt;td&gt;Share tables via Lake Formation (simple)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Fine-Grained Governance — Verified (May 2026)
&lt;/h3&gt;

&lt;p&gt;All three fine-grained Lake Formation capabilities have been validated on FSx for ONTAP S3 AP data:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Column-level permission&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Grant SELECT on 3 of 4 columns; query the denied column&lt;/td&gt;
&lt;td&gt;✅ Permitted columns return data; denied column (&lt;code&gt;humidity&lt;/code&gt;) returns "cannot be resolved"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Row filter (Data Cells Filter)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create filter &lt;code&gt;status = 'normal'&lt;/code&gt;; query returns only matching rows&lt;/td&gt;
&lt;td&gt;✅ Only rows matching the filter expression are returned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LF-Tag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create tag &lt;code&gt;sensitivity: public/internal/confidential&lt;/code&gt;; assign to table&lt;/td&gt;
&lt;td&gt;✅ Tag created, assigned, and queryable via Lake Formation console&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Governance implication for regulated workloads&lt;/strong&gt;: Lake Formation on FSx for ONTAP S3 AP data provides the same fine-grained access control as on native S3 data. Column masking, row filtering, and tag-based classification all work without data movement. This is the strongest AWS-native governance path for FSx for ONTAP data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iceberg + Lake Formation path&lt;/strong&gt;: Glue Data Catalog supports Iceberg table registration natively. For transactional workloads requiring ACID guarantees: sync FSx for ONTAP data to S3 via DataSync → write as Iceberg table (EMR Spark) → register in Glue Catalog → query via Redshift Spectrum with full Lake Formation governance (column/row/tag). This provides the best of both worlds: FSx for ONTAP as source of truth + Iceberg ACID + Lake Formation governance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Enterprise governance use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare&lt;/strong&gt;: Column-level masking of PHI fields (e.g., hide &lt;code&gt;patient_name&lt;/code&gt; from analysts)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finance&lt;/strong&gt;: Row-level filtering by business unit (each team sees only their data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public sector&lt;/strong&gt;: LF-Tag classification enforcement (sensitivity: public/internal/confidential)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparison with Other Engines in This Series
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Redshift Spectrum&lt;/th&gt;
&lt;th&gt;Athena (Part 1)&lt;/th&gt;
&lt;th&gt;DuckDB Lambda (Part 4)&lt;/th&gt;
&lt;th&gt;EMR Spark (Part 5)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query latency (5M rows)&lt;/td&gt;
&lt;td&gt;4,277ms&lt;/td&gt;
&lt;td&gt;2,196ms&lt;/td&gt;
&lt;td&gt;N/A (memory limit)&lt;/td&gt;
&lt;td&gt;6,780ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DWH JOINs with local tables&lt;/td&gt;
&lt;td&gt;✅ Best&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;Lake Formation governance&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️ Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Materialized views&lt;/td&gt;
&lt;td&gt;✅&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;Stored procedures&lt;/td&gt;
&lt;td&gt;✅&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;Zero idle cost&lt;/td&gt;
&lt;td&gt;✅ (Serverless)&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;Write-back to FSxN&lt;/td&gt;
&lt;td&gt;❌ (results stay in Redshift)&lt;/td&gt;
&lt;td&gt;✅ CTAS&lt;/td&gt;
&lt;td&gt;✅ COPY TO&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;~3s (Serverless)&lt;/td&gt;
&lt;td&gt;~2s&lt;/td&gt;
&lt;td&gt;1.9s&lt;/td&gt;
&lt;td&gt;20s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost model&lt;/td&gt;
&lt;td&gt;RPU-seconds&lt;/td&gt;
&lt;td&gt;$/TB scanned&lt;/td&gt;
&lt;td&gt;$/invocation&lt;/td&gt;
&lt;td&gt;$/job&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Partner Decision Card
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Customer requirement&lt;/th&gt;
&lt;th&gt;Redshift Spectrum + LF today&lt;/th&gt;
&lt;th&gt;Recommended path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JOIN NAS data with DWH tables&lt;/td&gt;
&lt;td&gt;✅ Best fit&lt;/td&gt;
&lt;td&gt;Redshift Spectrum external schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise governance (table/column/tag)&lt;/td&gt;
&lt;td&gt;✅ Best fit&lt;/td&gt;
&lt;td&gt;Add Lake Formation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing Redshift investment&lt;/td&gt;
&lt;td&gt;✅ Natural extension&lt;/td&gt;
&lt;td&gt;Add external schema to existing cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless SQL only (no DWH)&lt;/td&gt;
&lt;td&gt;⚠️ Overkill&lt;/td&gt;
&lt;td&gt;Use Athena (faster, cheaper for simple queries)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back to FSxN&lt;/td&gt;
&lt;td&gt;❌ Not supported&lt;/td&gt;
&lt;td&gt;Use EMR Serverless (Part 5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-second latency&lt;/td&gt;
&lt;td&gt;❌ Cold start overhead&lt;/td&gt;
&lt;td&gt;Use DuckDB Lambda (Part 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-account data sharing&lt;/td&gt;
&lt;td&gt;✅ Lake Formation sharing&lt;/td&gt;
&lt;td&gt;Configure LF cross-account grants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column-level masking for compliance&lt;/td&gt;
&lt;td&gt;✅ Lake Formation&lt;/td&gt;
&lt;td&gt;Configure column-level permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Discovery Questions for Partners
&lt;/h2&gt;

&lt;p&gt;When a customer asks about Redshift Spectrum + Lake Formation + FSx for ONTAP S3 AP:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the customer already have a Redshift cluster or Serverless workgroup? (If yes, adding Spectrum is trivial)&lt;/li&gt;
&lt;li&gt;Do they need to JOIN NAS data with existing DWH tables? (This is Redshift Spectrum's unique value)&lt;/li&gt;
&lt;li&gt;Is table/column-level governance required? (Lake Formation adds this layer)&lt;/li&gt;
&lt;li&gt;Is the workload read-only analytics, or does it need write-back? (Spectrum is read-only from external data)&lt;/li&gt;
&lt;li&gt;What is the query frequency? (For &amp;lt; 10 queries/day, Athena is cheaper)&lt;/li&gt;
&lt;li&gt;Is cross-account data sharing needed? (Lake Formation simplifies this)&lt;/li&gt;
&lt;li&gt;Are there compliance requirements for column-level masking? (Lake Formation provides this)&lt;/li&gt;
&lt;li&gt;What is the acceptable query latency? (Redshift Serverless has ~3s cold start)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Governance Impact Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Access path&lt;/th&gt;
&lt;th&gt;Authorization layers&lt;/th&gt;
&lt;th&gt;Auditability&lt;/th&gt;
&lt;th&gt;Production suitability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Redshift Spectrum (no LF)&lt;/td&gt;
&lt;td&gt;IAM + S3 AP + File System (3 layers)&lt;/td&gt;
&lt;td&gt;Medium (CloudTrail)&lt;/td&gt;
&lt;td&gt;Good for non-regulated workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redshift Spectrum + Lake Formation&lt;/td&gt;
&lt;td&gt;LF + IAM + S3 AP + File System (4 layers)&lt;/td&gt;
&lt;td&gt;High (LF audit + CloudTrail)&lt;/td&gt;
&lt;td&gt;Recommended for regulated workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena + Lake Formation&lt;/td&gt;
&lt;td&gt;LF + IAM + S3 AP + File System (4 layers)&lt;/td&gt;
&lt;td&gt;High (LF audit + CloudTrail)&lt;/td&gt;
&lt;td&gt;Recommended for serverless regulated workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: Redshift Spectrum and Athena share the same Glue Catalog and Lake Formation permissions. Governance configured for one automatically applies to the other. This means you can use EMR Spark for write-back, register output in Glue, apply Lake Formation permissions, and query from both Athena and Redshift Spectrum with the same governance.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Readiness Score
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;AI Capability&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Operational Simplicity&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redshift Spectrum + LF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Athena + Lake Formation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Snowflake External Table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DuckDB Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★☆☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★☆☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMR Serverless Spark&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scoring methodology&lt;/strong&gt;: Redshift Spectrum + LF scores highest on Governance (same as Athena + LF) but lower on Cost and Simplicity due to RPU pricing and DWH management overhead. Choose Redshift Spectrum when DWH JOINs are required; choose Athena when serverless SQL is sufficient.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cost Analysis
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Redshift Serverless (8 RPU, per query)&lt;/td&gt;
&lt;td&gt;~$0.36/RPU-hour (billed per second)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redshift Serverless (idle)&lt;/td&gt;
&lt;td&gt;$0 (scales to zero)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lake Formation&lt;/td&gt;
&lt;td&gt;$0 (no additional charge)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue Catalog&lt;/td&gt;
&lt;td&gt;$1/100K objects/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP (existing)&lt;/td&gt;
&lt;td&gt;$0 incremental&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Monthly estimate&lt;/strong&gt; (100 queries/day, avg 5s each):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100 queries × 5s × 8 RPU × $0.36/RPU-hour ÷ 3600 = &lt;strong&gt;~$0.40/day = ~$12/month&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Athena (same queries): ~$5/TB × data scanned&lt;/li&gt;
&lt;li&gt;DuckDB Lambda: ~$1.10/month (but no DWH JOINs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When Redshift Spectrum is cost-justified&lt;/strong&gt;: When you already have Redshift and need to JOIN NAS data with local tables. The marginal cost of adding Spectrum queries is low.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use (and When Not To)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use Redshift Spectrum + Lake Formation when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Customer already has Redshift (adding Spectrum is trivial)&lt;/li&gt;
&lt;li&gt;Need to JOIN NAS data with DWH tables&lt;/li&gt;
&lt;li&gt;Enterprise governance (table/column/tag) is required&lt;/li&gt;
&lt;li&gt;Cross-account data sharing is needed&lt;/li&gt;
&lt;li&gt;Compliance requires column-level masking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Don't use when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Simple serverless SQL is sufficient (use Athena — faster, cheaper)&lt;/li&gt;
&lt;li&gt;Need write-back to FSxN (use EMR Serverless)&lt;/li&gt;
&lt;li&gt;Need sub-second latency (use DuckDB Lambda)&lt;/li&gt;
&lt;li&gt;No existing Redshift investment (Athena is simpler to start)&lt;/li&gt;
&lt;li&gt;Dataset is small and ad-hoc (DuckDB Lambda is cheapest)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Known Failure Signatures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Next step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;permission denied for schema&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IAM role not associated with Redshift&lt;/td&gt;
&lt;td&gt;Associate IAM role with Redshift namespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;S3 access denied&lt;/code&gt; on external table&lt;/td&gt;
&lt;td&gt;IAM role missing S3 AP permissions&lt;/td&gt;
&lt;td&gt;Add S3 AP ARN to role policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External schema creation fails&lt;/td&gt;
&lt;td&gt;Glue database doesn't exist&lt;/td&gt;
&lt;td&gt;Create database in Glue Catalog first (or use Athena)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query returns 0 rows&lt;/td&gt;
&lt;td&gt;Table location doesn't match S3 AP path&lt;/td&gt;
&lt;td&gt;Verify Glue table LOCATION uses AP alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Spectrum is not supported&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Using provisioned cluster without Spectrum&lt;/td&gt;
&lt;td&gt;Enable Spectrum or use Serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lake Formation permission denied&lt;/td&gt;
&lt;td&gt;LF permissions not granted&lt;/td&gt;
&lt;td&gt;Grant SELECT via &lt;code&gt;aws lakeformation grant-permissions&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 7&lt;/strong&gt;: Table Format Boundaries — why Delta, Iceberg, and Hudi can't write to FSx S3 AP, and what flat Parquet patterns work instead (critical knowledge for architecture decisions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Previously in this series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Athena — Query NAS Data In Place&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Databricks — A Layer-by-Layer Validation of Observed Boundaries&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: &lt;a href="https://dev.to/yoshiki0705/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-4f5g"&gt;Snowflake — From 'Access Denied' to Working External Tables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: &lt;a href="//./part4-duckdb-lambda.md"&gt;DuckDB Lambda — Serverless Analytics for $0.00001/Query&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: &lt;a href="//./part5-emr-spark.md"&gt;EMR Spark — Read-Write ETL on NAS Data&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/redshift/latest/dg/c-using-spectrum.html" rel="noopener noreferrer"&gt;Redshift Spectrum documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lake-formation/latest/dg/what-is-lake-formation.html" rel="noopener noreferrer"&gt;AWS Lake Formation documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/accessing-data-via-s3-access-points.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 Access Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-whatis.html" rel="noopener noreferrer"&gt;Redshift Serverless documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Key achievement&lt;/strong&gt;: This validation established that Redshift Spectrum + Lake Formation provides the strongest enterprise governance path for FSx for ONTAP S3 AP data — 4-layer authorization (Lake Formation → IAM → S3 AP → File System), table/column-level access control, and seamless sharing of Glue Catalog with Athena. The same governance configuration applies to both Athena and Redshift Spectrum queries, enabling a unified governance model across query engines.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All benchmarks are from a specific test environment (Redshift Serverless 8 RPU, FSx for ONTAP Single-AZ 128 MB/s, ap-northeast-1). Performance improves with warm queries and provisioned clusters.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article is an independent validation report and does not represent AWS or NetApp official guidance. Product behavior and platform capabilities may change. Always validate in your own environment.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>redshift</category>
      <category>lakeformation</category>
      <category>amazonfsxfornetappontap</category>
    </item>
    <item>
      <title>Read-Write ETL on NAS Data with EMR Serverless Spark — No Cluster, No Copy</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Tue, 26 May 2026 16:57:34 +0000</pubDate>
      <link>https://forem.com/aws-builders/read-write-etl-on-nas-data-with-emr-serverless-spark-no-cluster-no-copy-hgm</link>
      <guid>https://forem.com/aws-builders/read-write-etl-on-nas-data-with-emr-serverless-spark-no-cluster-no-copy-hgm</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1&lt;/a&gt;, Athena provided serverless read-only SQL. In &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Part 2&lt;/a&gt;, Databricks hit session policy boundaries. In &lt;a href="https://dev.to/yoshiki0705/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-4f5g"&gt;Part 3&lt;/a&gt;, Snowflake works with config. In &lt;a href="//./part4-duckdb-lambda.md"&gt;Part 4&lt;/a&gt;, DuckDB Lambda delivered the cheapest path. This Part 5 shows the &lt;strong&gt;full-power Spark ETL path with write-back&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;EMR Serverless Spark can read, transform, and write-back Parquet files on FSx for ONTAP via S3 Access Points. Total Spark execution: &lt;strong&gt;16 seconds&lt;/strong&gt; for a full ETL pipeline (read → aggregate → window → write). Job total including cold start: 37 seconds. Cost: ~$0.05 per job.&lt;/p&gt;

&lt;p&gt;No cluster to manage. No data to copy. No idle cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick Decision Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Need Spark's full power (UDFs, ML, window functions) + write-back&lt;/strong&gt; → EMR Serverless&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only SQL, no Spark needed&lt;/strong&gt; → Use Athena (Part 1) or DuckDB Lambda (Part 4)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need enterprise governance on results&lt;/strong&gt; → Combine EMR write-back + Athena/Lake Formation for reads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/integrations/emr-spark" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations/integrations/emr-spark/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Read This Article
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This article is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reproduction-focused validation report&lt;/li&gt;
&lt;li&gt;Evidence from one environment (EMR Serverless emr-7.1.0, ap-northeast-1)&lt;/li&gt;
&lt;li&gt;A deployment guide for EMR Serverless + FSx for ONTAP S3 AP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read by role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data engineer&lt;/strong&gt;: Architecture → Critical Findings → PySpark Job&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform engineer&lt;/strong&gt;: Deploy and Run → Gotchas → Cost Analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner / SA&lt;/strong&gt;: Partner Decision Card → Discovery Questions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security reviewer&lt;/strong&gt;: Governance Impact → When to Use&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisite Concepts
&lt;/h2&gt;

&lt;p&gt;Before reading this article, it helps to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EMR Serverless&lt;/strong&gt; — a deployment option for EMR that runs Spark/Hive jobs without managing clusters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EMRFS&lt;/strong&gt; — EMR's S3 filesystem implementation (&lt;code&gt;s3://&lt;/code&gt; prefix) that natively supports S3 AP aliases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3A vs EMRFS&lt;/strong&gt; — &lt;code&gt;s3a://&lt;/code&gt; (Hadoop's S3AFileSystem) does NOT support S3 AP aliases; always use &lt;code&gt;s3://&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PySpark&lt;/strong&gt; — Python API for Apache Spark&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parquet timestamp resolution&lt;/strong&gt; — Spark requires microsecond timestamps; nanosecond (pandas default) causes errors&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why EMR Serverless + FSx for ONTAP?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional ETL&lt;/th&gt;
&lt;th&gt;This approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Provision EMR cluster (minutes)&lt;/td&gt;
&lt;td&gt;Submit job to EMR Serverless (seconds)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy data from NAS to S3&lt;/td&gt;
&lt;td&gt;Read NAS data in place via S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pay for idle cluster&lt;/td&gt;
&lt;td&gt;Pay only during job execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manage cluster scaling&lt;/td&gt;
&lt;td&gt;Auto-scales per job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write results to separate S3 bucket&lt;/td&gt;
&lt;td&gt;Write results back to FSx for ONTAP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;EMR Serverless eliminates cluster management entirely. Combined with FSx S3 AP, you get a fully serverless ETL pipeline that reads and writes directly to your NAS storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│  EMR Serverless Application (Spark 3.5, emr-7.1.0)              │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │  PySpark Job                                             │   │
│  │  ├── Read: spark.read.parquet("s3://&amp;lt;AP&amp;gt;/sensor-data/")  │   │
│  │  ├── Transform: GROUP BY, Window functions               │   │
│  │  └── Write: df.write.parquet("s3://&amp;lt;AP&amp;gt;/gold/output/")   │   │
│  └──────────────────────────────────────────────────────────┘   │
│                          │                                      │
│                    EMRFS (s3://)                                │
└──────────────────────────┼──────────────────────────────────────┘
                           │
                           ▼
              S3 Access Point (internet-origin)
                           │
                           ▼
              FSx for ONTAP Volume (Parquet files)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key&lt;/strong&gt;: EMR Serverless uses EMRFS (&lt;code&gt;s3://&lt;/code&gt; prefix) which natively supports S3 AP aliases. No special configuration needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read 10K rows&lt;/td&gt;
&lt;td&gt;6.78s&lt;/td&gt;
&lt;td&gt;First read includes Spark initialization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GROUP BY aggregation&lt;/td&gt;
&lt;td&gt;2.52s&lt;/td&gt;
&lt;td&gt;Status + AVG(temperature)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Window function&lt;/td&gt;
&lt;td&gt;1.19s&lt;/td&gt;
&lt;td&gt;Moving average per device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back to FSxN&lt;/td&gt;
&lt;td&gt;3.61s&lt;/td&gt;
&lt;td&gt;Parquet output to S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total Spark execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16.35s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All operations combined&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job total (with cold start)&lt;/td&gt;
&lt;td&gt;37s&lt;/td&gt;
&lt;td&gt;Includes EMR Serverless startup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Environment: EMR Serverless, emr-7.1.0, Spark 3.5, ap-northeast-1. FSx for ONTAP Single-AZ, 128 MB/s.&lt;/p&gt;




&lt;h2&gt;
  
  
  Evidence Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Evidence&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EMR Serverless app&lt;/td&gt;
&lt;td&gt;create-application&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Spark 3.5 app created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM role&lt;/td&gt;
&lt;td&gt;Execution role with S3 AP permissions&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;GetObject + PutObject on AP ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMRFS read&lt;/td&gt;
&lt;td&gt;spark.read.parquet("s3://AP/...")&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;EMRFS natively handles AP alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spark transforms&lt;/td&gt;
&lt;td&gt;GROUP BY, Window, aggregation&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Full Spark SQL works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back&lt;/td&gt;
&lt;td&gt;df.write.parquet("s3://AP/gold/...")&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;PutObject to FSxN via S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3A (negative test)&lt;/td&gt;
&lt;td&gt;spark.read.parquet("s3a://AP/...")&lt;/td&gt;
&lt;td&gt;❌ Expected fail&lt;/td&gt;
&lt;td&gt;S3A cannot parse AP alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job lifecycle&lt;/td&gt;
&lt;td&gt;start → running → success&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;37s total including cold start&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Critical Finding: EMRFS vs S3A
&lt;/h2&gt;

&lt;p&gt;This is the most important thing to know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ WORKS — EMRFS natively supports S3 AP aliases
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://my-ap-alias-ext-s3alias/sensor-data/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ❌ FAILS — S3A cannot parse AP alias URLs
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3a://my-ap-alias-ext-s3alias/sensor-data/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Error: IllegalArgumentException: Invalid S3 URI
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Always use &lt;code&gt;s3://&lt;/code&gt; (EMRFS) with EMR.&lt;/strong&gt; The &lt;code&gt;s3a://&lt;/code&gt; filesystem (Hadoop's S3AFileSystem) does not understand S3 AP alias format.&lt;/p&gt;




&lt;h2&gt;
  
  
  Critical Finding: Parquet Timestamp Compatibility
&lt;/h2&gt;

&lt;p&gt;If you generate Parquet files with pandas or DuckDB, they default to &lt;strong&gt;nanosecond&lt;/strong&gt; timestamps. Spark cannot read these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AnalysisException: Illegal Parquet type: INT64 (TIMESTAMP(NANOS, true))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Generate Parquet with microsecond timestamps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pyarrow&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pyarrow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parquet&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pq&lt;/span&gt;

&lt;span class="c1"&gt;# Convert nanosecond → microsecond before writing
&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pandas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;
&lt;span class="n"&gt;new_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;new_fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;new_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output.parquet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This affects cross-engine compatibility: if you write Parquet with DuckDB or pandas and want to read it with Spark (EMR, Glue, Databricks), always use microsecond resolution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison with Other Engines in This Series
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;EMR Serverless&lt;/th&gt;
&lt;th&gt;Athena (Part 1)&lt;/th&gt;
&lt;th&gt;DuckDB Lambda (Part 4)&lt;/th&gt;
&lt;th&gt;Snowflake (Part 3)&lt;/th&gt;
&lt;th&gt;Databricks (Part 2)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read from FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;✅ With ARN&lt;/td&gt;
&lt;td&gt;⚠️ Partial (explicit path only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back to FSx for ONTAP&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;td&gt;✅ CTAS&lt;/td&gt;
&lt;td&gt;✅ COPY TO&lt;/td&gt;
&lt;td&gt;⚠️ TBD&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex transforms (UDF, ML)&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;td&gt;❌ SQL only&lt;/td&gt;
&lt;td&gt;❌ SQL only&lt;/td&gt;
&lt;td&gt;⚠️ Snowpark&lt;/td&gt;
&lt;td&gt;✅ Best (if data in UC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;20s&lt;/td&gt;
&lt;td&gt;~2s&lt;/td&gt;
&lt;td&gt;1.9s&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A (cluster always on)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per job&lt;/td&gt;
&lt;td&gt;$0.05&lt;/td&gt;
&lt;td&gt;$0.005/TB&lt;/td&gt;
&lt;td&gt;$0.00001&lt;/td&gt;
&lt;td&gt;Credits&lt;/td&gt;
&lt;td&gt;DBU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Governance&lt;/td&gt;
&lt;td&gt;IAM only&lt;/td&gt;
&lt;td&gt;✅ Glue + LF&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Tags + RBAC&lt;/td&gt;
&lt;td&gt;❌ UC blocked on S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed processing&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ Best (if data in UC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session policy issues&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;Resolved with ARN&lt;/td&gt;
&lt;td&gt;❌ Blocks table creation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why EMR Serverless instead of Databricks for FSx for ONTAP S3 AP?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;EMR Serverless uses direct IAM role credentials without intermediary session policies. The S3 AP ARN format works natively — no special configuration needed. In contrast, Databricks UC generates a restrictive session policy that blocks subdirectory listing, table creation, and write operations on FSx for ONTAP S3 AP paths (confirmed by Databricks Support, May 2026).&lt;/p&gt;

&lt;p&gt;For teams that need Spark processing on FSx for ONTAP data today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EMR Serverless&lt;/strong&gt;: Direct read + write-back, no session policy issues, IAM governance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databricks&lt;/strong&gt;: Requires DataSync → S3 → UC (data copy), but provides full UC governance + Mosaic AI&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Partner Decision Card
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Customer requirement&lt;/th&gt;
&lt;th&gt;EMR Serverless today&lt;/th&gt;
&lt;th&gt;Recommended path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full Spark ETL with write-back&lt;/td&gt;
&lt;td&gt;✅ Best fit&lt;/td&gt;
&lt;td&gt;Deploy EMR Serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex transforms (UDFs, ML pipelines)&lt;/td&gt;
&lt;td&gt;✅ Best fit&lt;/td&gt;
&lt;td&gt;Deploy EMR Serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large-scale distributed processing&lt;/td&gt;
&lt;td&gt;✅ Best fit&lt;/td&gt;
&lt;td&gt;Deploy EMR Serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read-only SQL analytics&lt;/td&gt;
&lt;td&gt;⚠️ Overkill&lt;/td&gt;
&lt;td&gt;Use Athena or DuckDB Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-second query latency&lt;/td&gt;
&lt;td&gt;❌ 20s cold start&lt;/td&gt;
&lt;td&gt;Use DuckDB Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise governance on results&lt;/td&gt;
&lt;td&gt;⚠️ IAM only&lt;/td&gt;
&lt;td&gt;Write to FSxN → read via Athena + Lake Formation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta/Iceberg table format&lt;/td&gt;
&lt;td&gt;❌ Write not supported on S3 AP&lt;/td&gt;
&lt;td&gt;Write flat Parquet only. Iceberg &lt;strong&gt;read&lt;/strong&gt; (pre-existing table) is theoretically possible via GetObject but not validated.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduled batch ETL&lt;/td&gt;
&lt;td&gt;✅ Good fit&lt;/td&gt;
&lt;td&gt;EMR Serverless + Step Functions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Discovery Questions for Partners
&lt;/h2&gt;

&lt;p&gt;When a customer asks about EMR Serverless + FSx for ONTAP S3 Access Points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the workload require Spark-specific features (UDFs, ML, window functions, graph)?&lt;/li&gt;
&lt;li&gt;Is write-back to FSx for ONTAP required? (EMR is the best write-back path)&lt;/li&gt;
&lt;li&gt;What is the typical dataset size? (EMR shines at &amp;gt; 1 GB; for &amp;lt; 1 GB, DuckDB Lambda is cheaper)&lt;/li&gt;
&lt;li&gt;Is the workload batch or interactive? (EMR has 20s cold start — not suitable for interactive)&lt;/li&gt;
&lt;li&gt;Does the team have Spark expertise? (If not, Athena SQL may be simpler)&lt;/li&gt;
&lt;li&gt;Is Delta/Iceberg table format required? (Not supported for write on FSx S3 AP)&lt;/li&gt;
&lt;li&gt;What is the job frequency? (10 jobs/day = $15/month; 100 jobs/day = $150/month)&lt;/li&gt;
&lt;li&gt;Is there an existing EMR or Glue investment? (Leverage existing IAM roles and scripts)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Governance Impact
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;EMR Serverless&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;IAM (execution role)&lt;/td&gt;
&lt;td&gt;Standard AWS IAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;S3 AP policy + IAM&lt;/td&gt;
&lt;td&gt;No table/column-level control natively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit trail&lt;/td&gt;
&lt;td&gt;CloudWatch Logs + CloudTrail&lt;/td&gt;
&lt;td&gt;Job logs + S3 API calls logged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data classification&lt;/td&gt;
&lt;td&gt;❌ None built-in&lt;/td&gt;
&lt;td&gt;Can integrate with Lake Formation for reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row/column security&lt;/td&gt;
&lt;td&gt;❌ None built-in&lt;/td&gt;
&lt;td&gt;Apply at read layer (Athena + LF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catalog integration&lt;/td&gt;
&lt;td&gt;⚠️ Optional (Glue Catalog)&lt;/td&gt;
&lt;td&gt;Can register output in Glue for downstream governance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Governance model&lt;/strong&gt;: EMR Serverless uses IAM + S3 AP policy for access control. For enterprise governance, write results back to FSxN and read them via Athena + Lake Formation (Part 6). This gives you Spark's processing power with Lake Formation's governance on the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended pattern for governed ETL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSxN (raw) → EMR Spark (transform) → FSxN (gold) → Athena + Lake Formation (governed read)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  AI Readiness Score
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;AI Capability&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Operational Simplicity&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMR Serverless Spark&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Athena + Lake Formation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DuckDB Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★☆☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★☆☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Snowflake External Table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Governance&lt;/strong&gt;: Access control, audit, classification capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Processing throughput for ETL workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Capability&lt;/strong&gt;: Built-in ML/AI integration (Spark MLlib, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: Total cost for batch ETL workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational Simplicity&lt;/strong&gt;: Setup and maintenance effort&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scoring methodology&lt;/strong&gt;: Each dimension rated by the author based on validated evidence. EMR scores highest on Performance and AI Capability (Spark MLlib, distributed ML) but lower on Governance (IAM-only) and Simplicity (requires Spark expertise).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cost Analysis
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EMR Serverless (37s job)&lt;/td&gt;
&lt;td&gt;~$0.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP (existing)&lt;/td&gt;
&lt;td&gt;$0 incremental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP requests&lt;/td&gt;
&lt;td&gt;$0 (included in FSx)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Script storage (S3)&lt;/td&gt;
&lt;td&gt;&amp;lt; $0.01&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Monthly estimate&lt;/strong&gt; (10 jobs/day):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;300 jobs × $0.05 = &lt;strong&gt;$15/month&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Zero idle cost (application stopped between jobs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EMR on EC2 (m5.xlarge cluster): ~$200/month (always-on)&lt;/li&gt;
&lt;li&gt;Glue ETL (same workload): ~$0.44/job × 300 = $132/month&lt;/li&gt;
&lt;li&gt;DuckDB Lambda: ~$1.10/month (but no distributed processing)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The PySpark Job
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyspark.sql&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SparkSession&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyspark.sql&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;functions&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyspark.sql.window&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Window&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;spark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SparkSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FSxN-S3AP-Verification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getOrCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;S3_AP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;your-ap-alias-ext-s3alias&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# --- Read ---
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;S3_AP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/sensor-data/sensor_data_microsecond.parquet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;row_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Transform: GROUP BY ---
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;agg_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;avg_temp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;humidity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;avg_humidity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;agg_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GROUP BY: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Transform: Window function ---
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;window_spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;partitionBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;rowsBetween&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;window_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;moving_avg_temp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;over&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window_spec&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;window_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;moving_avg_temp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Window: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Write-back ---
&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;agg_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overwrite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;S3_AP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/gold/emr_spark_output/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write-back: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Deploy and Run
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Create EMR Serverless application&lt;/span&gt;
aws emr-serverless create-application &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"fsxn-spark"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--release-label&lt;/span&gt; &lt;span class="s2"&gt;"emr-7.1.0"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; &lt;span class="s2"&gt;"SPARK"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-1

&lt;span class="c"&gt;# 2. Upload script to S3 (regular bucket, not S3 AP)&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;scripts/spark_verification.py &lt;span class="se"&gt;\&lt;/span&gt;
  s3://my-scripts-bucket/emr-scripts/

&lt;span class="c"&gt;# 3. Submit job&lt;/span&gt;
aws emr-serverless start-job-run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--application-id&lt;/span&gt; &amp;lt;app-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--execution-role-arn&lt;/span&gt; arn:aws:iam::&amp;lt;ACCOUNT_ID&amp;gt;:role/emr-serverless-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--job-driver&lt;/span&gt; &lt;span class="s1"&gt;'{
    "sparkSubmit": {
      "entryPoint": "s3://my-scripts-bucket/emr-scripts/spark_verification.py"
    }
  }'&lt;/span&gt;

&lt;span class="c"&gt;# 4. Check status&lt;/span&gt;
aws emr-serverless get-job-run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--application-id&lt;/span&gt; &amp;lt;app-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--job-run-id&lt;/span&gt; &amp;lt;job-run-id&amp;gt;

&lt;span class="c"&gt;# 5. Stop application (zero cost when stopped)&lt;/span&gt;
aws emr-serverless stop-application &lt;span class="nt"&gt;--application-id&lt;/span&gt; &amp;lt;app-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Known Failure Signatures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Next step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IllegalArgumentException: Invalid S3 URI&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Using &lt;code&gt;s3a://&lt;/code&gt; instead of &lt;code&gt;s3://&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Switch to EMRFS (&lt;code&gt;s3://&lt;/code&gt;) prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Illegal Parquet type: INT64 (TIMESTAMP(NANOS))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nanosecond timestamps in Parquet&lt;/td&gt;
&lt;td&gt;Regenerate with microsecond resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job stuck in PENDING &amp;gt; 60s&lt;/td&gt;
&lt;td&gt;EMR Serverless capacity&lt;/td&gt;
&lt;td&gt;Check service quotas; retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;AccessDeniedException&lt;/code&gt; on S3 AP&lt;/td&gt;
&lt;td&gt;IAM role missing AP permissions&lt;/td&gt;
&lt;td&gt;Add S3 AP ARN to execution role policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Script not found&lt;/td&gt;
&lt;td&gt;Script on S3 AP instead of regular S3&lt;/td&gt;
&lt;td&gt;Move script to regular S3 bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write fails with 501&lt;/td&gt;
&lt;td&gt;Attempting Delta/Iceberg write&lt;/td&gt;
&lt;td&gt;Use flat Parquet write only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Gotchas and Lessons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Script must be on regular S3 (not S3 AP)
&lt;/h3&gt;

&lt;p&gt;EMR Serverless loads the PySpark script from S3. The script location must be a regular S3 bucket, not an FSx S3 AP. The script then reads/writes data from/to the S3 AP.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. IAM role needs both S3 bucket and S3 AP permissions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::my-scripts-bucket/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:ap-northeast-1:&amp;lt;ACCOUNT_ID&amp;gt;:accesspoint/&amp;lt;ap-name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:ap-northeast-1:&amp;lt;ACCOUNT_ID&amp;gt;:accesspoint/&amp;lt;ap-name&amp;gt;/object/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Cold start is ~20 seconds
&lt;/h3&gt;

&lt;p&gt;EMR Serverless has a cold start of ~20 seconds before Spark begins executing. For latency-sensitive workloads, keep the application in "started" state (costs ~$0.01/hour for pre-initialized capacity).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. No session policy issues
&lt;/h3&gt;

&lt;p&gt;Unlike Databricks and Snowflake, EMR Serverless uses direct IAM role credentials without intermediary session policies. The S3 AP ARN format works natively.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use EMR Serverless vs Other Engines
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;EMR Serverless&lt;/th&gt;
&lt;th&gt;Athena&lt;/th&gt;
&lt;th&gt;DuckDB Lambda&lt;/th&gt;
&lt;th&gt;Glue ETL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read-only SQL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back to FSxN&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;td&gt;✅ (CTAS)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex Spark transformations&lt;/td&gt;
&lt;td&gt;✅ Best&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;Sub-second latency&lt;/td&gt;
&lt;td&gt;❌ (cold start)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Best&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero idle cost&lt;/td&gt;
&lt;td&gt;✅&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;Large-scale distributed&lt;/td&gt;
&lt;td&gt;✅ Best&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;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 6&lt;/strong&gt;: Redshift Spectrum + Lake Formation — for teams that need DWH-integrated analytics with enterprise governance (4-layer authorization) on NAS data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 7&lt;/strong&gt;: Table Format Boundaries — why Delta, Iceberg, and Hudi can't write to FSx S3 AP, and what flat Parquet patterns work instead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Previously in this series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Athena — Query NAS Data In Place&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Databricks — A Layer-by-Layer Validation of Observed Boundaries&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: &lt;a href="https://dev.to/yoshiki0705/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-4f5g"&gt;Snowflake — From 'Access Denied' to Working External Tables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: &lt;a href="//./part4-duckdb-lambda.md"&gt;DuckDB Lambda — Serverless Analytics for $0.00001/Query&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/accessing-data-via-s3-access-points.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 Access Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-run-spark-with-emr-serverless.html" rel="noopener noreferrer"&gt;AWS Tutorial: Run Spark jobs using Amazon EMR Serverless&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/emr/latest/EMR-Serverless-UserGuide/emr-serverless.html" rel="noopener noreferrer"&gt;EMR Serverless documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Key achievement&lt;/strong&gt;: This validation established that EMR Serverless Spark provides the most capable read-write ETL path for FSx for ONTAP S3 AP data — full Spark SQL, UDFs, window functions, and write-back in 16 seconds of Spark execution at $0.05/job. No cluster management, no data copy, no session policy issues. The trade-off is cold start latency (20s) and lack of built-in governance — pair with Athena + Lake Formation for governed reads on the output.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All benchmarks are from a specific test environment (EMR Serverless emr-7.1.0, FSx for ONTAP Single-AZ 128 MB/s, ap-northeast-1). Scale throughput provisioning for production workloads.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article is an independent validation report and does not represent AWS or NetApp official guidance. Product behavior and platform capabilities may change. Always validate in your own environment.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>spark</category>
      <category>emr</category>
      <category>amazonfsxfornetappontap</category>
    </item>
    <item>
      <title>Serverless Analytics on NAS Data for $0.00001/Query — DuckDB Lambda FSx for ONTAP</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Tue, 26 May 2026 16:55:30 +0000</pubDate>
      <link>https://forem.com/aws-builders/serverless-analytics-on-nas-data-for-000001query-duckdb-lambda-x-fsx-for-ontap-2o5o</link>
      <guid>https://forem.com/aws-builders/serverless-analytics-on-nas-data-for-000001query-duckdb-lambda-x-fsx-for-ontap-2o5o</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1&lt;/a&gt;, Athena worked cleanly. In &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Part 2&lt;/a&gt;, Databricks hit session policy boundaries. In &lt;a href="https://dev.to/yoshiki0705/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-4f5g"&gt;Part 3&lt;/a&gt;, Snowflake works with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; config. This Part 4 shows the cheapest path: &lt;strong&gt;$0.00001/query&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can deploy DuckDB inside a Lambda function (arm64, 1024 MB) and query Parquet files on FSx for ONTAP via S3 Access Points. Warm queries return in &lt;strong&gt;452ms&lt;/strong&gt; for 10,000 rows. Cold start is ~1.9s. Cost per query: approximately &lt;strong&gt;$0.00001&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No database server. No cluster. No idle cost. Just a Lambda function with an 18 MB layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick Decision Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cheapest ad-hoc analytics on NAS data&lt;/strong&gt; → DuckDB Lambda ($1.10/month for 1000 queries/day)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need governance / catalog&lt;/strong&gt; → Use Athena + Lake Formation instead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need distributed processing &amp;gt; 10 GB&lt;/strong&gt; → Use EMR Serverless (Part 5)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/integrations/duckdb" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations/integrations/duckdb/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Read This Article
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This article is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reproduction-focused validation report&lt;/li&gt;
&lt;li&gt;Evidence from one environment (Lambda arm64, 1024 MB, ap-northeast-1)&lt;/li&gt;
&lt;li&gt;A deployment guide for DuckDB + Lambda + FSx for ONTAP S3 AP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read by role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developer / Data engineer&lt;/strong&gt;: Architecture → Deploy in 5 Minutes → Handler config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-conscious team lead&lt;/strong&gt;: Cost Analysis → When to Use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner / SA&lt;/strong&gt;: Partner Decision Card → Discovery Questions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security reviewer&lt;/strong&gt;: Governance Impact → When Not To Use&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisite Concepts
&lt;/h2&gt;

&lt;p&gt;Before reading this article, it helps to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DuckDB&lt;/strong&gt; — an in-process SQL engine (like SQLite for analytics) that runs inside your application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;httpfs extension&lt;/strong&gt; — DuckDB's HTTP/S3 file system extension for reading remote Parquet files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Access Point alias&lt;/strong&gt; — the &lt;code&gt;*-ext-s3alias&lt;/code&gt; hostname that FSx for ONTAP S3 AP exposes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda arm64 (Graviton2)&lt;/strong&gt; — AWS Lambda on ARM architecture, ~20% cheaper than x86&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path-style S3 access&lt;/strong&gt; — required for S3 AP aliases (&lt;code&gt;s3_url_style = 'path'&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why DuckDB + Lambda + FSx for ONTAP?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional approach&lt;/th&gt;
&lt;th&gt;This approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Provision Redshift/EMR cluster&lt;/td&gt;
&lt;td&gt;Deploy Lambda function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pay for idle compute&lt;/td&gt;
&lt;td&gt;Pay only when queried&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy data from NAS to S3&lt;/td&gt;
&lt;td&gt;Query NAS data in place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manage infrastructure&lt;/td&gt;
&lt;td&gt;Zero infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minutes to first query&lt;/td&gt;
&lt;td&gt;Sub-second (warm)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DuckDB is an in-process SQL engine — think "SQLite for analytics." It runs inside your Lambda function, reads Parquet directly from S3 via the &lt;code&gt;httpfs&lt;/code&gt; extension, and returns results. No external database, no connection pooling, no cluster scaling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client (API Gateway / SDK / CLI)
    │
    ▼
Lambda Function (arm64, Python 3.12, 1024 MB)
    │
    ├── DuckDB (in-process, 18 MB layer)
    │   └── httpfs extension (S3 access)
    │
    └── S3 Access Point (internet-origin)
            │
            └── FSx for ONTAP Volume
                ├── sensor_data.parquet (10K rows)
                └── sensor_data_large.parquet (5M rows)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No VPC attachment needed (internet-origin AP). This avoids the ~1-2s ENI cold start penalty.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftixe7otf1g48vgdbspmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftixe7otf1g48vgdbspmk.png" alt="Lambda function overview — arm64, Python 3.12, DuckDB layer" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lambda function: arm64 (Graviton2), Python 3.12, 1024 MB memory, DuckDB httpfs layer attached.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cold start (simple query)&lt;/td&gt;
&lt;td&gt;1,854 ms&lt;/td&gt;
&lt;td&gt;httpfs INSTALL + credential setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warm (simple query)&lt;/td&gt;
&lt;td&gt;0.96 ms&lt;/td&gt;
&lt;td&gt;Connection reused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warm COUNT(*) 10K rows&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;452 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3 AP → Parquet → result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warm GROUP BY 10K rows&lt;/td&gt;
&lt;td&gt;1,411 ms&lt;/td&gt;
&lt;td&gt;Full scan + aggregation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local COUNT(*) 5M rows&lt;/td&gt;
&lt;td&gt;779 ms&lt;/td&gt;
&lt;td&gt;For comparison (over internet)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local COPY TO Parquet&lt;/td&gt;
&lt;td&gt;304 ms&lt;/td&gt;
&lt;td&gt;Write-back to FSxN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Environment: Lambda arm64, 1024 MB, ap-northeast-1. FSx for ONTAP Single-AZ, 128 MB/s.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Storage context (Yakio-san lens)&lt;/strong&gt;: The 10K-row Parquet file is ~250 KB. At 128 MBps provisioned throughput, a single query consumes negligible bandwidth (&amp;lt;0.2% of capacity). NFS/SMB workloads sharing the same file system are not impacted. For concurrent Lambda invocations, throughput becomes a factor at ~500+ simultaneous queries reading large files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: Lambda warm queries (452ms) are faster than local queries (628ms) because Lambda runs in the same region as FSxN — lower network latency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FYoshiki0705%2Ffsxn-lakehouse-integrations%2Fmain%2Fdocs%2Fimages%2Fduckdb-02-lambda-test-success-v2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FYoshiki0705%2Ffsxn-lakehouse-integrations%2Fmain%2Fdocs%2Fimages%2Fduckdb-02-lambda-test-success-v2.png" alt="Lambda test invocation success — JSON result with metrics" width="800" height="1354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lambda test invocation: cold start 2071ms, query returns 3 status groups (normal: 8505, warning: 1221, critical: 274). Memory used: 185 MB.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Evidence Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Evidence&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda deployment&lt;/td&gt;
&lt;td&gt;CloudFormation stack&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Function + Layer deployed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DuckDB initialization&lt;/td&gt;
&lt;td&gt;httpfs INSTALL + LOAD&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Extension loads in ~1.8s cold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP connectivity&lt;/td&gt;
&lt;td&gt;read_parquet() via httpfs&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Path-style + endpoint config works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read (small)&lt;/td&gt;
&lt;td&gt;COUNT(*) 10K rows&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;452ms warm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read (aggregation)&lt;/td&gt;
&lt;td&gt;GROUP BY + AVG&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;1,411ms warm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read (large)&lt;/td&gt;
&lt;td&gt;COUNT(*) 5M rows&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;779ms (local test)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back&lt;/td&gt;
&lt;td&gt;COPY TO Parquet&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;304ms write to S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM authorization&lt;/td&gt;
&lt;td&gt;Lambda execution role&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;s3:GetObject/PutObject on AP ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Deploy in 5 Minutes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Build the Layer (Docker required)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;integrations/duckdb
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/arm64 &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; bash &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/dist:/output"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  public.ecr.aws/lambda/python:3.12-arm64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"dnf install -y zip &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
      pip install duckdb==1.1.3 --target /tmp/python/lib/python3.12/site-packages/ --quiet &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
      cd /tmp &amp;amp;&amp;amp; zip -qr /output/duckdb-layer.zip python/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./deploy.sh &lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This builds the layer, uploads to S3, deploys CloudFormation (Lambda + IAM + Layer), and runs a test invocation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Query
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; fsxn-duckdb-query &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"query": "SELECT status, COUNT(*) FROM read_parquet('&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'s3://{S3_AP}/sensor-data/sensor_data.parquet'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;') GROUP BY status"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cli-binary-format&lt;/span&gt; raw-in-base64-out &lt;span class="se"&gt;\&lt;/span&gt;
  response.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cat &lt;/span&gt;response.json | jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{S3_AP}&lt;/code&gt; placeholder is automatically replaced with your S3 AP alias from the Lambda environment variable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Handler (Key Configuration)
&lt;/h2&gt;

&lt;p&gt;Three settings are critical for DuckDB + S3 AP in Lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Lambda has no home directory
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET home_directory = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/tmp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. S3 AP aliases require path-style access
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET s3_url_style = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Explicit endpoint for AP alias resolution
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET s3_endpoint = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3.ap-northeast-1.amazonaws.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without these, you'll get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Can't find the home directory&lt;/code&gt; (missing #1)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Unknown error for HTTP HEAD&lt;/code&gt; (missing #2 or #3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full handler: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/blob/main/integrations/duckdb/lambda/handler.py" rel="noopener noreferrer"&gt;lambda/handler.py&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison with Other Engines in This Series
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;DuckDB Lambda&lt;/th&gt;
&lt;th&gt;Athena (Part 1)&lt;/th&gt;
&lt;th&gt;Snowflake (Part 3)&lt;/th&gt;
&lt;th&gt;EMR Spark (Part 5)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query latency (10K rows)&lt;/td&gt;
&lt;td&gt;452ms (warm)&lt;/td&gt;
&lt;td&gt;~2s&lt;/td&gt;
&lt;td&gt;~3s&lt;/td&gt;
&lt;td&gt;6.78s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;1.9s&lt;/td&gt;
&lt;td&gt;~2s&lt;/td&gt;
&lt;td&gt;N/A (warehouse)&lt;/td&gt;
&lt;td&gt;20s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per query&lt;/td&gt;
&lt;td&gt;$0.00001&lt;/td&gt;
&lt;td&gt;$0.005/TB&lt;/td&gt;
&lt;td&gt;Credits&lt;/td&gt;
&lt;td&gt;$0.05/job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back to FSxN&lt;/td&gt;
&lt;td&gt;✅ COPY TO&lt;/td&gt;
&lt;td&gt;✅ CTAS&lt;/td&gt;
&lt;td&gt;⚠️ TBD&lt;/td&gt;
&lt;td&gt;✅ Spark write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Governance / Catalog&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Glue + LF&lt;/td&gt;
&lt;td&gt;✅ Tags + RBAC&lt;/td&gt;
&lt;td&gt;⚠️ IAM only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max dataset size&lt;/td&gt;
&lt;td&gt;~1 GB (Lambda limit)&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed processing&lt;/td&gt;
&lt;td&gt;❌ Single process&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;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Partner Decision Card
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Customer requirement&lt;/th&gt;
&lt;th&gt;DuckDB Lambda today&lt;/th&gt;
&lt;th&gt;Recommended path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cheapest ad-hoc analytics&lt;/td&gt;
&lt;td&gt;✅ Best ($1.10/month)&lt;/td&gt;
&lt;td&gt;Deploy DuckDB Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API-driven analytics (behind API GW)&lt;/td&gt;
&lt;td&gt;✅ Best (sub-second warm)&lt;/td&gt;
&lt;td&gt;Deploy with API Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IoT / edge data quick analysis&lt;/td&gt;
&lt;td&gt;✅ Good fit&lt;/td&gt;
&lt;td&gt;Deploy DuckDB Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need governance / audit trail&lt;/td&gt;
&lt;td&gt;❌ No built-in governance&lt;/td&gt;
&lt;td&gt;Use Athena + Lake Formation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dataset &amp;gt; 10 GB&lt;/td&gt;
&lt;td&gt;❌ Lambda memory limit&lt;/td&gt;
&lt;td&gt;Use EMR Serverless or Athena&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need JOINs with DWH tables&lt;/td&gt;
&lt;td&gt;❌ Isolated engine&lt;/td&gt;
&lt;td&gt;Use Redshift Spectrum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need catalog integration&lt;/td&gt;
&lt;td&gt;❌ No Glue/catalog support&lt;/td&gt;
&lt;td&gt;Use Athena or Redshift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back (small files)&lt;/td&gt;
&lt;td&gt;✅ COPY TO works&lt;/td&gt;
&lt;td&gt;DuckDB Lambda for small writes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Discovery Questions for Partners
&lt;/h2&gt;

&lt;p&gt;When a customer asks about DuckDB Lambda + FSx for ONTAP S3 Access Points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is the typical dataset size per query? (DuckDB Lambda works best &amp;lt; 1 GB)&lt;/li&gt;
&lt;li&gt;How many concurrent queries are expected? (Lambda scales horizontally but each invocation is isolated)&lt;/li&gt;
&lt;li&gt;Is governance / audit trail required? (DuckDB has none — consider Athena + Lake Formation)&lt;/li&gt;
&lt;li&gt;Is the workload ad-hoc or scheduled? (Lambda excels at sporadic, event-driven queries)&lt;/li&gt;
&lt;li&gt;Does the team need SQL JOINs with other data sources? (DuckDB is isolated — no cross-source JOINs)&lt;/li&gt;
&lt;li&gt;Is sub-second latency required? (Warm DuckDB Lambda delivers; cold start adds ~1.9s)&lt;/li&gt;
&lt;li&gt;Is there an existing analytics platform? (If yes, DuckDB Lambda may be redundant)&lt;/li&gt;
&lt;li&gt;What is the budget tolerance? (DuckDB Lambda is the cheapest option in this series)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Governance Impact
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;DuckDB Lambda&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;IAM (Lambda execution role)&lt;/td&gt;
&lt;td&gt;Standard AWS IAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;S3 AP policy + IAM&lt;/td&gt;
&lt;td&gt;No table/column-level control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit trail&lt;/td&gt;
&lt;td&gt;CloudWatch Logs + CloudTrail&lt;/td&gt;
&lt;td&gt;Query text logged if configured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data classification&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;No tagging or masking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row/column security&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;All-or-nothing file access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catalog integration&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;No Glue, no schema registry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Governance model&lt;/strong&gt;: DuckDB Lambda relies entirely on IAM + S3 AP policy for access control. There is no built-in governance layer. For regulated workloads requiring table-level access control, column masking, or audit trails, use Athena + Lake Formation (Part 1 + Part 6) or Snowflake External Tables (Part 3).&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Readiness Score
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;AI Capability&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Operational Simplicity&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DuckDB Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★☆☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★☆☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Athena + Lake Formation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Snowflake External Table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMR Serverless Spark&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Governance&lt;/strong&gt;: Access control, audit, classification capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Query latency for typical workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Capability&lt;/strong&gt;: Built-in ML/AI integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: Total cost of ownership for low-frequency workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational Simplicity&lt;/strong&gt;: Setup and maintenance effort&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scoring methodology&lt;/strong&gt;: Each dimension rated by the author based on validated evidence. This is not an official AWS assessment. DuckDB Lambda scores highest on Cost and Simplicity but lowest on Governance and AI — it's the "quick and cheap" option, not the "governed enterprise" option.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cost Analysis
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Monthly Cost (1000 queries/day)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda invocations&lt;/td&gt;
&lt;td&gt;~$0.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda compute (1024 MB × 1s avg)&lt;/td&gt;
&lt;td&gt;~$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP (128 MB/s, existing)&lt;/td&gt;
&lt;td&gt;$0 incremental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP requests&lt;/td&gt;
&lt;td&gt;$0 (included in FSx)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$1.10/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Compare with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redshift Serverless (8 RPU): ~$2.88/hour when active&lt;/li&gt;
&lt;li&gt;Athena: $5/TB scanned (but no idle cost)&lt;/li&gt;
&lt;li&gt;EMR Serverless: ~$0.05/job (but 20s cold start per job)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DuckDB Lambda is the cheapest option for ad-hoc, low-frequency analytics on FSxN data.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use (and When Not To)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use DuckDB Lambda when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ad-hoc queries on &amp;lt; 1 GB datasets&lt;/li&gt;
&lt;li&gt;API-driven analytics (behind API Gateway)&lt;/li&gt;
&lt;li&gt;Cost is the primary concern&lt;/li&gt;
&lt;li&gt;No existing analytics infrastructure&lt;/li&gt;
&lt;li&gt;Edge/IoT data analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databricks customer waiting for UC + FSx for ONTAP S3 AP support&lt;/strong&gt;: Quick NAS data validation without spinning up a Databricks cluster or copying data to S3. Use as a lightweight bridge until Databricks UC natively supports FSx for ONTAP S3 Access Points.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Don't use when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Datasets &amp;gt; 10 GB (Lambda memory/timeout limits)&lt;/li&gt;
&lt;li&gt;High concurrency (&amp;gt; 100 concurrent queries)&lt;/li&gt;
&lt;li&gt;Need JOINs with DWH tables (use Redshift Spectrum)&lt;/li&gt;
&lt;li&gt;Need governance/catalog integration (use Athena + Lake Formation)&lt;/li&gt;
&lt;li&gt;Need Delta/Iceberg table format (not supported on FSxN S3 AP)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Known Failure Signatures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Next step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Can't find the home directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing &lt;code&gt;SET home_directory = '/tmp'&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add to handler initialization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Unknown error for HTTP HEAD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing path-style or endpoint config&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;s3_url_style = 'path'&lt;/code&gt; and &lt;code&gt;s3_endpoint&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HTTP 403 Forbidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IAM role missing S3 AP permissions&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;s3:GetObject&lt;/code&gt; on AP ARN to execution role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeout (15s Lambda limit)&lt;/td&gt;
&lt;td&gt;Dataset too large for Lambda memory&lt;/td&gt;
&lt;td&gt;Increase memory or use EMR Serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Out of Memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dataset exceeds Lambda memory&lt;/td&gt;
&lt;td&gt;Reduce query scope or increase to 10 GB memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start &amp;gt; 3s&lt;/td&gt;
&lt;td&gt;Layer too large or extension install slow&lt;/td&gt;
&lt;td&gt;Pre-install httpfs in layer (avoid runtime INSTALL)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Local Development
&lt;/h2&gt;

&lt;p&gt;You can also run DuckDB locally without Lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_credentials&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get_frozen_credentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duckdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:memory:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSTALL httpfs; LOAD httpfs;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET s3_region = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET s3_access_key_id = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET s3_secret_access_key = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET s3_session_token = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SET s3_url_style = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    SELECT status, COUNT(*), AVG(temperature)
    FROM read_parquet(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;your-ap-alias&amp;gt;/sensor-data/sensor_data.parquet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
    GROUP BY status
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: &lt;a href="//./part5-emr-spark.md"&gt;EMR Spark — Read-Write ETL on NAS Data&lt;/a&gt; — for teams that need distributed Spark processing with write-back capability and larger-than-memory datasets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6&lt;/strong&gt;: Redshift Spectrum + Lake Formation — for teams that need DWH-integrated analytics with enterprise governance on NAS data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 7&lt;/strong&gt;: Table Format Boundaries — why Delta, Iceberg, and Hudi can't write to FSx for ONTAP S3 AP, and what works instead&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Future exploration&lt;/strong&gt;: DuckDB's &lt;a href="https://duckdb.org/docs/extensions/iceberg.html" rel="noopener noreferrer"&gt;&lt;code&gt;iceberg&lt;/code&gt; extension&lt;/a&gt; may enable reading pre-existing Iceberg tables on FSx for ONTAP S3 AP (metadata + data files accessed via GetObject). Not validated in this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Previously in this series:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Athena — Query NAS Data In Place&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Databricks — A Layer-by-Layer Validation of Observed Boundaries&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: &lt;a href="https://dev.to/yoshiki0705/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-4f5g"&gt;Snowflake — From 'Access Denied' to Working External Tables&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://duckdb.org/docs/" rel="noopener noreferrer"&gt;DuckDB documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://duckdb.org/docs/extensions/httpfs/overview.html" rel="noopener noreferrer"&gt;DuckDB httpfs extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html" rel="noopener noreferrer"&gt;AWS Lambda arm64 (Graviton2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/accessing-data-via-s3-access-points.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 Access Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Key achievement&lt;/strong&gt;: This validation established that DuckDB Lambda is the lowest-cost analytics path for FSx for ONTAP S3 AP data — $0.00001/query with 452ms warm latency. Zero infrastructure, zero idle cost, and full read-write capability on Parquet files. The trade-off is zero governance — for regulated workloads, pair with Athena + Lake Formation or use Snowflake External Tables.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All benchmarks are from a specific test environment (FSx for ONTAP Single-AZ, 128 MB/s, ap-northeast-1). Scale throughput provisioning for production workloads. Full evidence: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack/duckdb-local" rel="noopener noreferrer"&gt;verification-pack/duckdb-local/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article is an independent validation report and does not represent AWS or NetApp official guidance. Product behavior and platform capabilities may change. Always validate in your own environment.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>duckdb</category>
      <category>serverless</category>
      <category>amazonfsxfornetappontap</category>
    </item>
    <item>
      <title>Snowflake and FSx for ONTAP S3 Access Points — From 'Access Denied' to Working External Tables</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Mon, 25 May 2026 14:52:05 +0000</pubDate>
      <link>https://forem.com/aws-builders/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-9k8</link>
      <guid>https://forem.com/aws-builders/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-9k8</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1&lt;/a&gt;, Athena worked cleanly. In &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Part 2&lt;/a&gt;, Databricks hit session policy boundaries. This Part 3 validates Snowflake's path — and it works.&lt;/p&gt;

&lt;p&gt;Snowflake can query FSx for ONTAP S3 Access Point data — but only with the correct stage configuration. Without the &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; parameter, SELECT fails with "access denied" while LIST works. With it, &lt;strong&gt;the tested read, governance, and AI paths work&lt;/strong&gt;: SELECT, External Tables, COPY INTO load, Directory Tables, governance tags, and &lt;strong&gt;8 out of 10 Cortex AI functions work on FSx data&lt;/strong&gt; (7 directly, 1 via COPY INTO for Cortex Search).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;LIST&lt;/th&gt;
&lt;th&gt;SELECT&lt;/th&gt;
&lt;th&gt;External Table&lt;/th&gt;
&lt;th&gt;Cortex AI (text)&lt;/th&gt;
&lt;th&gt;Vision AI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AP alias only (no ARN)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ Access Denied&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;AP alias + &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;✅ Via staging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This appears to be a recurring integration pattern in this series: platforms that generate restrictive session policies need an explicit S3 Access Point ARN parameter so the generated policy includes the regional access point ARN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick Decision Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-copy governed read on NAS data&lt;/strong&gt; → External Table with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full AI + maximum query performance&lt;/strong&gt; → COPY INTO internal table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG / semantic search over NAS documents&lt;/strong&gt; → COPY INTO → Cortex Search Service (198ms)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Read This Article
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This article is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reproduction-focused validation report&lt;/li&gt;
&lt;li&gt;Evidence from one environment (Snowflake Standard, ap-northeast-1)&lt;/li&gt;
&lt;li&gt;A configuration guide for Snowflake + FSx for ONTAP S3 AP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read by role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Snowflake admin&lt;/strong&gt;: Stage configuration → Working setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage engineer&lt;/strong&gt;: Evidence matrix → Root cause analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data engineer&lt;/strong&gt;: What works today → External Table setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner / SA&lt;/strong&gt;: Partner Decision Card → Architecture guidance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security / governance reviewer&lt;/strong&gt;: Governance Impact Summary → Regulated Workload Checklist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI/ML engineer&lt;/strong&gt;: AI / ML Integration Path → MLOps Boundary&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisite Concepts
&lt;/h2&gt;

&lt;p&gt;Before reading this article, it helps to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Snowflake Storage Integration&lt;/strong&gt; — an object that stores a reference to an IAM role for accessing external cloud storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snowflake External Stage&lt;/strong&gt; — maps a cloud storage URL to a storage integration for data access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External Table&lt;/strong&gt; — a Snowflake table that reads data directly from files on an external stage (no data copy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;&lt;/strong&gt; — a stage parameter that tells Snowflake to include the S3 Access Point ARN in its generated session policy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Access Point ARN vs S3 bucket ARN&lt;/strong&gt; — S3 AP uses &lt;code&gt;arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;name&amp;gt;&lt;/code&gt;, not &lt;code&gt;arn:aws:s3:::&amp;lt;bucket&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory Table&lt;/strong&gt; — a Snowflake feature that exposes file metadata (path, size, date) from a stage as a queryable table&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important premise&lt;/strong&gt;: Snowflake does NOT officially document FSx for ONTAP S3 Access Points as a supported External Stage storage backend. The &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; parameter exists in &lt;a href="https://docs.snowflake.com/en/sql-reference/sql/create-stage" rel="noopener noreferrer"&gt;Snowflake's CREATE STAGE documentation&lt;/a&gt; for S3 Access Points generally, but FSx for ONTAP S3 AP is not listed as a validated target. Our validation confirms that read and governance operations work when configured correctly, but this should not be interpreted as an officially supported configuration by Snowflake. Consult Snowflake Support before production use.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;Query structured and unstructured data stored on FSx for ONTAP from Snowflake — without copying data to a native S3 bucket. FSx for ONTAP S3 Access Points should make this possible by exposing NFS/SMB file data via S3 API.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1&lt;/a&gt;, Athena worked cleanly. In &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Part 2&lt;/a&gt;, Databricks required the &lt;code&gt;access_point&lt;/code&gt; field and still has limitations. This article validates Snowflake's path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test Environment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Snowflake Account: Standard edition, AWS ap-northeast-1
Warehouse: COMPUTE_WH (X-Small)
Role: ACCOUNTADMIN
FSx for ONTAP: &amp;lt;FILE_SYSTEM_ID&amp;gt; (ONTAP 9.17.1)
SVM: &amp;lt;SVM_NAME&amp;gt;
S3 Access Point: Internet-origin, UNIX file system user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;: This article validates Snowflake Standard edition. Enterprise features (e.g., advanced governance, private connectivity) may provide additional capabilities not tested here.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Snowflake accesses external data through a three-layer configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Storage Integration (IAM Role ARN + trust)
    │
    └── External Stage (S3 URL + AWS_ACCESS_POINT_ARN + file format)
            │
            └── External Table / SELECT @stage (data access)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Visual Story: Before and After
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Before: SELECT Fails Without &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_stage_without_arn&lt;/span&gt;
  &lt;span class="n"&gt;STORAGE_INTEGRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fsxn_verification_integration&lt;/span&gt;
  &lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'s3://&amp;lt;ap-alias&amp;gt;/'&lt;/span&gt;
  &lt;span class="n"&gt;FILE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;LIST&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage_without_arn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- ✅ Works&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage_without_arn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parquet&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- ❌ Access Denied&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9vi6isubrrvffsyunc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj9vi6isubrrvffsyunc2.png" alt="SELECT from stage fails — access denied error despite LIST working" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Failed to access remote file: access denied. Please check your credentials." — The same file that LIST found cannot be read.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  ✅ After: SELECT Succeeds With &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_stage_with_arn&lt;/span&gt;
  &lt;span class="n"&gt;STORAGE_INTEGRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fsxn_verification_integration&lt;/span&gt;
  &lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'s3://&amp;lt;ap-alias&amp;gt;/'&lt;/span&gt;
  &lt;span class="n"&gt;AWS_ACCESS_POINT_ARN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;ap-name&amp;gt;'&lt;/span&gt;
  &lt;span class="n"&gt;FILE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage_with_arn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parquet&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- ✅ SUCCESS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result: 3 rows of sensor data returned successfully.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"humidity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;32.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pressure"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1002.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sensor_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S004"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"normal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"temperature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;21.13&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"humidity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;45.63&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pressure"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1004.13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sensor_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S005"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"normal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"temperature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;23.07&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"humidity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;42.79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pressure"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1000.18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sensor_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"normal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"temperature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;36.96&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ✅ External Table Also Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;EXTERNAL&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;fsxn_sensor_ext_table&lt;/span&gt;
  &lt;span class="k"&gt;LOCATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage_with_arn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
  &lt;span class="n"&gt;FILE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;AUTO_REFRESH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;fsxn_sensor_ext_table&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- ✅ SUCCESS (3 rows)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Complete Capability Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read operations&lt;/strong&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;SELECT from &lt;code&gt;@​stage&lt;/code&gt; (Parquet)&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;GetObject with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT from &lt;code&gt;@​stage&lt;/code&gt; (CSV)&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;CSV with SKIP_HEADER works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT from &lt;code&gt;@​stage&lt;/code&gt; (JSON)&lt;/td&gt;
&lt;td&gt;✅ Expected&lt;/td&gt;
&lt;td&gt;Same GetObject path (no JSON files in test data)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Table (read)&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;CREATE + SELECT both succeed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LIST &lt;code&gt;@​stage&lt;/code&gt; (all prefixes)&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;Subdirectories included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET_PRESIGNED_URL&lt;/td&gt;
&lt;td&gt;✅ Observed&lt;/td&gt;
&lt;td&gt;Works but not officially supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Load operations&lt;/strong&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;COPY INTO (stage → table)&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;4.9s for Parquet load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Governance&lt;/strong&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;Governance Tags on External Table&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;CREATE TAG + ALTER TABLE SET TAG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SYSTEM$GET_TAG&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;Tag retrieval works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row Access Policy&lt;/td&gt;
&lt;td&gt;✅ Expected&lt;/td&gt;
&lt;td&gt;Standard Snowflake feature on tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column Masking&lt;/td&gt;
&lt;td&gt;✅ Expected&lt;/td&gt;
&lt;td&gt;Standard Snowflake feature on tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write operations&lt;/strong&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;PutObject (via COPY INTO unload)&lt;/td&gt;
&lt;td&gt;⚠️ TBD&lt;/td&gt;
&lt;td&gt;FSx S3 AP supports PutObject ≤5GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event-driven&lt;/strong&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;Snowpipe (auto-ingest)&lt;/td&gt;
&lt;td&gt;❌ Not possible&lt;/td&gt;
&lt;td&gt;S3 Event Notifications not supported on FSx S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AUTO_REFRESH on External Table&lt;/td&gt;
&lt;td&gt;❌ Not possible&lt;/td&gt;
&lt;td&gt;Requires S3 Event Notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transactional table formats&lt;/strong&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;Iceberg Table read (pre-existing metadata)&lt;/td&gt;
&lt;td&gt;⚠️ TBD&lt;/td&gt;
&lt;td&gt;Requires separate validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg Table write-back&lt;/td&gt;
&lt;td&gt;❌ Not suitable&lt;/td&gt;
&lt;td&gt;Conditional writes not supported on FSx for ONTAP S3 AP. For Iceberg, use Snowflake Managed Iceberg Table on standard S3 (COPY INTO from FSx for ONTAP External Stage → Iceberg table on S3). External engines (Spark, Athena) can then read the same Iceberg table.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta / Hudi write&lt;/td&gt;
&lt;td&gt;❌ Not suitable&lt;/td&gt;
&lt;td&gt;Conditional writes not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Supported file formats&lt;/strong&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;Parquet&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;Primary format for analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSV&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;With header skip, delimiter options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;✅ Expected&lt;/td&gt;
&lt;td&gt;Same read path as Parquet/CSV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avro&lt;/td&gt;
&lt;td&gt;✅ Expected&lt;/td&gt;
&lt;td&gt;Snowflake-supported format, same read path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ORC&lt;/td&gt;
&lt;td&gt;✅ Expected&lt;/td&gt;
&lt;td&gt;Snowflake-supported format, same read path&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: With &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;, Snowflake achieves broad read and governance integration for the tested paths. The only limitations are event-driven features (Snowpipe, AUTO_REFRESH) and transactional write formats (Iceberg, Delta) — both due to FSx S3 AP API limitations, not Snowflake limitations.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Root Cause: Session Policy ARN Mismatch
&lt;/h2&gt;

&lt;p&gt;When Snowflake performs &lt;code&gt;sts:AssumeRole&lt;/code&gt;, it applies a &lt;strong&gt;session policy&lt;/strong&gt;. Without &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;, this session policy uses standard S3 bucket ARN patterns that don't match the FSx S3 AP regional ARN format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Without AWS_ACCESS_POINT_ARN:
  Session policy allows GetObject on: arn:aws:s3:::*/*
  FSx S3 AP actual ARN:              arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;name&amp;gt;/object/*
  → NO MATCH → AccessDenied

With AWS_ACCESS_POINT_ARN:
  Session policy includes:            arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;name&amp;gt;/*
  → MATCH → GetObject succeeds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same pattern as Databricks Unity Catalog's &lt;code&gt;access_point&lt;/code&gt; field — both platforms need the S3 AP ARN explicitly specified to include it in the generated session policy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Support Confirmation (May 2026)&lt;/strong&gt;: Snowflake Support confirmed this resolution. The original issue (LIST works, SELECT fails with "access denied") is resolved by adding the &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; parameter to the stage definition. Unlike Databricks (where the equivalent &lt;code&gt;access_point&lt;/code&gt; field was never GA and has been removed), Snowflake's &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; is a documented, supported parameter in the &lt;a href="https://docs.snowflake.com/en/sql-reference/sql/create-stage" rel="noopener noreferrer"&gt;CREATE STAGE&lt;/a&gt; reference.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Evidence Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Evidence&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake integration&lt;/td&gt;
&lt;td&gt;DESCRIBE INTEGRATION&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Trust established&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stage metadata&lt;/td&gt;
&lt;td&gt;LIST &lt;code&gt;@​stage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;ListBucket path works (bucket-level ARN matches)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object read (no ARN)&lt;/td&gt;
&lt;td&gt;SELECT &lt;code&gt;@​stage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;❌ Fail&lt;/td&gt;
&lt;td&gt;GetObject blocked by session policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object read (with ARN)&lt;/td&gt;
&lt;td&gt;SELECT &lt;code&gt;@​stage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; resolves session policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Table&lt;/td&gt;
&lt;td&gt;CREATE + SELECT&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Governed table access works with ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same role direct&lt;/td&gt;
&lt;td&gt;AWS CLI List/Get/Head&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;IAM/AP/FSx permissions are correct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx authorization&lt;/td&gt;
&lt;td&gt;File system user permissions&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;FSx-side permission permits access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational health&lt;/td&gt;
&lt;td&gt;SVM DNS check&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Distinguish ReadTimeout from AccessDenied&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  FSx for ONTAP S3 AP Authorization Path
&lt;/h2&gt;

&lt;p&gt;FSx for ONTAP S3 Access Points use a &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-manage-access-fsxn.html" rel="noopener noreferrer"&gt;dual-layer authorization model&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — S3-side authorization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM identity-based policy (Snowflake's assumed role session)&lt;/li&gt;
&lt;li&gt;S3 Access Point resource policy&lt;/li&gt;
&lt;li&gt;Session policy generated by Snowflake (requires &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; to include AP ARN)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — FSx for ONTAP-side authorization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File system user associated with the access point&lt;/li&gt;
&lt;li&gt;UNIX mode-bits / NFSv4 ACLs (for UNIX security style volumes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the Snowflake validation, the initial failure occurred at &lt;strong&gt;Layer 1&lt;/strong&gt; — Snowflake's generated session policy did not include the S3 AP ARN pattern. Setting &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; resolves this by instructing Snowflake to include the AP ARN in the session policy, allowing both layers to be evaluated normally.&lt;/p&gt;




&lt;h2&gt;
  
  
  S3 API Compatibility and Snowflake Operations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Snowflake operation&lt;/th&gt;
&lt;th&gt;Likely S3 operation&lt;/th&gt;
&lt;th&gt;FSx S3 AP support&lt;/th&gt;
&lt;th&gt;Observed result (with ARN)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LIST &lt;code&gt;@​stage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;ListObjectsV2&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;td&gt;✅ Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT &lt;code&gt;@​stage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;GetObject / HeadObject&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;td&gt;✅ Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET_PRESIGNED_URL&lt;/td&gt;
&lt;td&gt;Presign / signed GetObject URL&lt;/td&gt;
&lt;td&gt;Presign not supported in FSx S3 AP docs&lt;/td&gt;
&lt;td&gt;Observed working; not a supported production path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Table read&lt;/td&gt;
&lt;td&gt;GetObject&lt;/td&gt;
&lt;td&gt;✅ Supported&lt;/td&gt;
&lt;td&gt;✅ Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg metadata read&lt;/td&gt;
&lt;td&gt;Head/Get + conditional&lt;/td&gt;
&lt;td&gt;Partial (conditional writes not supported)&lt;/td&gt;
&lt;td&gt;TBD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Comparison: Snowflake vs Databricks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Snowflake&lt;/th&gt;
&lt;th&gt;Databricks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Parameter name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; (on stage)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;access_point&lt;/code&gt; (on External Location)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LIST without parameter&lt;/td&gt;
&lt;td&gt;✅ Works&lt;/td&gt;
&lt;td&gt;❌ Blocked (before &lt;code&gt;access_point&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT without parameter&lt;/td&gt;
&lt;td&gt;❌ Fails&lt;/td&gt;
&lt;td&gt;❌ Fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT with parameter&lt;/td&gt;
&lt;td&gt;✅ Works&lt;/td&gt;
&lt;td&gt;✅ Works (explicit path only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Table / UC Table&lt;/td&gt;
&lt;td&gt;✅ Works&lt;/td&gt;
&lt;td&gt;❌ CREATE TABLE still fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subdirectory listing&lt;/td&gt;
&lt;td&gt;✅ Works&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.snowflake.com/en/sql-reference/sql/create-stage" rel="noopener noreferrer"&gt;CREATE STAGE docs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Databricks Support (May 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key difference&lt;/strong&gt;: Snowflake's &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; resolves the issue more completely than Databricks' &lt;code&gt;access_point&lt;/code&gt; field. Snowflake achieves full External Table support, while Databricks still cannot create UC tables.&lt;/p&gt;




&lt;h2&gt;
  
  
  Partner Decision Card
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Customer requirement&lt;/th&gt;
&lt;th&gt;Snowflake + FSx S3 AP today&lt;/th&gt;
&lt;th&gt;Recommended path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File discovery only&lt;/td&gt;
&lt;td&gt;✅ Works (LIST / Directory Table)&lt;/td&gt;
&lt;td&gt;Use directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query file contents in Snowflake&lt;/td&gt;
&lt;td&gt;✅ Works with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Configure stage with ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Governed Snowflake external tables&lt;/td&gt;
&lt;td&gt;✅ Works with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Configure stage with ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-copy SQL on NAS data&lt;/td&gt;
&lt;td&gt;✅ Snowflake or Athena&lt;/td&gt;
&lt;td&gt;Both work; choose by workload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake ML / Snowpark on NAS data&lt;/td&gt;
&lt;td&gt;✅ Possible via External Table&lt;/td&gt;
&lt;td&gt;Configure stage with ARN, validate Snowpark path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg Table on FSx S3 AP&lt;/td&gt;
&lt;td&gt;TBD (conditional writes not supported)&lt;/td&gt;
&lt;td&gt;Validate separately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Choose Snowflake when governed external tables, tags, Directory Tables, or Snowpark integration are required. Choose Athena when lightweight AWS-native serverless SQL over NAS data is sufficient.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Discovery Questions for Partners
&lt;/h2&gt;

&lt;p&gt;When a customer asks about Snowflake + FSx for ONTAP S3 Access Points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the workload read-only analytics, or does it require write-back?&lt;/li&gt;
&lt;li&gt;Is Snowflake governance (tags, row access policy, masking) required?&lt;/li&gt;
&lt;li&gt;Does the workload need real-time file detection (Snowpipe), or is scheduled refresh acceptable?&lt;/li&gt;
&lt;li&gt;Are the target files structured (Parquet/CSV/JSON) or unstructured (images/documents)?&lt;/li&gt;
&lt;li&gt;Is the data regulated (PHI, PII, financial)? If so, review presigned URL governance.&lt;/li&gt;
&lt;li&gt;Does the customer need Iceberg table format? (Write-back not supported on FSx S3 AP)&lt;/li&gt;
&lt;li&gt;What is the expected file count and average file size? (Impacts LIST/REFRESH latency)&lt;/li&gt;
&lt;li&gt;Is the Snowflake account in the same AWS region as FSx for ONTAP?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Governance Impact
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Governance impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LIST &lt;code&gt;@​stage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Works&lt;/td&gt;
&lt;td&gt;File inventory; not data access governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELECT &lt;code&gt;@​stage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅ Works (with ARN)&lt;/td&gt;
&lt;td&gt;Query-level access via Snowflake governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Table&lt;/td&gt;
&lt;td&gt;✅ Works (with ARN)&lt;/td&gt;
&lt;td&gt;Governed schema/table abstraction available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg Table&lt;/td&gt;
&lt;td&gt;❌ Write not suitable&lt;/td&gt;
&lt;td&gt;Conditional writes not supported; read of pre-existing tables TBD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET_PRESIGNED_URL&lt;/td&gt;
&lt;td&gt;⚠️ Observed only&lt;/td&gt;
&lt;td&gt;Risk of bypassing Snowflake query governance if misused&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;For regulated workloads, do not use &lt;code&gt;GET_PRESIGNED_URL&lt;/code&gt; as a workaround for query access. Even if URL generation is observed to work, it is not a governed Snowflake query path and should be reviewed separately for auditability, expiration, data classification, and access logging.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Governance Impact Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important premise&lt;/strong&gt;: FSx for ONTAP S3 Access Points are NOT officially documented by Snowflake as a supported External Stage storage backend. The governance paths described below are validated in this environment but should not be treated as officially supported configurations without Snowflake Support confirmation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Access path&lt;/th&gt;
&lt;th&gt;Governance model&lt;/th&gt;
&lt;th&gt;Auditability&lt;/th&gt;
&lt;th&gt;Production suitability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;External Table (with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Snowflake RBAC + Tags + Row Access Policy&lt;/td&gt;
&lt;td&gt;High (Snowflake Access History, query logs)&lt;/td&gt;
&lt;td&gt;Recommended governed read path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COPY INTO (load to Snowflake table)&lt;/td&gt;
&lt;td&gt;Full Snowflake governance on loaded data&lt;/td&gt;
&lt;td&gt;High (standard Snowflake table governance)&lt;/td&gt;
&lt;td&gt;Recommended for ML/AI workloads requiring full governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Directory Table + GET_PRESIGNED_URL&lt;/td&gt;
&lt;td&gt;File catalog governed; URL access is external&lt;/td&gt;
&lt;td&gt;Medium (catalog queries logged; URL access not logged by Snowflake)&lt;/td&gt;
&lt;td&gt;File discovery governed; downstream access requires separate audit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BUILD_SCOPED_FILE_URL&lt;/td&gt;
&lt;td&gt;Snowflake-mediated access&lt;/td&gt;
&lt;td&gt;High (access mediated through Snowflake privileges)&lt;/td&gt;
&lt;td&gt;Preferred for governed unstructured data access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GET_PRESIGNED_URL (direct)&lt;/td&gt;
&lt;td&gt;External access path&lt;/td&gt;
&lt;td&gt;Low (Snowflake does not log URL usage after generation)&lt;/td&gt;
&lt;td&gt;PoC / non-regulated only; requires separate access logging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Snowflake Access History captures query-level access to External Tables. However, presigned URL usage after generation is not tracked by Snowflake — use CloudTrail S3 data events for downstream audit if required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  MLOps Boundary
&lt;/h3&gt;

&lt;p&gt;Reading data from FSx for ONTAP S3 AP via Snowflake External Table does not automatically make the downstream ML workflow governed.&lt;/p&gt;

&lt;p&gt;If the data accessed via External Table or COPY INTO is used for ML or GenAI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register derived datasets in governed Snowflake tables&lt;/li&gt;
&lt;li&gt;Track experiments with Snowflake ML lineage or external experiment tracking&lt;/li&gt;
&lt;li&gt;Document source data access path (stage name, S3 AP alias, prefix, timestamp)&lt;/li&gt;
&lt;li&gt;Record whether training data lineage is captured within Snowflake or externalized&lt;/li&gt;
&lt;li&gt;Ensure Snowpark ML workloads use appropriate role privileges&lt;/li&gt;
&lt;li&gt;If using Cortex functions, validate that input data classification is appropriate for the model&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Snowflake's &lt;a href="https://docs.snowflake.com/en/developer-guide/snowpark-ml/lineage" rel="noopener noreferrer"&gt;ML Lineage&lt;/a&gt; tracks feature-to-model relationships. If the source data path is an External Table on FSx S3 AP, document this as the lineage origin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AI / RAG Data Readiness Checklist
&lt;/h3&gt;

&lt;p&gt;If the FSx for ONTAP S3 AP data is intended for AI, RAG, or GenAI pipelines via Snowflake:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Are documents classified by sensitivity (PHI, PII, financial, internal, public)?&lt;/li&gt;
&lt;li&gt;[ ] Are file-level permissions preserved or re-modeled for the AI pipeline?&lt;/li&gt;
&lt;li&gt;[ ] Is metadata available for filtering and retrieval (file type, date, owner)? → Use Directory Table&lt;/li&gt;
&lt;li&gt;[ ] Is freshness requirement defined (real-time, daily, weekly)? → Define REFRESH schedule&lt;/li&gt;
&lt;li&gt;[ ] Is read-only access sufficient, or does the pipeline need write-back?&lt;/li&gt;
&lt;li&gt;[ ] Is human review required for generated output before downstream use?&lt;/li&gt;
&lt;li&gt;[ ] Is permission-aware retrieval required (user A sees only their authorized documents)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If permission-aware retrieval is required, define one of:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enforce at source access path&lt;/strong&gt; — use per-user or per-group S3 Access Points with scoped file system users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-model permissions in metadata index&lt;/strong&gt; — extract file-level ACLs into Directory Table metadata and filter at query time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter retrieval results by user/group claims&lt;/strong&gt; — apply Snowflake Row Access Policy on External Table based on authenticated user identity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not proceed&lt;/strong&gt; until authorization model is validated and approved by security owner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Snowflake + FSx S3 AP approval requirements&lt;/strong&gt; (for regulated workloads):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data owner approval for External Table / stage access&lt;/li&gt;
&lt;li&gt;Security owner approval for presigned URL generation policy&lt;/li&gt;
&lt;li&gt;Platform owner approval for COPY INTO (data leaves FSx, enters Snowflake)&lt;/li&gt;
&lt;li&gt;Defined: allowed prefix, allowed operations, refresh schedule, expiration date&lt;/li&gt;
&lt;li&gt;Approval record location (where the decision is stored)&lt;/li&gt;
&lt;li&gt;Review / expiration date (when the approval must be re-evaluated)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For regulated workloads, exercise caution with:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET_PRESIGNED_URL for patient-facing or financial data (bypasses Snowflake query governance)&lt;/li&gt;
&lt;li&gt;COPY INTO without data classification review (data moves from FSx to Snowflake storage)&lt;/li&gt;
&lt;li&gt;Cortex LLM functions on sensitive data without human review gate&lt;/li&gt;
&lt;li&gt;Unreviewed access to regulated datasets via scoped URLs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Unstructured Data Support
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Support&lt;/th&gt;
&lt;th&gt;Access Method&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Images (JPEG, PNG, TIFF)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;GET_PRESIGNED_URL / BUILD_SCOPED_FILE_URL&lt;/td&gt;
&lt;td&gt;Thumbnail generation, ML inference, quality inspection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video (MP4, MOV)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;GET_PRESIGNED_URL&lt;/td&gt;
&lt;td&gt;Streaming, frame extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents (PDF, DOCX)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;GET_PRESIGNED_URL / Snowpark File Access&lt;/td&gt;
&lt;td&gt;Text extraction, RAG, document processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audio (WAV, MP3)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;GET_PRESIGNED_URL&lt;/td&gt;
&lt;td&gt;Transcription, speech analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binary / Archives&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;GET_PRESIGNED_URL&lt;/td&gt;
&lt;td&gt;Download, transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How to manage unstructured data as a library:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enable Directory Table for file catalog&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_stage&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;DIRECTORY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_stage&lt;/span&gt; &lt;span class="n"&gt;REFRESH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Query file catalog (search by path, size, date)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;RELATIVE_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LAST_MODIFIED&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DIRECTORY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;RELATIVE_PATH&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%images/%'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;LAST_MODIFIED&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Generate download URL for applications (valid 1 hour)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;GET_PRESIGNED_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'images/photo001.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Generate Snowflake-proxied secure URL&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;BUILD_SCOPED_FILE_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'documents/report.pdf'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: AUTO_REFRESH is not available because FSx S3 AP does not support S3 Event Notifications (GetBucketNotificationConfiguration is not supported). Use &lt;code&gt;ALTER STAGE REFRESH&lt;/code&gt; manually or via Snowflake Task on a schedule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;URL type guidance&lt;/strong&gt;: Use &lt;code&gt;BUILD_SCOPED_FILE_URL&lt;/code&gt; when you want access mediated through Snowflake role privileges (governed path). Treat &lt;code&gt;GET_PRESIGNED_URL&lt;/code&gt; as an external object access path that bypasses Snowflake query governance and requires separate review for regulated workloads.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  AI / ML Integration Path
&lt;/h2&gt;

&lt;p&gt;Snowflake provides AI/ML capabilities that can leverage FSx for ONTAP data via S3 AP. &lt;strong&gt;7 out of 9 tested Cortex AI functions work directly on FSx S3 AP data without copying.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Snowflake AI/ML Feature&lt;/th&gt;
&lt;th&gt;FSx S3 AP Compatibility&lt;/th&gt;
&lt;th&gt;Access Path&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORTEX.SUMMARIZE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;External Table → Cortex&lt;/td&gt;
&lt;td&gt;3.3s&lt;/td&gt;
&lt;td&gt;Text summarization on NAS documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORTEX.TRANSLATE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;External Table → Cortex&lt;/td&gt;
&lt;td&gt;5.1s&lt;/td&gt;
&lt;td&gt;Multi-language support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORTEX.SENTIMENT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;External Table → Cortex&lt;/td&gt;
&lt;td&gt;2.5s&lt;/td&gt;
&lt;td&gt;Sentiment analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORTEX.COMPLETE (text)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;External Table → Cortex&lt;/td&gt;
&lt;td&gt;16s&lt;/td&gt;
&lt;td&gt;AI analysis, anomaly detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORTEX.EXTRACT_ANSWER&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;External Table → Cortex&lt;/td&gt;
&lt;td&gt;2.7s&lt;/td&gt;
&lt;td&gt;Information extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PARSE_DOCUMENT (OCR)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;td&gt;Stage path → OCR&lt;/td&gt;
&lt;td&gt;~8s&lt;/td&gt;
&lt;td&gt;Invoice/report text extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;COMPLETE (Vision/Multimodal)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Workaround&lt;/td&gt;
&lt;td&gt;COPY FILES → internal stage → TO_FILE&lt;/td&gt;
&lt;td&gt;41s&lt;/td&gt;
&lt;td&gt;Image analysis, defect detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TO_FILE on FSx S3 AP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;"Remote file not found"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cortex Search (RAG)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;External Table → COPY INTO → Cortex Search Service&lt;/td&gt;
&lt;td&gt;198ms query&lt;/td&gt;
&lt;td&gt;Semantic search over NAS documents&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key finding&lt;/strong&gt;: Text-based Cortex functions, PARSE_DOCUMENT, and Cortex Search all work on FSx S3 AP data (Cortex Search requires COPY INTO as a staging step). Vision AI (multimodal COMPLETE) requires a staging step because &lt;code&gt;TO_FILE()&lt;/code&gt; cannot resolve files on S3 AP external stages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validated AI/ML paths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Cortex LLM SUMMARIZE on External Table data — AI-generated summary in 3.3s&lt;/li&gt;
&lt;li&gt;✅ Cortex TRANSLATE on External Table data — English to Japanese in 5.1s&lt;/li&gt;
&lt;li&gt;✅ Cortex SENTIMENT on External Table data — sentiment scores in 2.5s&lt;/li&gt;
&lt;li&gt;✅ Cortex COMPLETE (text) on External Table data — AI anomaly analysis in 16s&lt;/li&gt;
&lt;li&gt;✅ Cortex EXTRACT_ANSWER on External Table data — information extraction in 2.7s&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;PARSE_DOCUMENT (OCR)&lt;/strong&gt; on FSx S3 AP stage file — text extraction from images in ~8s&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;COMPLETE (Vision AI)&lt;/strong&gt; via COPY FILES workaround — image analysis in 41s (pixtral-large)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Cortex Search (RAG)&lt;/strong&gt; — External Table → COPY INTO → Cortex Search Service → semantic query in 198ms&lt;/li&gt;
&lt;li&gt;✅ COPY INTO loads NAS data into Snowflake tables → available for all Cortex/ML functions&lt;/li&gt;
&lt;li&gt;✅ Directory Table catalogs unstructured files → enables file discovery for processing pipelines&lt;/li&gt;
&lt;li&gt;✅ GET_PRESIGNED_URL generates download URLs → enables external ML services to access files&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vision AI Workaround (Validated)
&lt;/h3&gt;

&lt;p&gt;Direct &lt;code&gt;TO_FILE()&lt;/code&gt; on FSx S3 AP external stage returns "Remote file not found." The workaround:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Create unencrypted internal stage (SNOWFLAKE_SSE required — default encryption blocks TO_FILE)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_ai_stage&lt;/span&gt; &lt;span class="n"&gt;ENCRYPTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SNOWFLAKE_SSE'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Copy image from FSx S3 AP to internal stage&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;FILES&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_ai_stage&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_ap_arn_test_stage&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;invoice_sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Enable Cross-Region Inference (required for vision models in ap-northeast-1)&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;ACCOUNT&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;CORTEX_ENABLED_CROSS_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ANY_REGION'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 4. Run Vision AI&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_ai_stage&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;DIRECTORY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_ai_stage&lt;/span&gt; &lt;span class="n"&gt;REFRESH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CORTEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COMPLETE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pixtral-large'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Describe this invoice. What is the invoice number, customer, and amount?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FILE&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;vision_result&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;TO_FILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BUILD_SCOPED_FILE_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_ai_stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RELATIVE_PATH&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;FILE&lt;/span&gt;
      &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DIRECTORY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_ai_stage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;RELATIVE_PATH&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%.png'&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Vision AI correctly identified Invoice #INV-2026-0524, Customer: Acme Corp, Amount: USD 1,234.56.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Data residency note&lt;/strong&gt;: The COPY FILES step moves image data from FSx for ONTAP to Snowflake-managed internal storage. Cross-Region Inference may route data to US/EU regions for model processing. Verify compliance with your data residency requirements before enabling for regulated workloads.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Cortex Search (RAG) — Validated
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-search/cortex-search-overview" rel="noopener noreferrer"&gt;Cortex Search&lt;/a&gt; provides semantic search over text data — the Snowflake-native RAG building block. The validated path uses External Table → COPY INTO → Cortex Search Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Load FSx S3 AP data into internal table (required for Cortex Search)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;sensor_documents&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage_with_arn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
  &lt;span class="n"&gt;FILE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Create Cortex Search Service on the loaded data&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;CORTEX&lt;/span&gt; &lt;span class="k"&gt;SEARCH&lt;/span&gt; &lt;span class="n"&gt;SERVICE&lt;/span&gt; &lt;span class="n"&gt;sensor_search_service&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;text_column&lt;/span&gt;
  &lt;span class="n"&gt;WAREHOUSE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COMPUTE_WH&lt;/span&gt;
  &lt;span class="n"&gt;TARGET_LAG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1 hour'&lt;/span&gt;
  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sensor_documents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Semantic search query&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;PARSE_JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CORTEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SEARCH_PREVIEW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'sensor_search_service'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{"query": "high temperature anomaly", "columns": ["text_column"], "limit": 5}'&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Result: Relevant documents returned in 198ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dataset context&lt;/strong&gt;: This validation used the sensor data loaded via COPY INTO from FSx S3 AP (1000 rows of IoT sensor readings). Cortex Search performance at scale (millions of documents, large text corpora) should be validated separately — 198ms is a sizing reference for this dataset size, not a service-level guarantee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GA status&lt;/strong&gt;: Verify that Cortex Search Service and its query functions are Generally Available (GA) in your Snowflake edition and region before production use. Preview features may not be covered by Snowflake SLA and should not be used for regulated workloads without explicit vendor confirmation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5si3pf0c6v43hv0uz3ws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5si3pf0c6v43hv0uz3ws.png" alt="Cortex Search Service created successfully" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cortex Search Service created on data loaded from FSx for ONTAP via COPY INTO.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxn2exii68ji0y8s875p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwxn2exii68ji0y8s875p.png" alt="Cortex Search semantic query returns results in 198ms" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Semantic search query returns relevant results in 198ms — RAG-style retrieval over NAS-originated data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: Cortex Search requires COPY INTO (data must be in a Snowflake internal table), but the end-to-end path from FSx for ONTAP → External Stage → COPY INTO → Cortex Search Service → semantic query is validated. This provides a Snowflake-native RAG path for NAS documents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Data residency change&lt;/strong&gt;: COPY INTO moves data from FSx for ONTAP to Snowflake-managed storage. Once loaded, the data is subject to Snowflake's storage lifecycle, not ONTAP's. For regulated workloads, obtain data owner approval before COPY INTO and document the residency change in your compliance records. Cortex Search Service indexes are stored in the same region as the Snowflake account — no cross-region data movement occurs for the index itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comparison with Bedrock Knowledge Bases&lt;/strong&gt;: Cortex Search requires a COPY INTO step (data moves to Snowflake storage). Bedrock Knowledge Bases can read directly from FSx S3 AP without copying. Choose Cortex Search when the RAG pipeline must stay within Snowflake governance. Choose Bedrock KB when data residency on FSx is mandatory and AWS-native RAG is preferred.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;PoC Quick Start — Validate Cortex Search on your NAS data in 3 steps (estimated: 30 minutes with pre-configured stage):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configure External Stage with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; (see Configuration Guide above)&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;COPY INTO &amp;lt;target_table&amp;gt; FROM @​fsxn_stage/&amp;lt;your-documents-prefix&amp;gt;/&lt;/code&gt; to load text data&lt;/li&gt;
&lt;li&gt;Create Cortex Search Service on the loaded table and run a semantic query to validate retrieval quality&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Manufacturing Use Case: OCR + AI on NAS Data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- OCR: Extract text from inspection report image stored on FSx for ONTAP&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CORTEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PARSE_DOCUMENT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'media/documents/invoice_sample.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'mode'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'OCR'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ocr_result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Result: "INVOICE #INV-2026-0524", "Customer: Acme Corp", "Amount: USD 1,234.56"&lt;/span&gt;

&lt;span class="c1"&gt;-- AI Analysis: Analyze sensor data for anomalies&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;SNOWFLAKE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CORTEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COMPLETE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mistral-large2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Analyze this IoT sensor reading and identify anomalies: '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;VARCHAR&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ai_analysis&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;fsxn_sensor_ext_table&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos89kokv4hv8hug0vhpb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos89kokv4hv8hug0vhpb.png" alt="PARSE_DOCUMENT OCR extracts text from invoice image on FSx S3 AP" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PARSE_DOCUMENT (OCR mode) extracts text from an image on FSx for ONTAP via S3 AP — works directly without copying.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89hpmi2enry58img5lmv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89hpmi2enry58img5lmv.png" alt="Cortex COMPLETE generates AI analysis of sensor data from FSx S3 AP" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cortex COMPLETE (mistral-large2) generates AI anomaly analysis of IoT sensor data on FSx for ONTAP — works directly on External Table data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuf6r4zvo9ieate47tmqv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuf6r4zvo9ieate47tmqv.png" alt="Vision AI successfully analyzes invoice image (via staging workaround)" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Vision AI (pixtral-large) correctly extracts invoice details from an image originally on FSx for ONTAP — requires COPY FILES to internal stage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not validated in this article:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snowpark File Access (&lt;code&gt;SnowflakeFile.open()&lt;/code&gt;) for direct binary file processing in UDFs&lt;/li&gt;
&lt;li&gt;AI_TRANSCRIBE for audio files on FSx S3 AP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Comparison with Databricks AI/ML path:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AI/ML Capability&lt;/th&gt;
&lt;th&gt;Snowflake + FSx S3 AP&lt;/th&gt;
&lt;th&gt;Databricks + FSx S3 AP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Governed table as ML input&lt;/td&gt;
&lt;td&gt;✅ External Table&lt;/td&gt;
&lt;td&gt;❌ UC Table creation blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text AI (LLM) on NAS data&lt;/td&gt;
&lt;td&gt;✅ 6 Cortex functions direct&lt;/td&gt;
&lt;td&gt;⚠️ boto3 + external LLM (bypasses UC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vision AI on NAS images&lt;/td&gt;
&lt;td&gt;✅ Via staging workaround (41s)&lt;/td&gt;
&lt;td&gt;⚠️ boto3 driver-only (bypasses UC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OCR / Document extraction&lt;/td&gt;
&lt;td&gt;✅ PARSE_DOCUMENT direct (8s)&lt;/td&gt;
&lt;td&gt;⚠️ boto3 + external OCR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature engineering&lt;/td&gt;
&lt;td&gt;✅ Snowpark DataFrame on External Table&lt;/td&gt;
&lt;td&gt;⚠️ spark.read with explicit path only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File catalog for ML pipeline&lt;/td&gt;
&lt;td&gt;✅ Directory Table&lt;/td&gt;
&lt;td&gt;⚠️ dbutils.fs.ls (top-level only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG over NAS documents&lt;/td&gt;
&lt;td&gt;✅ Cortex Search (via COPY INTO, 198ms)&lt;/td&gt;
&lt;td&gt;⚠️ boto3 + external RAG (bypasses UC)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: Snowflake's AI/ML path benefits from governed External Tables and direct Cortex function access — 8 out of 10 tested functions work on FSx data (7 directly without copying, 1 via COPY INTO for Cortex Search). Databricks' AI/ML path is limited by UC table creation failure, forcing boto3 workarounds that bypass governance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For end-to-end RAG on NAS documents&lt;/strong&gt;: Use Snowflake &lt;strong&gt;Cortex Search&lt;/strong&gt; (validated: External Table → COPY INTO → Cortex Search Service, 198ms query latency) or &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-build-rag-with-bedrock.html" rel="noopener noreferrer"&gt;Amazon Bedrock Knowledge Bases&lt;/a&gt; as the AWS-documented path (no copy needed).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decision guidance&lt;/strong&gt;: Use Snowflake when the customer already needs Snowflake governance, Cortex/Snowpark processing, or table-based feature engineering. Use Bedrock Knowledge Bases when the primary requirement is AWS-native permission-aware RAG over NAS documents.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Comparison: Snowflake vs Databricks (Governance)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Governance Capability&lt;/th&gt;
&lt;th&gt;Snowflake + FSx S3 AP&lt;/th&gt;
&lt;th&gt;Databricks + FSx S3 AP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Table creation&lt;/td&gt;
&lt;td&gt;✅ External Table&lt;/td&gt;
&lt;td&gt;❌ CREATE TABLE fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data classification tags&lt;/td&gt;
&lt;td&gt;✅ Governance Tags&lt;/td&gt;
&lt;td&gt;❌ UC Table not creatable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access control&lt;/td&gt;
&lt;td&gt;✅ Row Access Policy&lt;/td&gt;
&lt;td&gt;❌ UC governance not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File catalog&lt;/td&gt;
&lt;td&gt;✅ Directory Table&lt;/td&gt;
&lt;td&gt;⚠️ dbutils.fs.ls (top-level only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secure URL generation&lt;/td&gt;
&lt;td&gt;✅ BUILD_SCOPED_FILE_URL&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Column masking&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COPY INTO (data load)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unstructured data catalog&lt;/td&gt;
&lt;td&gt;✅ Directory Table + Presigned URL&lt;/td&gt;
&lt;td&gt;⚠️ boto3 only (bypasses governance)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway&lt;/strong&gt;: In this validation, Snowflake with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; achieved a more complete governed read path than the Databricks path tested in Part 2. Snowflake can create governed tables, apply tags, and manage unstructured data catalogs — capabilities that remain blocked in Databricks due to UC table creation failure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For regulated workloads&lt;/strong&gt;: Snowflake provides a more complete governed path today (External Table + Tags + Row Access Policy + audit trail). Databricks requires staged ingestion to S3 for equivalent governance. If your compliance framework requires governed table-level access control on the data, Snowflake is the validated path for FSx S3 AP integration.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Business Impact
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Observed result&lt;/th&gt;
&lt;th&gt;Business impact&lt;/th&gt;
&lt;th&gt;Recommended decision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Zero-copy Snowflake query over NAS&lt;/td&gt;
&lt;td&gt;✅ Works (with ARN)&lt;/td&gt;
&lt;td&gt;Eliminates copy pipeline&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake governance on FSx data&lt;/td&gt;
&lt;td&gt;✅ External Table works&lt;/td&gt;
&lt;td&gt;Governed table abstraction available&lt;/td&gt;
&lt;td&gt;Create External Tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File inventory from Snowflake&lt;/td&gt;
&lt;td&gt;✅ Works&lt;/td&gt;
&lt;td&gt;Metadata cataloging possible&lt;/td&gt;
&lt;td&gt;Use LIST / Directory Tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG / AI over NAS documents&lt;/td&gt;
&lt;td&gt;✅ Cortex Search validated (198ms)&lt;/td&gt;
&lt;td&gt;Snowflake-native RAG path available&lt;/td&gt;
&lt;td&gt;COPY INTO → Cortex Search Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text AI on NAS data (no copy)&lt;/td&gt;
&lt;td&gt;✅ 7 functions direct&lt;/td&gt;
&lt;td&gt;AI processing without data movement&lt;/td&gt;
&lt;td&gt;Use Cortex functions on External Table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Detailed validation metrics (refresh duration, file count, query latency, COPY INTO duration, URL generation success rate) should be recorded in the &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack" rel="noopener noreferrer"&gt;verification-pack&lt;/a&gt; evidence files rather than treated as universal benchmark numbers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Use Case Fit Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Best current path&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL analytics on structured NAS files&lt;/td&gt;
&lt;td&gt;Snowflake External Table or Athena&lt;/td&gt;
&lt;td&gt;Both validated; Snowflake adds governance tags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unstructured data catalog&lt;/td&gt;
&lt;td&gt;Snowflake Directory Table&lt;/td&gt;
&lt;td&gt;File metadata queryable with governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data load from NAS to Snowflake&lt;/td&gt;
&lt;td&gt;COPY INTO from FSx S3 AP stage&lt;/td&gt;
&lt;td&gt;Validated (4.9s for Parquet)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG over NAS documents&lt;/td&gt;
&lt;td&gt;Cortex Search (via COPY INTO, validated 198ms) or Bedrock KB (AWS-native)&lt;/td&gt;
&lt;td&gt;Cortex Search validated; Bedrock KB is AWS-documented path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML feature engineering&lt;/td&gt;
&lt;td&gt;Snowpark DataFrame on External Table&lt;/td&gt;
&lt;td&gt;Governed read path available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time ingestion&lt;/td&gt;
&lt;td&gt;Not FSx S3 AP path&lt;/td&gt;
&lt;td&gt;Use native S3 + Snowpipe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg / transactional tables&lt;/td&gt;
&lt;td&gt;Not FSx S3 AP path&lt;/td&gt;
&lt;td&gt;Use native S3 for write-back&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Cost Model Considerations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Cost driver&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Snowflake warehouse&lt;/td&gt;
&lt;td&gt;Credit consumption during queries&lt;/td&gt;
&lt;td&gt;X-Small sufficient for validation; scale per workload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP&lt;/td&gt;
&lt;td&gt;Throughput capacity + storage&lt;/td&gt;
&lt;td&gt;S3 AP queries share throughput with NFS/SMB workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP requests&lt;/td&gt;
&lt;td&gt;No additional S3 request charges&lt;/td&gt;
&lt;td&gt;FSx S3 AP does not incur separate S3 API fees&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data transfer&lt;/td&gt;
&lt;td&gt;Standard AWS data transfer&lt;/td&gt;
&lt;td&gt;Snowflake SaaS in same region minimizes transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Cost comparison across engines is not the focus of this article. Snowflake's credit-based model differs fundamentally from Athena's per-TB-scanned model. Evaluate based on workload pattern, governance requirements, and existing Snowflake investment.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Configuration Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create Storage Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;STORAGE&lt;/span&gt; &lt;span class="n"&gt;INTEGRATION&lt;/span&gt; &lt;span class="n"&gt;fsxn_integration&lt;/span&gt;
  &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXTERNAL_STAGE&lt;/span&gt;
  &lt;span class="n"&gt;STORAGE_PROVIDER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'S3'&lt;/span&gt;
  &lt;span class="n"&gt;ENABLED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;TRUE&lt;/span&gt;
  &lt;span class="n"&gt;STORAGE_AWS_ROLE_ARN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'arn:aws:iam::&amp;lt;account&amp;gt;:role/&amp;lt;role-name&amp;gt;'&lt;/span&gt;
  &lt;span class="n"&gt;STORAGE_ALLOWED_LOCATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s3://&amp;lt;ap-alias&amp;gt;/'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create Stage WITH &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;STAGE&lt;/span&gt; &lt;span class="n"&gt;fsxn_stage&lt;/span&gt;
  &lt;span class="n"&gt;STORAGE_INTEGRATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fsxn_integration&lt;/span&gt;
  &lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'s3://&amp;lt;ap-alias&amp;gt;/'&lt;/span&gt;
  &lt;span class="n"&gt;AWS_ACCESS_POINT_ARN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;ap-name&amp;gt;'&lt;/span&gt;
  &lt;span class="n"&gt;FILE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;LIST&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                    &lt;span class="c1"&gt;-- File discovery&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parquet&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- Data read&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Create External Table (optional)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The following DDL is simplified for readability. See the &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/integrations/snowflake/sql" rel="noopener noreferrer"&gt;GitHub SQL scripts&lt;/a&gt; for the exact tested definition.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;EXTERNAL&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;my_ext_table&lt;/span&gt;
  &lt;span class="k"&gt;LOCATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sensor&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
  &lt;span class="n"&gt;FILE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;AUTO_REFRESH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Internal Table vs External Table — Design Guide
&lt;/h2&gt;

&lt;p&gt;Understanding the difference between internal (managed) tables and external tables is critical for architecture decisions when integrating FSx for ONTAP with Snowflake.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;External Table (on FSx S3 AP)&lt;/th&gt;
&lt;th&gt;Internal Table (COPY INTO)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remains on FSx for ONTAP (zero-copy)&lt;/td&gt;
&lt;td&gt;Copied into Snowflake-managed storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-protocol access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same data via NFS/SMB/S3 AP simultaneously&lt;/td&gt;
&lt;td&gt;Only accessible via Snowflake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data freshness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time (reads current file state)&lt;/td&gt;
&lt;td&gt;Stale until next COPY INTO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slower (estimated ~2-5s for small queries based on observed S3 AP GetObject latency)&lt;/td&gt;
&lt;td&gt;Faster (sub-second with micro-partitions, pruning)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Governance (Tags, Masking)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Full support&lt;/td&gt;
&lt;td&gt;✅ Full support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time Travel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Not available&lt;/td&gt;
&lt;td&gt;✅ Available (up to 90 days)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cortex AI (text functions)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Direct (SUMMARIZE, TRANSLATE, etc.)&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cortex AI (Vision/TO_FILE)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ TO_FILE blocked on FSx S3 AP&lt;/td&gt;
&lt;td&gt;✅ Works on internal stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cortex Search (RAG)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Requires COPY INTO first&lt;/td&gt;
&lt;td&gt;✅ Direct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ONTAP features preserved&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Snapshot, FlexClone, Dedup, FPolicy&lt;/td&gt;
&lt;td&gt;❌ Data is outside ONTAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FSx for ONTAP only (no Snowflake storage)&lt;/td&gt;
&lt;td&gt;FSx + Snowflake storage (duplicate)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Decision Flowchart
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q: Does the data need to stay on FSx for ONTAP?
├── YES → External Table
│         Q: Do you need Vision AI or Cortex Search?
│         ├── YES → Hybrid: External Table + selective COPY INTO
│         └── NO → External Table is sufficient (text AI works directly)
│
└── NO → COPY INTO internal table
          Q: Do you need real-time freshness?
          ├── YES → Scheduled COPY INTO (Task) or FPolicy → Lambda → Snowpipe
          └── NO → Batch COPY INTO on schedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cost Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;FSx Storage&lt;/th&gt;
&lt;th&gt;Snowflake Storage&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;External Table only&lt;/td&gt;
&lt;td&gt;✅ (existing)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Read-heavy, compliance, multi-protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COPY INTO (full)&lt;/td&gt;
&lt;td&gt;✅ (existing)&lt;/td&gt;
&lt;td&gt;+ full copy&lt;/td&gt;
&lt;td&gt;Max performance, Time Travel, full AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid (External + selective COPY)&lt;/td&gt;
&lt;td&gt;✅ (existing)&lt;/td&gt;
&lt;td&gt;+ images/RAG data only&lt;/td&gt;
&lt;td&gt;AI workloads with data residency needs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Industry-Specific Recommendations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Industry&lt;/th&gt;
&lt;th&gt;Recommended Pattern&lt;/th&gt;
&lt;th&gt;Rationale&lt;/th&gt;
&lt;th&gt;PoC Success Criteria&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Manufacturing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External Table + PARSE_DOCUMENT (OCR)&lt;/td&gt;
&lt;td&gt;Data stays on FSx; inspection images processed in place&lt;/td&gt;
&lt;td&gt;OCR extracts text from 10+ inspection images in &amp;lt;10s each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Financial Services&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hybrid (External Table + COPY INTO for Cortex Search)&lt;/td&gt;
&lt;td&gt;Compliance requires data on FSx; RAG needs internal table&lt;/td&gt;
&lt;td&gt;Cortex Search returns relevant compliance docs in &amp;lt;500ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Healthcare&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External Table + SnapLock&lt;/td&gt;
&lt;td&gt;PHI must not leave controlled storage; immutable audit&lt;/td&gt;
&lt;td&gt;SELECT on External Table succeeds with governance tags applied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Media / Entertainment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External Table + COPY FILES (Vision AI)&lt;/td&gt;
&lt;td&gt;Large media files stay on FSx; selective staging for AI&lt;/td&gt;
&lt;td&gt;Vision AI describes image content correctly via staging path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-Industry Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;COPY INTO (full)&lt;/td&gt;
&lt;td&gt;Maximum query performance; data duplication acceptable&lt;/td&gt;
&lt;td&gt;COPY INTO completes in &amp;lt;10s for representative dataset&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Snowpipe Alternatives for FSx for ONTAP
&lt;/h2&gt;

&lt;p&gt;Since FSx S3 AP does not support S3 Event Notifications, standard Snowpipe auto-ingest is not available. Use these alternatives:&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: FPolicy → Lambda → SNS → Snowpipe REST API (Recommended)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSx for ONTAP ──FPolicy──▶ Lambda ──▶ SNS ──▶ Snowpipe REST API ──▶ COPY INTO target table
     │                                              │
     └── NFS/SMB users access same data             └── Snowflake governance on loaded data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Seconds (&amp;lt;30s from file write to Snowflake availability)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Medium (requires FPolicy configuration + Lambda function)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best for&lt;/strong&gt;: Near-real-time ingestion requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FPolicy throughput note&lt;/strong&gt;: FPolicy introduces minimal latency on the NFS/SMB I/O path (typically &amp;lt;1ms per operation for passthrough mode). However, under high-frequency file write workloads (thousands of files/second), validate throughput impact on the FSx for ONTAP file system before production deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Option 2: Snowflake Task + COPY INTO (Simple)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create a task that runs COPY INTO every 5 minutes&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="n"&gt;TASK&lt;/span&gt; &lt;span class="n"&gt;fsxn_ingest_task&lt;/span&gt;
  &lt;span class="n"&gt;WAREHOUSE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;COMPUTE_WH&lt;/span&gt;
  &lt;span class="n"&gt;SCHEDULE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'5 MINUTE'&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="k"&gt;COPY&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;target_table&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fsxn_stage_with_arn&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;incoming&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
  &lt;span class="n"&gt;FILE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'.*[.]parquet'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="n"&gt;TASK&lt;/span&gt; &lt;span class="n"&gt;fsxn_ingest_task&lt;/span&gt; &lt;span class="n"&gt;RESUME&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Minutes (configurable schedule interval)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Low (pure Snowflake SQL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best for&lt;/strong&gt;: Batch ingestion where minutes-level latency is acceptable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 3: Snowpipe REST API (Manual Trigger)
&lt;/h3&gt;

&lt;p&gt;Applications call the Snowpipe REST API with a file list when new files are known:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Seconds (triggered by application)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Low (API call from any application)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best for&lt;/strong&gt;: Application-controlled ingestion workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Snowpipe / COPY INTO Supported Formats
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Snowpipe&lt;/th&gt;
&lt;th&gt;COPY INTO&lt;/th&gt;
&lt;th&gt;External Table&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CSV&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Delimiter, header, encoding options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Nested, semi-structured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parquet&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Column pruning, predicate pushdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avro&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Schema evolution supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ORC&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XML&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Native support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Stop Criteria
&lt;/h2&gt;

&lt;p&gt;Stop the Snowflake direct-access PoC when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SELECT from stage fails with AccessDenied &lt;strong&gt;after&lt;/strong&gt; &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; is configured and IAM/AP/FSx permissions are proven correct&lt;/li&gt;
&lt;li&gt;The workload requires Iceberg Table write-back (conditional writes not supported on FSx S3 AP)&lt;/li&gt;
&lt;li&gt;Data owner does not approve the access path&lt;/li&gt;
&lt;li&gt;ReadTimeout occurs (check SVM DNS/AD configuration — see &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/blob/main/docs/en/fsxn-s3ap-networking.md#7-svm-dnsad-configuration-and-s3-ap-availability" rel="noopener noreferrer"&gt;Networking Troubleshooting&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Regulated Workload Checklist
&lt;/h2&gt;

&lt;p&gt;Before using Snowflake + FSx S3 AP for regulated data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Confirm the S3 Access Point file-system user identity and least-privilege permissions&lt;/li&gt;
&lt;li&gt;[ ] Confirm Snowflake role privileges for stage, external table, and tag access&lt;/li&gt;
&lt;li&gt;[ ] Define whether users may generate presigned or scoped URLs (prefer &lt;code&gt;BUILD_SCOPED_FILE_URL&lt;/code&gt; for governed access)&lt;/li&gt;
&lt;li&gt;[ ] Record derived data locations if COPY INTO loads data into Snowflake tables&lt;/li&gt;
&lt;li&gt;[ ] Define manual refresh schedule and evidence retention&lt;/li&gt;
&lt;li&gt;[ ] Store approval owner, review date, and expiration date&lt;/li&gt;
&lt;li&gt;[ ] Validate that &lt;code&gt;GET_PRESIGNED_URL&lt;/code&gt; is not used as a bypass for query-level governance&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;If Vision AI is required&lt;/strong&gt;: Approve COPY FILES to internal stage (data moves to Snowflake-managed storage)&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;If Cross-Region Inference is enabled&lt;/strong&gt;: Verify that image/document data may be processed in US/EU regions&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;If Cortex Search is used&lt;/strong&gt;: Approve COPY INTO (data moves to Snowflake storage) AND Cortex Search Service index creation (data residency changes twice — once for table load, once for search index). Cortex Search Service index is stored in the Snowflake account region.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Store the checklist result with an approval ID, owner, review date, expiration date, and evidence location so the PoC decision can be audited later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Cross-Region Inference — Data Residency Warning
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;CORTEX_ENABLED_CROSS_REGION = 'ANY_REGION'&lt;/code&gt; is set, Cortex AI functions may route data to model endpoints in other AWS regions (US, EU) for processing. For regulated workloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt;: Does your compliance framework allow data processing outside the home region?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alternatives&lt;/strong&gt;: Use &lt;code&gt;AWS_US&lt;/code&gt; or &lt;code&gt;AWS_EU&lt;/code&gt; instead of &lt;code&gt;ANY_REGION&lt;/code&gt; to limit routing scope&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mitigation&lt;/strong&gt;: Process only non-regulated images via Vision AI; keep PHI/PII in text-only Cortex functions (which run in-region)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: Record which Cross-Region setting is used and which data types are processed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Compliance Framework Mapping
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Recommended Pattern&lt;/th&gt;
&lt;th&gt;Key Controls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;HIPAA&lt;/strong&gt; (PHI)&lt;/td&gt;
&lt;td&gt;External Table + SnapLock + FPolicy audit&lt;/td&gt;
&lt;td&gt;Data never leaves FSx; file access audited; admin cannot delete during retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;SOX&lt;/strong&gt; (Financial)&lt;/td&gt;
&lt;td&gt;COPY INTO + Time Travel + audit trail&lt;/td&gt;
&lt;td&gt;Full change history; point-in-time queries for audit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;GDPR&lt;/strong&gt; (PII)&lt;/td&gt;
&lt;td&gt;External Table + Row Access Policy + Tag-based Masking&lt;/td&gt;
&lt;td&gt;Data minimization at query time; PII masked for non-authorized roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;FINRA&lt;/strong&gt; (Records)&lt;/td&gt;
&lt;td&gt;External Table + SnapLock Compliance&lt;/td&gt;
&lt;td&gt;Non-erasable, non-writable records for retention period&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Approval Evidence Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;approval_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FSXN-SF-POC-001"&lt;/span&gt;
&lt;span class="na"&gt;data_owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;name/group&amp;gt;"&lt;/span&gt;
&lt;span class="na"&gt;security_owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;name/group&amp;gt;"&lt;/span&gt;
&lt;span class="na"&gt;platform_owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;name/group&amp;gt;"&lt;/span&gt;
&lt;span class="na"&gt;allowed_prefixes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;ap-alias&amp;gt;/sensor-data/"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;ap-alias&amp;gt;/bronze/"&lt;/span&gt;
&lt;span class="na"&gt;allowed_operations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LIST&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SELECT (External Table)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;COPY INTO (load only)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Directory Table&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;BUILD_SCOPED_FILE_URL&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Cortex text functions (SUMMARIZE, TRANSLATE, SENTIMENT)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;COPY FILES to internal stage (for Vision AI only)&lt;/span&gt;
&lt;span class="na"&gt;disallowed_operations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET_PRESIGNED_URL for regulated data&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;COPY INTO unload (write-back)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Cortex LLM on PHI/PII without human review&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Cross-Region Inference on regulated images (unless approved)&lt;/span&gt;
&lt;span class="na"&gt;cross_region_inference&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ANY_REGION"&lt;/span&gt;  &lt;span class="c1"&gt;# or "DISABLED" for regulated data&lt;/span&gt;
&lt;span class="na"&gt;review_date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YYYY-MM-DD&amp;gt;"&lt;/span&gt;
&lt;span class="na"&gt;expiration_date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YYYY-MM-DD&amp;gt;"&lt;/span&gt;
&lt;span class="na"&gt;evidence_location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verification-pack/snowflake/evidence/&amp;lt;date&amp;gt;/evidence-record.yaml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;COPY INTO unload (write-back to FSx S3 AP) was not validated in this article. Although FSx S3 AP supports PutObject, Snowflake unload behavior should be tested separately before positioning write-back as supported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data residency note&lt;/strong&gt;: COPY INTO (load) and COPY FILES change the data residency model — source files remain on FSx, but a derived copy is created in Snowflake-managed storage. Cross-Region Inference may further route data to other regions. Treat loaded tables and staged files as derived regulated data and apply retention, classification, and deletion controls separately.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Troubleshooting Playbook
&lt;/h2&gt;

&lt;p&gt;When Snowflake access to FSx for ONTAP S3 AP fails, isolate one layer at a time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stage configuration&lt;/strong&gt; — Is &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; set? Without it, GetObject will fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; — Does the Storage Integration role have &lt;code&gt;s3:GetObject&lt;/code&gt;, &lt;code&gt;s3:ListBucket&lt;/code&gt; on the S3 AP ARN?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 AP policy&lt;/strong&gt; — Does the Access Point resource policy allow the Snowflake IAM user ARN?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FSx file system&lt;/strong&gt; — Is the file system user (e.g., root) permitted to read the target files?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network&lt;/strong&gt; — Is the AP internet-origin? (Snowflake SaaS cannot use VPC-origin APs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational&lt;/strong&gt; — Does &lt;code&gt;vserver services dns check&lt;/code&gt; show healthy DNS? (ReadTimeout = DNS/AD issue)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Known Failure Signatures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely layer&lt;/th&gt;
&lt;th&gt;Next step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LIST works, SELECT fails with "access denied"&lt;/td&gt;
&lt;td&gt;Missing &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add ARN parameter to stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LIST and SELECT both fail with "access denied"&lt;/td&gt;
&lt;td&gt;IAM role or S3 AP policy&lt;/td&gt;
&lt;td&gt;Check DESCRIBE INTEGRATION, verify trust policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReadTimeout (no response)&lt;/td&gt;
&lt;td&gt;SVM DNS/AD or FSx backend&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;vserver services dns check&lt;/code&gt;; verify S3 AP lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stage creation fails&lt;/td&gt;
&lt;td&gt;Storage Integration config&lt;/td&gt;
&lt;td&gt;Verify STORAGE_ALLOWED_LOCATIONS includes the AP alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Table creation fails&lt;/td&gt;
&lt;td&gt;Stage or file format issue&lt;/td&gt;
&lt;td&gt;Verify LIST works first, then check FILE_FORMAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COPY INTO fails&lt;/td&gt;
&lt;td&gt;File format mismatch or permissions&lt;/td&gt;
&lt;td&gt;Verify SELECT works first&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What This Article Does Not Conclude
&lt;/h2&gt;

&lt;p&gt;This article does not conclude that Snowflake + FSx for ONTAP S3 AP is production-certified for all workloads. It documents the behavior observed in one validated environment and identifies the configuration required for successful integration.&lt;/p&gt;

&lt;p&gt;Specifically, this article does not validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snowpipe auto-ingest (requires S3 Event Notifications)&lt;/li&gt;
&lt;li&gt;Iceberg Table write-back (requires conditional writes)&lt;/li&gt;
&lt;li&gt;COPY INTO unload / write-back to FSx S3 AP&lt;/li&gt;
&lt;li&gt;Snowpark File Access (SnowflakeFile.open) for binary processing&lt;/li&gt;
&lt;li&gt;Performance at scale (large file counts, concurrent queries, large directory refreshes, or mixed NFS/SMB/S3 workload contention on the FSx file system)&lt;/li&gt;
&lt;li&gt;Private connectivity (PrivateLink) path&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Operational Note: ReadTimeout vs AccessDenied
&lt;/h2&gt;

&lt;p&gt;During this validation series, all S3 APs on one SVM became unresponsive for 7+ days due to orphaned DNS/AD configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important distinction:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ReadTimeout&lt;/strong&gt; (no response) → Check SVM DNS/AD configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AccessDenied&lt;/strong&gt; (immediate error) → Check &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; stage parameter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/blob/main/docs/en/fsxn-s3ap-networking.md#7-svm-dnsad-configuration-and-s3-ap-availability" rel="noopener noreferrer"&gt;FSx S3 AP Networking — DNS/AD Troubleshooting&lt;/a&gt; for details.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Platform documentation holds the answer
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; parameter exists in &lt;a href="https://docs.snowflake.com/en/sql-reference/sql/create-stage" rel="noopener noreferrer"&gt;Snowflake's CREATE STAGE documentation&lt;/a&gt;. The initial "no workaround" conclusion was premature — always check platform docs for S3 AP-specific parameters before concluding incompatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The same pattern recurs across platforms
&lt;/h3&gt;

&lt;p&gt;Both Snowflake (&lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;) and Databricks (&lt;code&gt;access_point&lt;/code&gt; field) require explicit S3 AP ARN configuration. This appears to be a recurring integration pattern: platforms that generate restrictive session policies need an explicit parameter so the generated policy includes the regional access point ARN format.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. LIST ≠ READ (but the fix is simple)
&lt;/h3&gt;

&lt;p&gt;The partial success (LIST works, SELECT doesn't) is confusing but has a clear fix. The root cause is that &lt;code&gt;ListBucket&lt;/code&gt; uses bucket-level ARN matching while &lt;code&gt;GetObject&lt;/code&gt; requires object-level ARN matching — and the AP ARN parameter resolves both.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. SVM DNS/AD configuration can silently break S3 AP
&lt;/h3&gt;

&lt;p&gt;ReadTimeout (not AccessDenied) indicates an operational issue, not a session policy issue. Check &lt;code&gt;vserver services dns check&lt;/code&gt; on the SVM.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Pre-signed URLs work but are not a governed path
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GET_PRESIGNED_URL()&lt;/code&gt; generates valid URLs for FSx S3 AP objects. However, this bypasses Snowflake query governance and should not be used as a production workaround for regulated workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to Tell Stakeholders
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Current recommendation (8 out of 10 tested AI functions validated on FSx data):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Snowflake External Stage with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;&lt;/strong&gt; for governed read access to FSx for ONTAP data&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;External Tables&lt;/strong&gt; for governed schema abstraction with tags and access policies&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;COPY INTO&lt;/strong&gt; when data needs to be loaded into Snowflake for ML/AI processing&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Directory Table&lt;/strong&gt; for unstructured data cataloging&lt;/li&gt;
&lt;li&gt;Do not rely on &lt;strong&gt;Snowpipe AUTO_REFRESH&lt;/strong&gt; — use scheduled &lt;code&gt;ALTER STAGE REFRESH&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Do not position &lt;strong&gt;Iceberg write-back&lt;/strong&gt; on FSx S3 AP as supported&lt;/li&gt;
&lt;li&gt;For end-to-end RAG, use &lt;strong&gt;Cortex Search&lt;/strong&gt; (validated: External Table → COPY INTO → Cortex Search Service, 198ms query) or &lt;strong&gt;Bedrock Knowledge Bases&lt;/strong&gt; (AWS-documented path, no copy needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This validation should be used to guide architecture selection and stage configuration, not as a production certification.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Athena — Query NAS Data In Place&lt;/a&gt; (validated read-oriented SQL path)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d"&gt;Databricks — A Layer-by-Layer Validation of Observed Boundaries&lt;/a&gt; (session policy + &lt;code&gt;access_point&lt;/code&gt; field)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: DuckDB Lambda — Serverless analytics at $0.00001/query (for teams that need lightweight, zero-idle-cost SQL without warehouse management)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: EMR Spark — Read-Write ETL Pipeline (for teams that need distributed Spark processing with write-back to S3 for downstream lakehouse consumption)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.snowflake.com/en/sql-reference/sql/create-stage" rel="noopener noreferrer"&gt;Snowflake CREATE STAGE — AWS_ACCESS_POINT_ARN parameter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/accessing-data-via-s3-access-points.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 Access Points documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/access-points-for-fsxn-object-api-support.html" rel="noopener noreferrer"&gt;FSx S3 AP API compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-manage-access-fsxn.html" rel="noopener noreferrer"&gt;FSx S3 AP dual-layer authorization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Key achievement&lt;/strong&gt;: This validation established that Snowflake + FSx for ONTAP S3 AP provides a governed, AI-ready read path — 8 out of 10 tested Cortex AI functions work on NAS data, External Tables enable full governance (tags, masking, row policies), and Cortex Search delivers 198ms semantic search over NAS-originated documents. This is the most complete governed integration path validated in this series.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article documents observed behavior in one validated environment (Snowflake Standard edition, AWS ap-northeast-1, May 2026). Platform behavior may change with future updates.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article is an independent validation report and does not represent Snowflake, AWS, or NetApp official guidance. Product behavior, support status, and platform capabilities may change. Always validate in your own environment and consult vendor documentation and support channels.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>snowflake</category>
      <category>amazonfsxfornetappontap</category>
      <category>lakehouse</category>
    </item>
    <item>
      <title>Databricks and FSx for ONTAP S3 Access Points — A Layer-by-Layer Validation of Observed Boundaries</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Sun, 24 May 2026 11:17:38 +0000</pubDate>
      <link>https://forem.com/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d</link>
      <guid>https://forem.com/aws-builders/databricks-and-fsx-for-ontap-s3-access-points-a-layer-by-layer-validation-of-observed-boundaries-p4d</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Connecting Databricks to FSx for ONTAP S3 Access Points is significantly harder than Athena (&lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1&lt;/a&gt;). After testing every approach I could find — Unity Catalog External Locations, NFS mounts, Instance Profiles, multiple VPC configurations — here is what I found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unity Catalog's session policy&lt;/strong&gt; initially blocked the FSx for ONTAP S3 AP ARN pattern → 403&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setting the &lt;code&gt;access_point&lt;/code&gt; field&lt;/strong&gt; on the External Location partially resolves the session policy: explicit-path file read succeeds, but UC table creation, subdirectory listing, and write operations remain blocked — meaning UC governance features (lineage, tags, fine-grained access) cannot yet be applied&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFS kernel mount&lt;/strong&gt; is blocked by seccomp by design (confirmed by Databricks Support)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance Profile + boto3&lt;/strong&gt; works for direct S3 AP access (bypassing Unity Catalog)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spark read with explicit file path&lt;/strong&gt; works under UC governance — 1000 rows of sensor data readable with full schema inference, proving data access is possible even if table creation is blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick Decision Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read-only SQL analytics on NAS data&lt;/strong&gt; → Use Athena (Part 1) or Snowflake External Table (&lt;a href="https://dev.to/aws-builders/snowflake-and-fsx-for-ontap-s3-access-points-from-access-denied-to-working-external-tables-9k8"&gt;Part 3&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governed Databricks lakehouse on NAS data&lt;/strong&gt; → Stage via FPolicy → Lambda → S3 → Auto Loader → UC Managed Table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploratory PoC (time-limited)&lt;/strong&gt; → Instance Profile + boto3 with compensating controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article is a layer-by-layer validation of observed integration boundaries between Databricks and FSx for ONTAP S3 Access Points. It is not an argument against Databricks. Databricks remains a strong platform for lakehouse, ML, and production Delta workloads. This article focuses narrowly on one integration boundary: direct access from Databricks to FSx for ONTAP S3 Access Points.&lt;/p&gt;

&lt;p&gt;This article documents the full troubleshooting journey, including the strace analysis that identified the root cause of NFS mount failures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article documents observed behavior in one validated environment. It should not be interpreted as a general compatibility statement for all Databricks configurations or future platform versions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to reproduce this validation, the repository's &lt;code&gt;integrations/databricks/&lt;/code&gt; directory contains environment setup notes, and &lt;code&gt;verification-pack/&lt;/code&gt; contains test templates and evidence recording formats. The verification pack is intentionally template-first by design, so validation runs can produce consistent, reviewable evidence across environments. Actual result files will be added as validation runs are completed.&lt;/p&gt;

&lt;p&gt;This article also includes a &lt;strong&gt;Snowflake ↔ Databricks concept mapping table&lt;/strong&gt; (showing which capabilities work on each platform) and an &lt;strong&gt;AI Readiness Score&lt;/strong&gt; to help teams quantitatively compare pattern options for FSx for ONTAP integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Read This Article
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This article is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A reproduction-focused validation report&lt;/li&gt;
&lt;li&gt;Evidence from one environment (DBR 17.3 LTS, ap-northeast-1)&lt;/li&gt;
&lt;li&gt;A starting point for vendor confirmation and architecture discussion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This article is not:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A general compatibility statement&lt;/li&gt;
&lt;li&gt;A production certification&lt;/li&gt;
&lt;li&gt;A statement on behalf of Databricks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Read by role:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Databricks admin&lt;/strong&gt;: Unity Catalog External Location → Governance Impact Summary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage engineer&lt;/strong&gt;: NFS Mount investigation → Evidence Matrix&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data engineer&lt;/strong&gt;: Instance Profile + boto3 → Next Validation Metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner / SA&lt;/strong&gt;: Decision Matrix → Discovery Questions → Partner Conversation Guide&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opening a support case&lt;/strong&gt;: Databricks Support Case Packet&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisite Concepts
&lt;/h2&gt;

&lt;p&gt;Before reading this article, it helps to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unity Catalog Storage Credential&lt;/strong&gt; — an object that stores a reference to a cloud IAM role for accessing external storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unity Catalog External Location&lt;/strong&gt; — maps a cloud storage path to a storage credential for governed access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance Profile on AWS&lt;/strong&gt; — an IAM role attached to an EC2 instance, providing credentials via IMDS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databricks-managed VPC vs Customer-managed VPC&lt;/strong&gt; — whether Databricks or the customer controls the workspace network&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster access modes&lt;/strong&gt; — Standard (shared, multi-user with UC governance) and Dedicated (single-user with sudo access). Unity Catalog requires &lt;a href="https://docs.databricks.com/aws/en/compute/standard-overview" rel="noopener noreferrer"&gt;standard or dedicated access mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Access Point ARN vs S3 bucket ARN&lt;/strong&gt; — S3 AP uses &lt;code&gt;arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;name&amp;gt;&lt;/code&gt;, not &lt;code&gt;arn:aws:s3:::&amp;lt;bucket&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Driver vs executor behavior in Spark&lt;/strong&gt; — the driver orchestrates; executors run distributed tasks. Credentials and network paths may differ between them&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Concepts: Databricks Storage &amp;amp; Ingestion Architecture
&lt;/h2&gt;

&lt;p&gt;Understanding Databricks' storage and ingestion concepts is essential for evaluating FSx for ONTAP S3 AP integration boundaries.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Partner Quick Reference&lt;/strong&gt;: If your customer asks "Can Databricks read our NAS data via S3 Access Points?" — the answer is "partially, with limitations." File-level read works under UC governance, but table creation and directory listing are blocked. For governed analytics on NAS data today, recommend Snowflake External Table or Athena. For Databricks-specific workloads, recommend staged ingestion to S3 → UC Managed Table (see Recommended Architecture Pattern). If the customer already uses Databricks, the FPolicy → Lambda → S3 → Auto Loader pattern preserves full UC governance on ingested data. See Discovery Questions for customer qualification.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Storage Credential → External Location → External Table/Volume
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Storage Credential (IAM Role ARN + External ID)
    │
    └── External Location (cloud storage path + credential + access_point field)
            │
            ├── External Table (tabular data: Parquet, Delta, Iceberg)
            └── External Volume (non-tabular: images, documents, audio)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;FSx S3 AP Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Credential&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;IAM Role that Databricks assumes to access cloud storage. During AssumeRole, Databricks generates a session policy that restricts what the assumed session can do — even if the IAM role itself has broader permissions.&lt;/td&gt;
&lt;td&gt;✅ Created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Maps S3 path to a Storage Credential; defines access boundary&lt;/td&gt;
&lt;td&gt;✅ Created (with &lt;code&gt;access_point&lt;/code&gt; field)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External Table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UC-governed table whose data resides in External Location&lt;/td&gt;
&lt;td&gt;❌ CREATE TABLE blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External Volume&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UC-governed volume for unstructured files in External Location&lt;/td&gt;
&lt;td&gt;❌ Blocked (same session policy issue)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;External Volume&lt;/strong&gt; is the Databricks equivalent of Snowflake's Directory Table — it provides governed access to non-tabular files (images, documents, audio, video). Since External Volume requires External Location creation with full subdirectory access, it is currently blocked by the same session policy limitation that blocks External Table creation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto Loader (Incremental Ingestion)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.databricks.com/ingestion/auto-loader/index.html" rel="noopener noreferrer"&gt;Auto Loader&lt;/a&gt; is Databricks' equivalent of Snowflake's Snowpipe — it incrementally processes new files as they arrive in cloud storage.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;FSx S3 AP Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Directory Listing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Periodically lists directory to find new files&lt;/td&gt;
&lt;td&gt;⚠️ Requires External Location (blocked)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;File Notification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uses S3 Event Notifications + SQS for real-time detection&lt;/td&gt;
&lt;td&gt;❌ Not possible (FSx S3 AP doesn't support S3 Events)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Auto Loader supported formats&lt;/strong&gt; (8 formats): JSON, CSV, Parquet, Avro, ORC, XML, TEXT, BINARYFILE.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FSx S3 AP latency context&lt;/strong&gt;: Even if Directory Listing mode were unblocked, FSx S3 AP ListObjectsV2 latency is significantly higher than native S3 (tens of seconds to minutes for large directories). This would impact Auto Loader polling intervals and new-file detection speed. Plan for minutes-level detection latency, not seconds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Concept Mapping: Snowflake ↔ Databricks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Snowflake Concept&lt;/th&gt;
&lt;th&gt;Databricks Equivalent&lt;/th&gt;
&lt;th&gt;FSx S3 AP (Snowflake)&lt;/th&gt;
&lt;th&gt;FSx S3 AP (Databricks)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage Integration&lt;/td&gt;
&lt;td&gt;Storage Credential&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Stage&lt;/td&gt;
&lt;td&gt;External Location&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (partial)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Table&lt;/td&gt;
&lt;td&gt;External Table&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Directory Table&lt;/td&gt;
&lt;td&gt;External Volume&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snowpipe&lt;/td&gt;
&lt;td&gt;Auto Loader&lt;/td&gt;
&lt;td&gt;⚠️ (no S3 Events)&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COPY INTO&lt;/td&gt;
&lt;td&gt;COPY INTO / Auto Loader&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;access_point&lt;/code&gt; field&lt;/td&gt;
&lt;td&gt;✅ (resolves all)&lt;/td&gt;
&lt;td&gt;⚠️ (partial resolution)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cortex Search (RAG)&lt;/td&gt;
&lt;td&gt;Mosaic AI / MLflow&lt;/td&gt;
&lt;td&gt;✅ (via COPY INTO)&lt;/td&gt;
&lt;td&gt;⚠️ (boto3 + external)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Data Ingestion Alternatives for FSx for ONTAP (When Auto Loader Is Blocked)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Throughput constraint&lt;/strong&gt;: All S3 AP operations are bounded by the FSx for ONTAP file system's provisioned throughput capacity (e.g., 128 MB/s in this validation environment). This throughput is shared with NFS/SMB workloads on the same file system. Plan ingestion windows and concurrent access accordingly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since Auto Loader requires External Location (currently blocked on FSx S3 AP), use these alternatives:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FPolicy → Lambda → S3 → Auto Loader&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FPolicy detects file changes → Lambda copies to S3 → Auto Loader ingests&lt;/td&gt;
&lt;td&gt;Seconds&lt;/td&gt;
&lt;td&gt;✅ Full UC (on S3 copy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Glue ETL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Glue job reads from FSx S3 AP → writes to S3/Delta&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;AWS-side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMR Serverless&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spark job reads from FSx S3 AP → writes to S3/Delta&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;AWS-side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS DataSync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scheduled sync from FSx NFS → S3 bucket&lt;/td&gt;
&lt;td&gt;Minutes-Hours&lt;/td&gt;
&lt;td&gt;AWS-side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SnapMirror to S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ONTAP-native replication to S3 bucket&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;ONTAP-side&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SnapMirror to S3 caveat&lt;/strong&gt;: Object metadata in SnapMirror S3 targets differs from NFS file metadata. Validate schema compatibility and file naming conventions before using SnapMirror S3 as an ingestion path for analytics engines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Recommended production pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSx for ONTAP ──FPolicy──▶ Lambda ──▶ S3 Bucket ──▶ Auto Loader ──▶ Delta Table (UC governed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Iceberg interoperability note&lt;/strong&gt;: Once data is in UC as a managed Delta or Iceberg table, external engines can access it via &lt;a href="https://docs.databricks.com/aws/en/external-access/" rel="noopener noreferrer"&gt;UC's Iceberg REST Catalog&lt;/a&gt; — enabling Athena, EMR, and Trino to query the same governed table without data duplication. This makes the DataSync → S3 → UC path a hub for multi-engine access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AI Readiness Score
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Governance&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;th&gt;AI Capability&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Operational Simplicity&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Athena + FSx S3 AP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★☆☆☆☆ (SQL only)&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Snowflake External Table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆ (Cortex AI)&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Staged to S3 → UC Table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★★ (full Mosaic AI)&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;boto3 PoC (Databricks)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★☆☆☆☆&lt;/td&gt;
&lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;td&gt;★★★☆☆ (driver-only)&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.8&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bedrock KB + FSx S3 AP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★☆ (RAG)&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;★★★★☆&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Governance&lt;/strong&gt;: UC lineage, tags, masking, row filters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Query latency, distributed processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Capability&lt;/strong&gt;: Breadth of AI/ML functions available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: Storage efficiency, compute cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational Simplicity&lt;/strong&gt;: Setup, maintenance, pipeline complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scoring methodology&lt;/strong&gt;: Each dimension rated by the author based on validated evidence in this article series. This is not an official AWS assessment or certification. Scores reflect observed capabilities in one test environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance note&lt;/strong&gt;: Performance scores reflect relative comparison within FSx S3 AP access patterns, not comparison with native S3 bucket performance. All patterns accessing FSx S3 AP have higher latency than equivalent native S3 operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to use this score&lt;/strong&gt;: Use Overall score as a starting point for pattern selection. Scores ≥ 4.0 indicate strong fit for governed production workloads. Scores 3.5–3.9 indicate viable paths with trade-offs. Scores &amp;lt; 3.0 indicate PoC-only paths requiring compensating controls.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;When to choose which:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;strong&gt;Snowflake External Table&lt;/strong&gt; (4.0) when governed AI on NAS data without copying is the priority&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Staged to S3 → UC Table&lt;/strong&gt; (3.8) when maximum Databricks performance and full Mosaic AI are required (accepts data duplication cost)&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Bedrock KB&lt;/strong&gt; (3.8) when AWS-native RAG with zero-copy on FSx is the primary requirement&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;boto3 PoC&lt;/strong&gt; (2.8) only for time-limited exploration with explicit approval; with compensating controls (see Compensating Controls section), governance risk can be partially mitigated for PoC scope. Post-expiration actions must be defined: terminate cluster, remove instance profile, archive evidence.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;Process unstructured data (images, documents, audio) stored on FSx for ONTAP from Databricks — without copying data to S3. FSx for ONTAP S3 Access Points should make this possible by exposing NFS/SMB file data via S3 API.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1&lt;/a&gt;, Athena worked cleanly in my validation using the &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;official AWS tutorial pattern&lt;/a&gt;. Databricks, however, has multiple security layers that interact with S3 AP in unexpected ways.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test Environment
&lt;/h2&gt;

&lt;p&gt;I tested across two workspace configurations:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Runtime scope&lt;/strong&gt;: Only DBR 17.3 LTS (Spark 4.0.0) was tested. This article does not compare DBR 16.x, 18.x, ML runtimes, GPU runtimes, or serverless compute. Runtime-level behavior may differ across versions and compute types. This article does not compare behavior across DBR versions or access modes beyond those listed in the test environment.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────┐
│ Workspace 1: Databricks-managed VPC                                 │
│ - VPC created and managed by Databricks                             │
│ - Limited network control                                           │
│ - VPC Peering to FSx for ONTAP VPC                                  │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ Workspace 2: Customer-managed VPC (same VPC as FSx for ONTAP)       │
│ - Full network control                                              │
│ - Direct connectivity to FSx for ONTAP (no peering needed)          │
│ - NAT Gateway for Databricks control plane                          │
└─────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cluster modes tested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard (Shared Access)&lt;/li&gt;
&lt;li&gt;Dedicated (Single User) — provides sudo/root access&lt;/li&gt;
&lt;li&gt;Dedicated with Instance Profile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All tests used DBR 17.3 LTS (Spark 4.0.0), ap-northeast-1.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 1: Unity Catalog External Location
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;The Databricks-governed path for S3 data access is to create a Storage Credential and External Location. I tested whether the same pattern could work with an FSx for ONTAP S3 Access Point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# What I expected to work
&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dbutils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;FSx-S3-AP-alias&amp;gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Error
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AccessDenied: User: arn:aws:sts::&amp;lt;ACCOUNT&amp;gt;:assumed-role/databricks-...-cross-account-role/
  databricks-unity-catalog-credential-&amp;lt;WORKSPACE_ID&amp;gt;
is not authorized to perform: s3:ListBucket on resource:
  "arn:aws:s3:&amp;lt;REGION&amp;gt;:&amp;lt;ACCOUNT&amp;gt;:accesspoint/&amp;lt;AP_NAME&amp;gt;"
because no session policy allows the s3:ListBucket action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Observed Boundary
&lt;/h3&gt;

&lt;p&gt;Unity Catalog applies a &lt;strong&gt;session policy&lt;/strong&gt; when it calls &lt;code&gt;AssumeRole&lt;/code&gt;. This session policy acts as a permissions boundary — even if the IAM role has &lt;code&gt;s3:*&lt;/code&gt; on &lt;code&gt;*&lt;/code&gt;, the session policy restricts what the assumed session can do.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The evidence narrows the failure domain, but does not identify Databricks internal implementation details.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this validation, the generated session policy behavior allowed access to a standard S3 bucket path but did not allow the FSx for ONTAP S3 Access Point ARN pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:s3:::bucket-name       ✅ Allowed
arn:aws:s3:::bucket-name/*     ✅ Allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But FSx for ONTAP S3 AP uses a different ARN format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;name&amp;gt;    ❌ Not in session policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Proof
&lt;/h3&gt;

&lt;p&gt;The same IAM role works fine for regular S3 buckets through Unity Catalog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This works — regular S3 bucket
&lt;/span&gt;&lt;span class="n"&gt;dbutils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://my-workspace-storage-bucket/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# SUCCESS
&lt;/span&gt;
&lt;span class="c1"&gt;# This fails — FSx for ONTAP S3 Access Point
&lt;/span&gt;&lt;span class="n"&gt;dbutils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://&amp;lt;FSx-S3-AP-alias&amp;gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# AccessDenied: no session policy allows...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Status
&lt;/h3&gt;

&lt;p&gt;In my initial validation, this behaved like a platform boundary in Unity Catalog's generated session policy. I opened a support case to confirm whether S3 Access Point ARN patterns can be supported for external locations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (&lt;code&gt;access_point&lt;/code&gt; field not set)&lt;/strong&gt; — Unity Catalog session policy blocks all S3 AP operations:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh05qqisia04lu739xp1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh05qqisia04lu739xp1.png" alt="Session policy error before access_point field — UNAUTHORIZED_ACCESS on dbutils.fs.ls" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Without the &lt;code&gt;access_point&lt;/code&gt; field, &lt;code&gt;dbutils.fs.ls&lt;/code&gt; on the S3 AP alias returns UNAUTHORIZED_ACCESS. The session policy only allows standard S3 bucket ARNs.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Update (2026-05-24): &lt;code&gt;access_point&lt;/code&gt; Field Resolves Session Policy
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Critical Update (2026-05-26)&lt;/strong&gt;: Databricks Support subsequently confirmed that the &lt;code&gt;access_point&lt;/code&gt; field was never released as a generally available feature and has been removed from documentation. The partial success described below is "a side effect of incomplete internal handling, not a supported code path." Unity Catalog External Locations do not currently support S3 Access Points. See the full support confirmation at the end of this section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Databricks Support (May 2026) confirmed that Unity Catalog External Locations support an &lt;code&gt;access_point&lt;/code&gt; field. Setting this field includes the S3 AP ARN in the generated session policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration that works:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;External Location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3://&amp;lt;FSx-S3-AP-alias&amp;gt;/&lt;/span&gt;
  &lt;span class="na"&gt;Credential&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;storage-credential-name&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;access_point&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;ap-name&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;API call to set the field:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PATCH &lt;span class="se"&gt;\&lt;/span&gt;
  https://&amp;lt;workspace&amp;gt;/api/2.1/unity-catalog/external-locations/&amp;lt;location-name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;token&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"access_point": "arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;ap-name&amp;gt;"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What now works under UC governance:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dbutils.fs.ls("s3://&amp;lt;alias&amp;gt;/")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Top-level listing (287 items)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dbutils.fs.head("s3://&amp;lt;alias&amp;gt;/file.txt")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Read file content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.read.text("s3://&amp;lt;alias&amp;gt;/file.txt")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Spark read with explicit file path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.read.csv("s3://&amp;lt;alias&amp;gt;/path/to/file.csv")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;1000 rows, schema inferred&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;After (&lt;code&gt;access_point&lt;/code&gt; field set)&lt;/strong&gt; — Top-level listing succeeds, 287 items visible:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1q5n5m33qy9vg8zzijl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1q5n5m33qy9vg8zzijl.png" alt="dbutils.fs.ls success — 287 items listed from FSx for ONTAP S3 AP" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;With the &lt;code&gt;access_point&lt;/code&gt; field configured, &lt;code&gt;dbutils.fs.ls&lt;/code&gt; at the top level returns 287 items from the FSx for ONTAP volume.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sensor data read via Spark&lt;/strong&gt; — 1000 rows with schema inference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flydan8xi3arhr2jxb8r6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flydan8xi3arhr2jxb8r6.png" alt="Spark DataFrame reading sensor CSV from FSx for ONTAP S3 AP — 1000 rows" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;code&gt;spark.read.csv&lt;/code&gt; with explicit file path successfully reads 1000 sensor readings with full schema inference (timestamp, machine_id, temperature_c, vibration_mm_s, pressure_bar, rpm, status, location).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What still does NOT work:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dbutils.fs.ls("s3://&amp;lt;alias&amp;gt;/subdir/")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;AccessDenied on getFileStatus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;spark.read.load("s3://&amp;lt;alias&amp;gt;/subdir/")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Forbidden (directory-level access)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE LOCATION 's3://&amp;lt;alias&amp;gt;/...'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;UC_CLOUD_STORAGE_ACCESS_FAILURE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;dbutils.fs.cp&lt;/code&gt; (PutObject)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;AccessDenied&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Remaining blockers&lt;/strong&gt; — Subdirectory listing and UC table creation fail:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsr0tsyysbj2v5argvh4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsr0tsyysbj2v5argvh4j.png" alt="Subdirectory ls blocked and CREATE TABLE fails — UC governance cannot be applied" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Subdirectory &lt;code&gt;dbutils.fs.ls&lt;/code&gt; returns UNAUTHORIZED_ACCESS. &lt;code&gt;CREATE TABLE LOCATION&lt;/code&gt; fails with UC_CLOUD_STORAGE_ACCESS_FAILURE. Without a UC table, governance features (lineage, tags, fine-grained access control) cannot be applied.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rkdqqy3hy548yy8s6mb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rkdqqy3hy548yy8s6mb.png" alt="Summary of what works and what doesn't — governance impact" width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Summary: Data is readable but not governable. The critical blocker is &lt;code&gt;CREATE TABLE LOCATION&lt;/code&gt; failure, which prevents Unity Catalog governance from being applied to the data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key pattern&lt;/strong&gt;: File-level read operations succeed (GetObject with explicit key). Directory-level operations (ListObjectsV2 with prefix, HeadObject on prefix) fail for subdirectories. This suggests the session policy scopes ListObjectsV2 to the root prefix only.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Implication&lt;/strong&gt;: Explicit-path file read works, but without UC table creation, Unity Catalog governance features — lineage, fine-grained access control, governance tags, column masking, row filtering — cannot be applied. The data is technically readable through the External Location path but not registerable as a governed UC table. This limits the practical value for production governance use cases until the subdirectory listing and table creation issues are resolved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Requirements for this path:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer-managed VPC workspace (same VPC as FSx for ONTAP)&lt;/li&gt;
&lt;li&gt;External Location with &lt;code&gt;access_point&lt;/code&gt; field set&lt;/li&gt;
&lt;li&gt;Storage Credential IAM role with S3 AP permissions&lt;/li&gt;
&lt;li&gt;NAT Gateway for control plane connectivity&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Approach 2: NFS Mount (Managed VPC)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Idea
&lt;/h3&gt;

&lt;p&gt;If S3 AP doesn't work through Unity Catalog, mount the FSx for ONTAP volume directly via NFS.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;Created VPC Peering between Databricks-managed VPC and FSx for ONTAP VPC. Updated route tables and security groups.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Result
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;timeout &lt;/span&gt;3 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo &amp;gt; /dev/tcp/10.0.3.133/2049'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"REACHABLE"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"NOT REACHABLE"&lt;/span&gt;
&lt;span class="c"&gt;# NOT REACHABLE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;NFS port (TCP 2049) is unreachable&lt;/strong&gt; from Databricks-managed VPC, even with VPC Peering configured. From the customer-controlled routing perspective, route tables and FSx for ONTAP-side security groups were configured to allow NFS. However, cluster-side egress remained governed by the Databricks-managed environment, and NFS egress was not permitted.&lt;/p&gt;
&lt;h3&gt;
  
  
  Lesson
&lt;/h3&gt;

&lt;p&gt;Databricks-managed VPC gives you limited network control. The egress rules on cluster instances are managed by Databricks, not by customer-added security group rules.&lt;/p&gt;


&lt;h2&gt;
  
  
  Approach 3: NFS Mount (Customer-managed VPC)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;Deployed a new workspace in the &lt;strong&gt;same VPC&lt;/strong&gt; as FSx for ONTAP. No peering needed — direct L3 connectivity.&lt;/p&gt;
&lt;h3&gt;
  
  
  Network Verification (All Pass)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"TCP 2049 (NFS):"&lt;/span&gt;
&lt;span class="nb"&gt;timeout &lt;/span&gt;3 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo &amp;gt; /dev/tcp/10.0.3.133/2049'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"REACHABLE"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"TCP 111 (portmapper):"&lt;/span&gt;
&lt;span class="nb"&gt;timeout &lt;/span&gt;3 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo &amp;gt; /dev/tcp/10.0.3.133/111'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"REACHABLE"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"TCP 635 (mountd):"&lt;/span&gt;
&lt;span class="nb"&gt;timeout &lt;/span&gt;3 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo &amp;gt; /dev/tcp/10.0.3.133/635'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"REACHABLE"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TCP 2049 (NFS): REACHABLE ✅
TCP 111 (portmapper): REACHABLE ✅
TCP 635 (mountd): REACHABLE ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The &lt;code&gt;/dev/tcp&lt;/code&gt; test confirms TCP reachability. NFSv3 mountd may use TCP or UDP depending on configuration. The exact transport should be validated with &lt;code&gt;rpcinfo&lt;/code&gt; if needed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  sudo Access (Dedicated Mode)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;sudo whoami&lt;/span&gt;
&lt;span class="c"&gt;# root ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  NFS Client Installation and Export Verification
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nfs-common
showmount &lt;span class="nt"&gt;-e&lt;/span&gt; 10.0.3.133
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Export list for 10.0.3.133:
/vol1 (everyone) ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Everything looks perfect. Network connected, root access available, NFS exports visible. Let's mount:&lt;/p&gt;
&lt;h3&gt;
  
  
  The Mount Attempt
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/fsxn
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;nfsvers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3,nolock 10.0.3.133:/vol1 /mnt/fsxn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mount.nfs: access denied by server while mounting 10.0.3.133:/vol1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Wait, what?&lt;/strong&gt; The server is showing the export to everyone, we have root access, the network is connected... why "access denied by server"?&lt;/p&gt;


&lt;h2&gt;
  
  
  The Investigation: Why NFS Mount Fails
&lt;/h2&gt;

&lt;p&gt;This is where it gets interesting. The error message says "access denied &lt;strong&gt;by server&lt;/strong&gt;" — but is it really the server?&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Verify ONTAP Export Policy
&lt;/h3&gt;

&lt;p&gt;Via ONTAP REST API (accessible from the same cluster):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"clients"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ro_rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rw_rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"superuser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"protocols"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The export policy is &lt;strong&gt;maximally permissive&lt;/strong&gt; — all clients, all protocols, read-write, superuser. ONTAP is not denying access.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: This permissive export policy was used only to eliminate ONTAP export restrictions as a variable during troubleshooting. It is not a production recommendation. For production, restrict: client CIDR, protocol, read/write rule, superuser mapping, and volume/junction path scope.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ONTAP Production Hardening Checklist
&lt;/h3&gt;

&lt;p&gt;For production deployments, harden the ONTAP configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Restrict export policy client CIDR to known analytics subnets only&lt;/li&gt;
&lt;li&gt;[ ] Avoid &lt;code&gt;rw=any&lt;/code&gt; and &lt;code&gt;superuser=any&lt;/code&gt; — use explicit security flavors&lt;/li&gt;
&lt;li&gt;[ ] Map S3 Access Point file system user to a least-privilege NAS user (not root/UID 0)&lt;/li&gt;
&lt;li&gt;[ ] Validate NFS/SMB ACL behavior when S3 AP is active&lt;/li&gt;
&lt;li&gt;[ ] Validate S3 API access against file-level permissions&lt;/li&gt;
&lt;li&gt;[ ] Capture ONTAP audit evidence where required (&lt;a href="https://docs.netapp.com/us-en/ontap/nas-audit/index.html" rel="noopener noreferrer"&gt;ONTAP FPolicy&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;[ ] Document junction path and volume scope&lt;/li&gt;
&lt;li&gt;[ ] Isolate analytics volumes from production NFS/SMB workloads if throughput contention is a concern&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: strace the mount command
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;strace &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mount mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;nfsvers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3,nolock 10.0.3.133:/vol1 /mnt/fsxn 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount.nfs: trying 10.0.3.133 prog 100003 vers 3 prot TCP port 2049
mount.nfs: trying 10.0.3.133 prog 100005 vers 3 prot UDP port 635
mount&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"10.0.3.133:/vol1"&lt;/span&gt;, &lt;span class="s2"&gt;"/mnt/fsxn"&lt;/span&gt;, &lt;span class="s2"&gt;"nfs"&lt;/span&gt;, ...&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; EACCES &lt;span class="o"&gt;(&lt;/span&gt;Permission denied&lt;span class="o"&gt;)&lt;/span&gt;
mount.nfs: mount&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;)&lt;/span&gt;: Permission denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key finding&lt;/strong&gt;: &lt;code&gt;mount.nfs&lt;/code&gt; successfully connects to both NFS (port 2049) and mountd (port 635), but the &lt;code&gt;mount()&lt;/code&gt; &lt;strong&gt;syscall&lt;/strong&gt; returns &lt;code&gt;EACCES&lt;/code&gt;. The denial happens at the kernel level, not at the server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TCP/UDP note&lt;/strong&gt;: The initial reachability check used &lt;code&gt;/dev/tcp&lt;/code&gt;, confirming TCP reachability. During the actual mount attempt, &lt;code&gt;mount.nfs&lt;/code&gt; tried mountd over UDP as shown in the strace output. This is not a contradiction — NFSv3 mountd may use either transport. For production troubleshooting, use &lt;code&gt;rpcinfo&lt;/code&gt; and packet capture to confirm the actual protocol and port mapping.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Manual NFS RPC Calls (User-space)
&lt;/h3&gt;

&lt;p&gt;To prove ONTAP is granting access, I performed manual NFS RPC calls using Python sockets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;

&lt;span class="c1"&gt;# MOUNT RPC (program 100005, version 3, procedure MNT)
&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_INET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SOCK_DGRAM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;settimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mount_rpc_packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.0.3.133&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;635&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Parse: status=0 (MNT3_OK), file_handle=44 bytes
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MOUNT RPC: SUCCESS ✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# NFS3 FSINFO, GETATTR, READDIRPLUS — all succeed
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NFS3 FSINFO: SUCCESS ✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NFS3 GETATTR: SUCCESS ✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NFS3 READDIRPLUS: SUCCESS ✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All NFS operations succeed at user-space level.&lt;/strong&gt; ONTAP grants full access. The problem is not the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: tmpfs Mount Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; tmpfs tmpfs /tmp/test_mount &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SUCCESS"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FAILED"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUCCESS ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;mount()&lt;/code&gt; syscall itself is allowed. Only NFS filesystem type is blocked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Seccomp Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;%sh
&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/self/status | &lt;span class="nb"&gt;grep &lt;/span&gt;Seccomp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seccomp:        2
Seccomp_filters:        1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Seccomp: 2&lt;/code&gt; = BPF filter mode active.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Conclusion
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│ Evidence Chain:                                                 │
│                                                                 │
│ 1. Network connectivity      → ✅ All NFS ports reachable       │
│ 2. ONTAP export policy       → ✅ 0.0.0.0/0, rw=any, su=any     │
│ 3. NFS RPC (user-space)      → ✅ All operations succeed        │
│ 4. mount() with type="nfs"   → ❌ EACCES                        │
│ 5. mount() with type="tmpfs" → ✅ Success                       │
│ 6. Seccomp                   → Active (BPF filter mode)         │
│                                                                 │
│ Conclusion: The evidence points to a local platform security    │
│ boundary, likely seccomp filtering or an equivalent runtime     │
│ restriction, blocking the NFS mount path.                       │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error message "access denied &lt;strong&gt;by server&lt;/strong&gt;" is misleading. The &lt;code&gt;mount.nfs&lt;/code&gt; program interprets the kernel's EACCES as a server-side denial, but strace reveals the truth: the denial is local.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If sharing this finding&lt;/strong&gt;: This is not a Databricks compatibility verdict. It is a layer-by-layer validation of observed boundaries in one environment (DBR 17.3 LTS, ap-northeast-1). Platform behavior may differ across runtime versions, access modes, and configurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Because Databricks does not publicly document this specific syscall/filesystem-type behavior, treat this as validation evidence rather than an official platform statement until confirmed by Databricks Support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  All Mount Options Tested
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Options&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o nfsvers=3,nolock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;access denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o nfsvers=4.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;access denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o nfsvers=3,nolock,resvport&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;access denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o nfsvers=3,nolock,noresvport&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;access denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o sec=sys&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;access denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(no options)&lt;/td&gt;
&lt;td&gt;access denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;tmpfs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SUCCESS&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Evidence Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Evidence&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;TCP 2049 / TCP 111 / TCP 635 reachable&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Network path exists between cluster and FSx for ONTAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP export&lt;/td&gt;
&lt;td&gt;Export policy allows 0.0.0.0/0, rw=any, su=any&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;Export policy is not the blocker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS server RPC&lt;/td&gt;
&lt;td&gt;MOUNT / FSINFO / GETATTR / READDIRPLUS succeed via user-space&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;ONTAP grants NFS operations to this client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local syscall&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mount(type=nfs)&lt;/code&gt; returns EACCES&lt;/td&gt;
&lt;td&gt;❌ Fail&lt;/td&gt;
&lt;td&gt;Evidence points to a local runtime boundary affecting kernel NFS mount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local syscall control&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mount(type=tmpfs)&lt;/code&gt; succeeds&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mount()&lt;/code&gt; syscall is not universally blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime security&lt;/td&gt;
&lt;td&gt;Seccomp mode 2 observed in the tested process context&lt;/td&gt;
&lt;td&gt;Observed&lt;/td&gt;
&lt;td&gt;Runtime filtering may restrict NFS-specific mount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unity Catalog S3&lt;/td&gt;
&lt;td&gt;External Location test on S3 AP ARN → AccessDenied&lt;/td&gt;
&lt;td&gt;❌ Fail&lt;/td&gt;
&lt;td&gt;Session policy does not allow S3 AP ARN pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instance Profile S3&lt;/td&gt;
&lt;td&gt;boto3 GetObject on S3 AP → Success&lt;/td&gt;
&lt;td&gt;✅ Pass&lt;/td&gt;
&lt;td&gt;IAM role itself has correct permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;showmount -e&lt;/code&gt; confirms that the export is visible through mountd. It does not guarantee that the local runtime allows the kernel NFS mount operation to complete. &lt;code&gt;showmount -e&lt;/code&gt; validates NFS export visibility only. It does not validate the file system user identity associated with the S3 Access Point. For S3 AP authorization, record the associated UNIX or Windows identity and verify file-level permissions separately — these are independent authorization paths.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  FSx for ONTAP S3 AP Authorization Path
&lt;/h2&gt;

&lt;p&gt;FSx for ONTAP S3 Access Points use a &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-manage-access-fsxn.html" rel="noopener noreferrer"&gt;dual-layer authorization model&lt;/a&gt; that combines AWS IAM permissions with file system-level permissions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — S3-side authorization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM identity-based policy (caller's permissions)&lt;/li&gt;
&lt;li&gt;S3 Access Point resource policy&lt;/li&gt;
&lt;li&gt;VPC endpoint policy (if applicable)&lt;/li&gt;
&lt;li&gt;SCP / RCP (if applicable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — FSx for ONTAP-side authorization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File system user associated with the access point&lt;/li&gt;
&lt;li&gt;UNIX mode-bits / NFSv4 ACLs (for UNIX security style volumes)&lt;/li&gt;
&lt;li&gt;Windows ACLs (for NTFS security style volumes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the Databricks validation, the failure occurs &lt;strong&gt;before Layer 2&lt;/strong&gt; — Unity Catalog's generated session policy restricts the assumed role session at the S3 API level, preventing the request from reaching FSx for ONTAP-side authorization. The Instance Profile + boto3 path bypasses Unity Catalog's session policy, allowing both layers to be evaluated normally.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For production, both layers must be configured with least-privilege. A permissive file system user (e.g., root / UID 0) combined with a broad IAM policy creates an overly permissive access path.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Approach 4: Instance Profile + boto3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;Customer-managed VPC workspace, Dedicated cluster with an Instance Profile attached.&lt;/p&gt;

&lt;h3&gt;
  
  
  IMDS Access
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="c1"&gt;# IMDSv2 token
&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://169.254.169.254/latest/api/token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-aws-ec2-metadata-token-ttl-seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;21600&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ✅ Success
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Regular S3 Access
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;buckets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_buckets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ListBuckets: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Buckets&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; buckets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ✅ 58 buckets
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  FSx for ONTAP S3 AP Access
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_objects_v2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;FSx-S3-AP-alias&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxKeys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Objects: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;KeyCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ✅ Works
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This works.&lt;/strong&gt; Instance Profile credentials bypass Unity Catalog's session policy entirely. boto3 talks directly to the S3 API with the EC2 instance's IAM role.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Governance warning&lt;/strong&gt;&lt;br&gt;
Instance Profile + boto3 is a pragmatic workaround for PoC and controlled experiments. It bypasses Unity Catalog governance, including fine-grained access control, lineage, and centralized data access auditing. Do not treat this as a production lakehouse governance pattern without a separate security and compliance review. Databricks recommends &lt;a href="https://docs.databricks.com/en/connect/unity-catalog/storage-credentials.html" rel="noopener noreferrer"&gt;Unity Catalog external locations&lt;/a&gt; as the standard governed access mechanism.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope note&lt;/strong&gt;&lt;br&gt;
The Instance Profile + boto3 sample above runs on the driver node only (single-node PoC pattern). Whether the same credential, network path, and concurrency behavior applies to Spark executors in a multi-node cluster requires separate validation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Approach 5: S3 AP + Instance Profile (Managed VPC with VPC Peering)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Hypothesis
&lt;/h3&gt;

&lt;p&gt;If Instance Profile + boto3 works on a Customer-managed VPC (Approach 4), does it also work from a Databricks-managed VPC with VPC Peering to the FSx for ONTAP VPC? This would validate whether the S3 Gateway Endpoint in the Databricks-managed VPC can route S3 AP requests to the FSx for ONTAP backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Databricks-managed VPC (&lt;code&gt;vpc-060209589cbe4c298&lt;/code&gt;, CIDR: 10.53.0.0/16)&lt;/li&gt;
&lt;li&gt;FSx for ONTAP VPC (&lt;code&gt;vpc-0ae01826f906191af&lt;/code&gt;, CIDR: 10.0.0.0/16)&lt;/li&gt;
&lt;li&gt;VPC Peering: &lt;code&gt;pcx-02167ddf900a30782&lt;/code&gt; (active)&lt;/li&gt;
&lt;li&gt;Route tables: updated in both directions&lt;/li&gt;
&lt;li&gt;FSx for ONTAP security group: allows all traffic (0.0.0.0/0)&lt;/li&gt;
&lt;li&gt;S3 Gateway Endpoint: &lt;code&gt;vpce-020b59ab4da0b44b8&lt;/code&gt; (full access policy)&lt;/li&gt;
&lt;li&gt;Cluster: m5.large × 3, DBR 17.3 LTS, Dedicated mode, Instance Profile attached&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Result
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dns_resolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"52.219.151.110"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vpc_peering_443"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vpc_peering_nfs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"s3_ap_access"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read timeout"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Analysis
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DNS resolution&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;S3 AP alias resolves to S3 endpoint IP (52.219.x.x)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VPC Peering (TCP 443)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;FSx for ONTAP management IP unreachable — egress blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VPC Peering (NFS 2049)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;NFS port unreachable — egress blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP via S3 Gateway Endpoint&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Read timeout — S3 service reachable but FSx for ONTAP backend connection fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IMDS / Instance Profile&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Credentials available and valid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key finding&lt;/strong&gt;: Even with VPC Peering established, routes configured, and security groups permissive, the Databricks-managed VPC's egress restrictions block connectivity to the FSx for ONTAP backend. The S3 Gateway Endpoint routes requests to the S3 service, but FSx for ONTAP S3 AP requires the S3 service to reach the FSx for ONTAP file system — which is in a different VPC from the Databricks cluster. The S3 service-side routing to the FSx for ONTAP backend is not affected by customer-side VPC Peering.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: This result confirms that FSx for ONTAP S3 AP access requires the requesting service (Databricks cluster) to be in the same VPC as the FSx for ONTAP file system, or to use a network configuration where the S3 service can reach the FSx for ONTAP backend. VPC Peering between the requester VPC and the FSx for ONTAP VPC does not help because S3 AP requests are routed through the S3 service, not directly to the FSx for ONTAP IP.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Lesson
&lt;/h3&gt;

&lt;p&gt;S3 AP requests do not traverse VPC Peering. They are routed through the S3 service endpoint. For FSx for ONTAP S3 AP to work, the S3 service must be able to reach the FSx for ONTAP file system's internal endpoint. This is handled by AWS internally when the request originates from the same region, but the Databricks-managed VPC's egress restrictions appear to interfere with this path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer-managed VPC (same VPC as FSx for ONTAP) remains the only validated path for Instance Profile + boto3 access to FSx for ONTAP S3 AP from Databricks.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  IMDS Access Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cluster Mode&lt;/th&gt;
&lt;th&gt;Workspace Type&lt;/th&gt;
&lt;th&gt;IMDS&lt;/th&gt;
&lt;th&gt;boto3 S3&lt;/th&gt;
&lt;th&gt;boto3 S3 AP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Standard (Shared)&lt;/td&gt;
&lt;td&gt;Managed VPC&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;Dedicated&lt;/td&gt;
&lt;td&gt;Managed VPC&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;Dedicated&lt;/td&gt;
&lt;td&gt;Customer VPC&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;Dedicated + Instance Profile&lt;/td&gt;
&lt;td&gt;Managed VPC (VPC Peering)&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;Dedicated + Instance Profile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Customer VPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Row 4 note: IMDS works and Instance Profile credentials are valid, but S3 AP access times out because the Databricks-managed VPC egress restrictions block FSx for ONTAP backend connectivity. Regular S3 bucket access was not tested with a permissive policy (AccessDenied was due to intentionally scoped IAM policy, not network).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;IMDS is blocked on all configurations except Dedicated mode with an explicitly registered Instance Profile on a Customer-managed VPC workspace.&lt;/p&gt;




&lt;h2&gt;
  
  
  Complete Results Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Blocker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;UC External Location + dbutils.fs (without &lt;code&gt;access_point&lt;/code&gt; field)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Generated session policy did not allow S3 AP ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1b&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;UC External Location + &lt;code&gt;access_point&lt;/code&gt; field (file-level read)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Top-level ls, head, spark.read with explicit path all work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1c&lt;/td&gt;
&lt;td&gt;UC External Location + &lt;code&gt;access_point&lt;/code&gt; field (subdirectory ls)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Prefix-based ListObjectsV2 still blocked for subdirectories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1d&lt;/td&gt;
&lt;td&gt;UC External Location + CREATE TABLE LOCATION&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;UC_CLOUD_STORAGE_ACCESS_FAILURE during internal validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;UC External Location + Spark read (directory)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Same prefix-level access issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;NFS mount (Managed VPC, VPC Peering)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Egress blocked (port 2049)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;NFS mount (Customer VPC, Dedicated)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;NFS mount blocked by seccomp by design (confirmed by Databricks Support)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;boto3 (Managed VPC, no Instance Profile)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;IMDS blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;boto3 (Customer VPC, no Instance Profile)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;IMDS blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Instance Profile + boto3 (Customer VPC)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Works (bypasses UC governance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NFS RPC user-space (Customer VPC)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Works but impractical for production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;No Isolation Shared mode&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Legacy access mode; not pursued&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;S3 AP + Instance Profile + boto3 (Managed VPC, VPC Peering)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Managed VPC egress blocks FSx for ONTAP backend connectivity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Governance Impact Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Documentation status (Updated 2026-05-26)&lt;/strong&gt;: Databricks Support confirmed that the &lt;code&gt;access_point&lt;/code&gt; field was never released as GA and has been removed from documentation. Unity Catalog External Locations do not currently support S3 Access Points as storage targets. The partial success observed is a side effect, not a supported code path. Feature gap reported to UC engineering — no timeline available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Access path&lt;/th&gt;
&lt;th&gt;Governance model&lt;/th&gt;
&lt;th&gt;Auditability&lt;/th&gt;
&lt;th&gt;Production suitability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unity Catalog External Location&lt;/td&gt;
&lt;td&gt;Centralized UC governance (fine-grained, lineage)&lt;/td&gt;
&lt;td&gt;High (if supported)&lt;/td&gt;
&lt;td&gt;Preferred, but blocked in this validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instance Profile + boto3&lt;/td&gt;
&lt;td&gt;EC2 IAM role based&lt;/td&gt;
&lt;td&gt;AWS-side logs possible if enabled; UC lineage not captured&lt;/td&gt;
&lt;td&gt;PoC only unless separately approved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel NFS mount&lt;/td&gt;
&lt;td&gt;Filesystem / OS level&lt;/td&gt;
&lt;td&gt;Outside UC governance&lt;/td&gt;
&lt;td&gt;Not viable in this validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User-space NFS RPC&lt;/td&gt;
&lt;td&gt;Custom application path&lt;/td&gt;
&lt;td&gt;Custom logging required&lt;/td&gt;
&lt;td&gt;Experimental only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;IAM / S3 AP / Athena workgroup&lt;/td&gt;
&lt;td&gt;AWS-side evidence possible&lt;/td&gt;
&lt;td&gt;Best current read-only SQL analytics fit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock Knowledge Bases + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;IAM / S3 AP / Bedrock Knowledge Base role / guardrails where used&lt;/td&gt;
&lt;td&gt;AWS-side evidence possible&lt;/td&gt;
&lt;td&gt;AWS-documented RAG / GenAI path; validated with permission-aware retrieval in &lt;a href="https://dev.to/aws-builders/building-an-agentic-access-aware-rag-system-with-amazon-fsx-for-netapp-ontap-s3-vectors-and-s3-2b86"&gt;related series&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue / EMR Serverless + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;IAM / S3 AP / Glue / EMR job roles&lt;/td&gt;
&lt;td&gt;AWS-side evidence possible&lt;/td&gt;
&lt;td&gt;Validated ETL / Spark path in this broader series where verification-pack evidence is available; validate production write-back semantics separately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;AWS-side audit events, such as CloudTrail data events where enabled and applicable, may show S3 API access by the instance profile, but they do not replace Unity Catalog lineage, table-level privileges, or centralized Databricks governance controls.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  MLOps Boundary
&lt;/h3&gt;

&lt;p&gt;Using boto3 to read objects from FSx for ONTAP S3 AP does not automatically make the downstream ML workflow governed.&lt;/p&gt;

&lt;p&gt;If the data retrieved via Instance Profile + boto3 is used for ML or GenAI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register derived datasets in governed storage (Unity Catalog managed location)&lt;/li&gt;
&lt;li&gt;Track experiments with &lt;a href="https://docs.databricks.com/en/mlflow/index.html" rel="noopener noreferrer"&gt;MLflow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Register models in &lt;a href="https://docs.databricks.com/aws/en/catalog-explorer/explore-models" rel="noopener noreferrer"&gt;Unity Catalog&lt;/a&gt; where applicable&lt;/li&gt;
&lt;li&gt;Document source data access path (S3 AP alias, prefix, timestamp)&lt;/li&gt;
&lt;li&gt;Record whether training data lineage is captured or externalized&lt;/li&gt;
&lt;li&gt;Ensure the ML compute uses an access mode compatible with Unity Catalog governance&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Models in Unity Catalog provides centralized access control, auditing, lineage, and model discovery across workspaces. If the PoC data path bypasses UC, the model lifecycle should still be governed through UC model registry.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  AI / RAG Data Readiness Checklist
&lt;/h3&gt;

&lt;p&gt;If the FSx for ONTAP S3 AP data is intended for AI, RAG, or GenAI pipelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Are documents classified by sensitivity (PHI, PII, financial, internal, public)?&lt;/li&gt;
&lt;li&gt;[ ] Are file-level permissions preserved or re-modeled for the AI pipeline?&lt;/li&gt;
&lt;li&gt;[ ] Is metadata available for filtering and retrieval (file type, date, owner)?&lt;/li&gt;
&lt;li&gt;[ ] Is freshness requirement defined (real-time, daily, weekly)?&lt;/li&gt;
&lt;li&gt;[ ] Is read-only access sufficient, or does the pipeline need write-back?&lt;/li&gt;
&lt;li&gt;[ ] Is human review required for generated output before downstream use?&lt;/li&gt;
&lt;li&gt;[ ] Is permission-aware retrieval required (user A sees only their authorized documents)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If permission-aware retrieval is required, define one of:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enforce at source access path&lt;/strong&gt; — use per-user or per-group S3 Access Points with scoped file system users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-model permissions in metadata index&lt;/strong&gt; — extract file-level ACLs into a searchable metadata store and filter at query time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter retrieval results by user/group claims&lt;/strong&gt; — apply post-retrieval filtering based on authenticated user identity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not proceed&lt;/strong&gt; until authorization model is validated and approved by security owner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Instance Profile + boto3 approval requirements&lt;/strong&gt; (for regulated workloads):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data owner approval&lt;/li&gt;
&lt;li&gt;Security owner approval&lt;/li&gt;
&lt;li&gt;Platform owner approval&lt;/li&gt;
&lt;li&gt;Compliance reviewer approval (if regulated data involved)&lt;/li&gt;
&lt;li&gt;Defined: allowed prefix, allowed operations, logging requirements, expiration date&lt;/li&gt;
&lt;li&gt;Approval record location (where the decision is stored)&lt;/li&gt;
&lt;li&gt;Review / expiration date (when the approval must be re-evaluated)&lt;/li&gt;
&lt;li&gt;Incident escalation contact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For regulated workloads, do not use Instance Profile + boto3 for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Patient-facing responses or clinical decision support&lt;/li&gt;
&lt;li&gt;Financial decision automation&lt;/li&gt;
&lt;li&gt;Unreviewed access to regulated datasets&lt;/li&gt;
&lt;li&gt;Writeback to source-controlled data locations&lt;/li&gt;
&lt;li&gt;Workloads requiring Unity Catalog lineage&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Decision Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Recommended path today&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;th&gt;Next validation action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL query on structured files&lt;/td&gt;
&lt;td&gt;Athena + FSx for ONTAP S3 AP (Part 1)&lt;/td&gt;
&lt;td&gt;Verified, simple, governed&lt;/td&gt;
&lt;td&gt;Scale test with production data sizes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG / GenAI over NAS documents&lt;/td&gt;
&lt;td&gt;Bedrock Knowledge Bases + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-build-rag-with-bedrock.html" rel="noopener noreferrer"&gt;AWS-documented tutorial&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Validate retrieval accuracy, permission-aware filtering, and sync freshness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ETL pipeline on NAS data&lt;/td&gt;
&lt;td&gt;Glue or EMR Serverless + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;Validated in this broader series where verification-pack evidence is available&lt;/td&gt;
&lt;td&gt;Validate throughput impact and production write-back semantics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless file processing&lt;/td&gt;
&lt;td&gt;Lambda + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-process-files-with-lambda.html" rel="noopener noreferrer"&gt;AWS-documented tutorial&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Validate concurrency and throughput for your workload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks governance with Unity Catalog&lt;/td&gt;
&lt;td&gt;Wait for platform support&lt;/td&gt;
&lt;td&gt;UC session policy currently blocks S3 AP ARN in my validation&lt;/td&gt;
&lt;td&gt;Monitor Databricks support case response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks unstructured data PoC&lt;/td&gt;
&lt;td&gt;Dedicated cluster + Instance Profile + boto3&lt;/td&gt;
&lt;td&gt;Works, but bypasses UC governance&lt;/td&gt;
&lt;td&gt;Validate executor-scale behavior separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production Databricks lakehouse tables&lt;/td&gt;
&lt;td&gt;Use supported cloud storage (S3 bucket)&lt;/td&gt;
&lt;td&gt;Required for Delta write semantics&lt;/td&gt;
&lt;td&gt;N/A — use standard pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks distributed processing over FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;Not validated yet&lt;/td&gt;
&lt;td&gt;Driver-only boto3 success does not prove executor-scale behavior&lt;/td&gt;
&lt;td&gt;Test with multi-node cluster and Spark mapPartitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise read-only analytics&lt;/td&gt;
&lt;td&gt;Athena / Glue / EMR Serverless / FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;Best current fit for AWS-native path&lt;/td&gt;
&lt;td&gt;Production workload isolation test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video streaming from NAS&lt;/td&gt;
&lt;td&gt;CloudFront + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-stream-video-with-cloudfront.html" rel="noopener noreferrer"&gt;AWS-documented tutorial&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Validate caching and latency for your content&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;This article does not recommend bypassing Unity Catalog for production governed lakehouse workloads. The Instance Profile + boto3 path is documented because it worked in a controlled validation environment, not because it is the preferred governance model.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Architecture Decision Guidance
&lt;/h2&gt;

&lt;p&gt;Databricks remains the recommended platform for curated lakehouse workloads, governed Delta tables, ML pipelines, and multi-step data engineering. FSx for ONTAP S3 AP should be treated as a source integration boundary that may require staging, validation, or an alternate read path depending on governance requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Databricks when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data is already in supported object storage (S3 bucket)&lt;/li&gt;
&lt;li&gt;Delta Lake write semantics are required (INSERT, MERGE, OPTIMIZE, VACUUM)&lt;/li&gt;
&lt;li&gt;Unity Catalog lineage and fine-grained governance are mandatory&lt;/li&gt;
&lt;li&gt;Large-scale Spark processing is required&lt;/li&gt;
&lt;li&gt;ML/AI workloads need integrated compute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use AWS-native services + FSx for ONTAP S3 AP when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The primary requirement is read-only SQL analytics over NAS data → &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;Athena&lt;/a&gt; (validated in Part 1)&lt;/li&gt;
&lt;li&gt;RAG / GenAI over enterprise documents → &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-build-rag-with-bedrock.html" rel="noopener noreferrer"&gt;Bedrock Knowledge Bases&lt;/a&gt; (AWS-documented path)&lt;/li&gt;
&lt;li&gt;ETL pipelines reading/transforming NAS data → &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-transform-data-with-glue.html" rel="noopener noreferrer"&gt;Glue&lt;/a&gt; (validated in this broader series where verification-pack evidence is available)&lt;/li&gt;
&lt;li&gt;Spark-scale processing without persistent clusters → &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-run-spark-with-emr-serverless.html" rel="noopener noreferrer"&gt;EMR Serverless&lt;/a&gt; (validated in this broader series where verification-pack evidence is available)&lt;/li&gt;
&lt;li&gt;Serverless file processing (thumbnails, text extraction, transcription) → &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-process-files-with-lambda.html" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; (AWS-documented path)&lt;/li&gt;
&lt;li&gt;Video streaming from NAS → &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-stream-video-with-cloudfront.html" rel="noopener noreferrer"&gt;CloudFront&lt;/a&gt; (AWS-documented path)&lt;/li&gt;
&lt;li&gt;External partner file exchange → &lt;a href="https://docs.aws.amazon.com/transfer/latest/userguide/fsx-s3-access-points.html" rel="noopener noreferrer"&gt;Transfer Family&lt;/a&gt; (AWS-documented path)&lt;/li&gt;
&lt;li&gt;BI and AI-assisted analytics → QuickSight candidate path, typically via Athena or Glue Catalog&lt;/li&gt;
&lt;li&gt;Source data copy should be minimized&lt;/li&gt;
&lt;li&gt;Workload isolation and governance can be validated with AWS-side controls&lt;/li&gt;
&lt;li&gt;Serverless, pay-per-query or pay-per-invocation cost model is preferred&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use controlled boto3 PoC only when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The workload is exploratory and time-limited&lt;/li&gt;
&lt;li&gt;Unity Catalog lineage is not required for the PoC scope&lt;/li&gt;
&lt;li&gt;Explicit approval is obtained from data owner, security owner, and platform owner&lt;/li&gt;
&lt;li&gt;Compensating controls are defined and documented&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FSx for ONTAP Sizing Considerations
&lt;/h3&gt;

&lt;p&gt;Before selecting an analytics engine, validate FSx for ONTAP-side capacity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Throughput capacity&lt;/strong&gt; — S3 API throughput is bounded by the FSx for ONTAP file system's provisioned throughput&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected S3 API request rate&lt;/strong&gt; — high-frequency small object reads may hit IOPS limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File count and average object size&lt;/strong&gt; — large directories with many small files may increase listing latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefix layout&lt;/strong&gt; — flat vs hierarchical prefix design affects listing performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFS/SMB production workload window&lt;/strong&gt; — analytics queries share throughput with existing file workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot / backup / replication schedule&lt;/strong&gt; — SnapMirror and backup operations consume throughput&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation strategy&lt;/strong&gt; — consider a dedicated volume or SVM for analytics access to avoid contention&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Delta Lake production workloads require more than object read access. They require validated behavior for transaction log writes, atomic commit assumptions, concurrent writers, checkpointing, recovery, and lifecycle operations. This article does not validate FSx for ONTAP S3 AP for Delta write-path semantics.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Compensating Controls for Controlled boto3 PoC
&lt;/h2&gt;

&lt;p&gt;If Instance Profile + boto3 is approved for a controlled PoC, define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dedicated cluster only (no shared compute)&lt;/li&gt;
&lt;li&gt;Single-purpose instance profile (not reused across workloads)&lt;/li&gt;
&lt;li&gt;Least-privilege S3 Access Point policy (specific prefix only)&lt;/li&gt;
&lt;li&gt;Read-only permissions by default&lt;/li&gt;
&lt;li&gt;Allowed prefix list (explicitly documented)&lt;/li&gt;
&lt;li&gt;CloudTrail data event coverage where enabled and applicable&lt;/li&gt;
&lt;li&gt;Notebook/job owner (named individual)&lt;/li&gt;
&lt;li&gt;Approval expiration date&lt;/li&gt;
&lt;li&gt;No production writeback&lt;/li&gt;
&lt;li&gt;No regulated data unless separately approved with compensating controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Recommended Databricks-side controls:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restrict instance profile usage to an approved group via workspace admin settings&lt;/li&gt;
&lt;li&gt;Enforce dedicated access mode through cluster policy&lt;/li&gt;
&lt;li&gt;Restrict cluster creation permissions to approved users&lt;/li&gt;
&lt;li&gt;Tag PoC clusters with owner, approval ID, and expiration date&lt;/li&gt;
&lt;li&gt;Disable or terminate clusters after approval expiration&lt;/li&gt;
&lt;li&gt;Review workspace audit logs for cluster and instance profile usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Post-expiration mandatory actions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Terminate all PoC clusters using the instance profile&lt;/li&gt;
&lt;li&gt;Remove the instance profile from workspace admin settings&lt;/li&gt;
&lt;li&gt;Archive all evidence (notebooks, logs, results) to approved storage&lt;/li&gt;
&lt;li&gt;Update approval record with completion date and findings&lt;/li&gt;
&lt;li&gt;Confirm no residual access paths remain (audit workspace settings)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Data Protection Considerations
&lt;/h2&gt;

&lt;p&gt;FSx for ONTAP S3 AP exposes access to file data; it does not replace ONTAP volume-level protection. When analytics workloads access source data via S3 AP, validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot schedule impact&lt;/strong&gt; — analytics reads do not conflict with scheduled snapshots, but heavy write-back could&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SnapMirror replication policy&lt;/strong&gt; — source volume replication continues regardless of S3 AP access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backup window vs analytics query window&lt;/strong&gt; — concurrent backup and analytics may compete for throughput&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write-back isolation&lt;/strong&gt; — analytics results should be written to a separate volume or prefix, not the source-of-record volume&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovery behavior&lt;/strong&gt; — if analytics workload reads during a failover event, understand the RPO/RTO implications&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;ONTAP S3 NAS bucket data is protected by volume-level SnapMirror asynchronous replication, not by S3-level replication. Plan DR at the volume level.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Discovery Questions for Partners
&lt;/h2&gt;

&lt;p&gt;When a customer asks about Databricks + FSx for ONTAP S3 Access Points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Are the target files currently stored on NFS, SMB, or both?&lt;/li&gt;
&lt;li&gt;Is the workload read-only analytics, unstructured object processing, or Delta write?&lt;/li&gt;
&lt;li&gt;Is Unity Catalog lineage mandatory for this use case?&lt;/li&gt;
&lt;li&gt;Is this a regulated dataset (PHI, PII, financial)?&lt;/li&gt;
&lt;li&gt;Can the PoC run with a dedicated instance profile and limited prefix?&lt;/li&gt;
&lt;li&gt;What is the required concurrency and data size?&lt;/li&gt;
&lt;li&gt;Is executor-scale Spark processing required, or is driver-only sufficient?&lt;/li&gt;
&lt;li&gt;What rollback action is acceptable if FSx for ONTAP throughput impact is observed?&lt;/li&gt;
&lt;li&gt;Who approves non-Unity Catalog access paths?&lt;/li&gt;
&lt;li&gt;What evidence is required for security review?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Troubleshooting Playbook
&lt;/h2&gt;

&lt;p&gt;When Databricks access to FSx for ONTAP S3 AP fails, isolate one layer at a time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; — Can the instance profile call &lt;code&gt;s3:ListBucket&lt;/code&gt; on the S3 AP ARN? Can it call &lt;code&gt;s3:GetObject&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unity Catalog&lt;/strong&gt; — Does the same role work for a standard S3 bucket? Does it fail only for the FSx for ONTAP S3 AP ARN?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network&lt;/strong&gt; — Is the workspace customer-managed or Databricks-managed? Can the cluster reach NFS TCP 2049? Are route tables and security groups correct?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFS server&lt;/strong&gt; — Does &lt;code&gt;showmount -e&lt;/code&gt; work? Does the ONTAP export policy allow the client?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local runtime&lt;/strong&gt; — Does &lt;code&gt;strace&lt;/code&gt; show &lt;code&gt;mount()&lt;/code&gt; returning &lt;code&gt;EACCES&lt;/code&gt;? Does &lt;code&gt;tmpfs&lt;/code&gt; mount succeed? Does user-space NFS RPC succeed?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workaround&lt;/strong&gt; — Does Dedicated + Instance Profile + boto3 work? Is bypassing Unity Catalog acceptable for this PoC?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Known Failure Signatures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely layer&lt;/th&gt;
&lt;th&gt;Next step&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;no session policy allows s3:ListBucket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unity Catalog session policy&lt;/td&gt;
&lt;td&gt;Compare regular S3 bucket vs FSx for ONTAP S3 AP with the same role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP 2049 unreachable&lt;/td&gt;
&lt;td&gt;Network / managed VPC boundary&lt;/td&gt;
&lt;td&gt;Test from customer-managed VPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;mount.nfs: access denied by server&lt;/code&gt; with &lt;code&gt;mount()&lt;/code&gt; EACCES in strace&lt;/td&gt;
&lt;td&gt;Local runtime restriction&lt;/td&gt;
&lt;td&gt;Capture strace and &lt;code&gt;/proc/self/status&lt;/code&gt; seccomp output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;boto3 &lt;code&gt;NoCredentialsError&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Instance profile / IMDS blocked&lt;/td&gt;
&lt;td&gt;Verify cluster mode is Dedicated and instance profile is registered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;boto3 &lt;code&gt;ReadTimeoutError&lt;/code&gt; on S3 AP&lt;/td&gt;
&lt;td&gt;FSx for ONTAP backend or VPC endpoint routing&lt;/td&gt;
&lt;td&gt;Test with a fresh SVM/volume to isolate; check FSx for ONTAP CPU utilization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;boto3 &lt;code&gt;ReadTimeoutError&lt;/code&gt; on S3 AP from Managed VPC (IMDS works)&lt;/td&gt;
&lt;td&gt;Managed VPC egress restriction blocking FSx for ONTAP backend&lt;/td&gt;
&lt;td&gt;Deploy in Customer-managed VPC (same VPC as FSx for ONTAP); VPC Peering does not resolve this&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Driver-only boto3 works, but Spark job fails&lt;/td&gt;
&lt;td&gt;Executor credential/network path&lt;/td&gt;
&lt;td&gt;Validate credentials, routing, and concurrency from executors separately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What This Article Does Not Conclude
&lt;/h2&gt;

&lt;p&gt;This article does not conclude that Databricks cannot ever support FSx for ONTAP S3 AP. It documents the behavior observed in one validated environment and identifies the platform boundaries that need vendor confirmation or additional support.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to Tell Stakeholders
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Current recommendation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;AWS-documented native service paths&lt;/strong&gt; where they match the workload: Athena for SQL, Bedrock Knowledge Bases for RAG/GenAI, Glue or EMR Serverless for ETL/Spark, Lambda for serverless file processing, CloudFront for streaming, and Transfer Family for partner file exchange&lt;/li&gt;
&lt;li&gt;Treat Athena as the validated read-oriented SQL path in Part 1. Treat Glue / EMR Serverless as validated ETL / Spark paths only where corresponding &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack" rel="noopener noreferrer"&gt;verification-pack&lt;/a&gt; evidence is available.&lt;/li&gt;
&lt;li&gt;Treat Bedrock Knowledge Bases, Lambda (file processing), CloudFront, and Transfer Family as AWS-documented candidate paths that still require workload-specific validation&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Databricks + Instance Profile + boto3&lt;/strong&gt; only for controlled PoC or unstructured data experiments&lt;/li&gt;
&lt;li&gt;Do not position Unity Catalog + FSx for ONTAP S3 AP as production-ready until the session policy supports S3 Access Point ARN patterns&lt;/li&gt;
&lt;li&gt;Do not rely on kernel NFS mounts inside Databricks until the platform explicitly supports this path&lt;/li&gt;
&lt;li&gt;For Delta Lake production tables, continue to use supported object storage patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This validation should be used to guide architecture selection, not to disqualify Databricks from lakehouse workloads.&lt;/p&gt;

&lt;p&gt;This validation should not be used to compare AWS-native services and Databricks as competing platforms. AWS-native services (Athena, Bedrock, Glue, EMR Serverless, Lambda) each have &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/using-access-points-with-aws-services.html" rel="noopener noreferrer"&gt;AWS-documented integration paths&lt;/a&gt; with FSx for ONTAP S3 AP — some validated in this series, others requiring workload-specific validation. Databricks is strong for governed lakehouse, Delta, ML, and production-scale data engineering workloads. The right choice depends on the access pattern, governance requirement, and workload type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key contributions of this validation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identified the root cause of NFS mount failure (seccomp BPF filter, not server-side denial) via strace analysis&lt;/li&gt;
&lt;li&gt;Discovered the &lt;code&gt;access_point&lt;/code&gt; field on External Location (via Databricks Support) that partially resolves the session policy&lt;/li&gt;
&lt;li&gt;Proved that file-level read under UC governance is possible (1000 rows, schema inference)&lt;/li&gt;
&lt;li&gt;Mapped the complete evidence chain: network → ONTAP → NFS RPC → kernel → seccomp&lt;/li&gt;
&lt;li&gt;Established that Customer-managed VPC (same VPC as FSx) is the only validated network path&lt;/li&gt;
&lt;li&gt;Provided a reusable troubleshooting playbook for future S3 AP integration attempts&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. "S3-compatible" ≠ "works everywhere S3 works"
&lt;/h3&gt;

&lt;p&gt;FSx for ONTAP S3 AP is S3-compatible at the API level, but platform security layers (session policies, VPC restrictions) may not recognize the ARN format. S3 API compatibility and platform-integrated S3 governance are different things.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Error messages can be misleading
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;mount.nfs: access denied by server&lt;/code&gt; made me spend hours checking ONTAP export policies. The real issue was a local runtime restriction. Always use &lt;code&gt;strace&lt;/code&gt; when mount fails unexpectedly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Platform security boundaries are not always documented
&lt;/h3&gt;

&lt;p&gt;You discover these boundaries by hitting them. The troubleshooting playbook above can save you time.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Customer-managed VPC is essential for storage integration
&lt;/h3&gt;

&lt;p&gt;If you need to connect Databricks to anything beyond standard S3 buckets, deploy in a Customer-managed VPC. Databricks-managed VPC provides limited customer control over cluster networking compared with a customer-managed VPC.&lt;/p&gt;

&lt;p&gt;This was further confirmed by testing S3 AP access from a Databricks-managed VPC with VPC Peering: even with VPC Peering active, routes configured, security groups permissive, and a S3 Gateway Endpoint present, S3 AP requests to FSx for ONTAP timed out. The Databricks-managed VPC egress restrictions block not only direct IP communication but also S3 AP backend connectivity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;S3 AP routing note&lt;/strong&gt;: S3 AP requests are routed through the S3 service endpoint, not directly to the FSx for ONTAP IP. VPC Peering between the requester VPC and the FSx for ONTAP VPC does not help because the S3 service needs internal connectivity to the FSx for ONTAP file system. Customer-managed VPC (same VPC as FSx for ONTAP) is the only validated path.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Databricks Control Plane (SaaS)
        ^
        | NAT Gateway (required outbound)
        |
Databricks Cluster ENI (Customer VPC, private subnet)
        |
        | Private VPC routing (no internet required)
        v
FSx for ONTAP ENI / SVM (same VPC, private subnet)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Databricks Support Case Packet, include network evidence: cluster subnet ID, FSx for ONTAP subnet ID, route table IDs, security group rules, and DNS resolution for FSx for ONTAP endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Instance Profile is a pragmatic PoC workaround
&lt;/h3&gt;

&lt;p&gt;Use Instance Profile + boto3 as a controlled PoC workaround. Do not use it as a substitute for Unity Catalog governance without a formal security review.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Always isolate variables when troubleshooting
&lt;/h3&gt;

&lt;p&gt;When FSx for ONTAP S3 AP wasn't responding, I created a new SVM and volume to isolate the issue. This confirmed the problem was SVM-specific rather than a platform-wide limitation.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Negative validation creates value
&lt;/h3&gt;

&lt;p&gt;A failed integration path can still create value when it prevents the wrong production architecture. This validation helps teams avoid assuming S3 API compatibility equals platform governance compatibility, choose the right engine for the right access pattern, and reduce time spent on ambiguous troubleshooting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Databricks Support Case Packet
&lt;/h2&gt;

&lt;p&gt;If you open a support case with Databricks, include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workspace type: Databricks-managed VPC or customer-managed VPC&lt;/li&gt;
&lt;li&gt;Cluster access mode and DBR version&lt;/li&gt;
&lt;li&gt;IAM role / instance profile configuration&lt;/li&gt;
&lt;li&gt;Unity Catalog storage credential and external location configuration&lt;/li&gt;
&lt;li&gt;Full AccessDenied error message (including the ARN and "no session policy" text)&lt;/li&gt;
&lt;li&gt;S3 AP ARN and alias format&lt;/li&gt;
&lt;li&gt;Network test results for NFS ports (TCP 2049, TCP 111, TCP 635)&lt;/li&gt;
&lt;li&gt;strace output showing &lt;code&gt;mount()&lt;/code&gt; EACCES&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/proc/self/status&lt;/code&gt; showing seccomp mode&lt;/li&gt;
&lt;li&gt;User-space NFS RPC success evidence (if applicable)&lt;/li&gt;
&lt;li&gt;Instance Profile boto3 success evidence (if applicable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showmount -e&lt;/code&gt; output (confirms export visibility)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tmpfs&lt;/code&gt; mount success evidence (proves mount syscall itself is allowed)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use Case Fit Matrix
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;When this article says "validated in this broader series," it refers to evidence captured in the linked verification-pack or related articles, not to Databricks-specific validation in this Part 2 article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Best current path&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL analytics on structured NAS files&lt;/td&gt;
&lt;td&gt;Athena + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;Verified read-oriented path with AWS-side governance controls, serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise IT RAG over documents&lt;/td&gt;
&lt;td&gt;Bedrock Knowledge Bases + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-build-rag-with-bedrock.html" rel="noopener noreferrer"&gt;AWS-documented tutorial&lt;/a&gt;; also validated in &lt;a href="https://dev.to/aws-builders/building-an-agentic-access-aware-rag-system-with-amazon-fsx-for-netapp-ontap-s3-vectors-and-s3-2b86"&gt;related series&lt;/a&gt; with permission-aware retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ETL / data transformation&lt;/td&gt;
&lt;td&gt;Glue or EMR Serverless + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;Validated in this broader series where &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack" rel="noopener noreferrer"&gt;verification-pack&lt;/a&gt; evidence is available; validate production write-back semantics separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless file processing (thumbnails, OCR, transcription)&lt;/td&gt;
&lt;td&gt;Lambda + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-process-files-with-lambda.html" rel="noopener noreferrer"&gt;AWS-documented tutorial&lt;/a&gt;; validate for your workload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large-scale Spark ETL&lt;/td&gt;
&lt;td&gt;EMR Serverless + FSx for ONTAP S3 AP or standard S3 bucket&lt;/td&gt;
&lt;td&gt;Validated in this series; Databricks executor-scale not validated on S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production Delta Lake tables&lt;/td&gt;
&lt;td&gt;Supported object storage (S3 bucket)&lt;/td&gt;
&lt;td&gt;Required for Delta write semantics and UC governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unstructured data experimentation (Databricks)&lt;/td&gt;
&lt;td&gt;Instance Profile + boto3 PoC&lt;/td&gt;
&lt;td&gt;Works in driver-only pattern, needs governance review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video streaming from NAS&lt;/td&gt;
&lt;td&gt;CloudFront + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-stream-video-with-cloudfront.html" rel="noopener noreferrer"&gt;AWS-documented tutorial&lt;/a&gt;; validate caching, latency, and file size for your content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External partner file exchange&lt;/td&gt;
&lt;td&gt;Transfer Family + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.aws.amazon.com/transfer/latest/userguide/fsx-s3-access-points.html" rel="noopener noreferrer"&gt;AWS-documented path&lt;/a&gt;; also validated in &lt;a href="https://dev.to/aws-builders/smart-routing-transfer-family-ingestion-and-voice-chat-permission-aware-rag-v42-3iml"&gt;related series&lt;/a&gt;; validate file operation limitations (rename, append, upload size)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lightweight serverless analytics&lt;/td&gt;
&lt;td&gt;DuckDB Lambda + FSx for ONTAP S3 AP&lt;/td&gt;
&lt;td&gt;Planned Part 3 validation; candidate for lightweight, low-idle-cost analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BI / dashboarding over NAS data&lt;/td&gt;
&lt;td&gt;Candidate: QuickSight via Athena or Glue Catalog&lt;/td&gt;
&lt;td&gt;AWS positions BI as a candidate use case; validate whether access path is Athena-backed or catalog-mediated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Cost Model Considerations
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Primary cost driver&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Athena&lt;/td&gt;
&lt;td&gt;Data scanned (per TB)&lt;/td&gt;
&lt;td&gt;Occasional SQL queries, serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock Knowledge Bases&lt;/td&gt;
&lt;td&gt;Model invocation + embedding + retrieval&lt;/td&gt;
&lt;td&gt;RAG / GenAI over enterprise documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue&lt;/td&gt;
&lt;td&gt;DPU-hours&lt;/td&gt;
&lt;td&gt;ETL pipelines, data transformation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks&lt;/td&gt;
&lt;td&gt;DBU + cloud compute instance hours&lt;/td&gt;
&lt;td&gt;Lakehouse pipelines, ML, Delta workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMR Serverless&lt;/td&gt;
&lt;td&gt;vCPU / memory × runtime duration&lt;/td&gt;
&lt;td&gt;Spark ETL without persistent clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda + DuckDB&lt;/td&gt;
&lt;td&gt;Invocation duration × memory&lt;/td&gt;
&lt;td&gt;Lightweight serverless analytics, event-driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudFront&lt;/td&gt;
&lt;td&gt;Data transfer + requests&lt;/td&gt;
&lt;td&gt;Video/media streaming from NAS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Cost comparison is not the focus of this article. Each engine has a fundamentally different pricing model. Databricks provides &lt;a href="https://docs.databricks.com/aws/en/admin/clusters/policy-definition.html" rel="noopener noreferrer"&gt;compute policies&lt;/a&gt; to control cluster creation, instance types, auto-termination, and cost-related attributes. For cost optimization, evaluate based on workload pattern (interactive vs batch, frequency, data volume) rather than unit price alone.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Partner / Customer Conversation Guide
&lt;/h2&gt;

&lt;p&gt;If a customer asks whether Databricks can directly process FSx for ONTAP S3 Access Point data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS-native service paths&lt;/strong&gt; such as Athena, Bedrock Knowledge Bases, Glue, EMR Serverless, Lambda, CloudFront, and Transfer Family have &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/using-access-points-with-aws-services.html" rel="noopener noreferrer"&gt;AWS-documented integration patterns&lt;/a&gt; with FSx for ONTAP S3 AP. In this series, Athena (Part 1), Glue, and EMR Serverless have been validated; the other paths should be validated per workload, Region, IAM model, FSx for ONTAP-side authorization, and governance requirement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databricks Unity Catalog&lt;/strong&gt; integration requires vendor confirmation for S3 Access Point ARN handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance Profile + boto3&lt;/strong&gt; can be used for controlled PoC experiments, but it bypasses Unity Catalog governance and is classified as a &lt;a href="https://docs.databricks.com/en/admin/sql/data-access-configuration.html" rel="noopener noreferrer"&gt;legacy data access pattern&lt;/a&gt; by Databricks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Delta Lake workloads&lt;/strong&gt; should continue to use supported object storage patterns&lt;/li&gt;
&lt;li&gt;Any Databricks integration should be validated per workspace type, cluster mode, runtime version, IAM path, and governance requirement&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next Validation Metrics
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Current blocker&lt;/strong&gt;: Executor-scale validation requires a Customer-managed VPC workspace (same VPC as FSx for ONTAP). The Databricks-managed VPC workspace was tested with VPC Peering and Instance Profile (2026-05-24) — S3 AP access timed out due to managed VPC egress restrictions. A Customer-managed VPC workspace creation is pending Databricks support ticket resolution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For executor-scale validation (not yet performed):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Object listing latency per executor&lt;/li&gt;
&lt;li&gt;Total objects processed across cluster&lt;/li&gt;
&lt;li&gt;Per-executor success/failure rate&lt;/li&gt;
&lt;li&gt;Throughput per executor&lt;/li&gt;
&lt;li&gt;Retry count and S3 API error rate&lt;/li&gt;
&lt;li&gt;FSx for ONTAP throughput utilization during distributed access&lt;/li&gt;
&lt;li&gt;Cost per processed GB&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Driver-only boto3 success is not sufficient for Spark workloads. The next validation should run boto3 calls from executors using &lt;code&gt;mapPartitions&lt;/code&gt; and compare credential, routing, latency, and error behavior across workers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Executor-scale validation should not only test success/failure. It should capture per-executor latency, retry count, error code, and object count so that routing and concurrency behavior can be reviewed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benchmark run guidance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cold run: at least 1 (first access after cluster start, no metadata cache)&lt;/li&gt;
&lt;li&gt;Warm metadata run: at least 1 (after initial listing populates metadata cache)&lt;/li&gt;
&lt;li&gt;Repeated run: at least 3 (steady-state measurement)&lt;/li&gt;
&lt;li&gt;Report: p50, p90, p95, p99 latency, plus average, min, max, and outliers&lt;/li&gt;
&lt;li&gt;Include: object count, average object size, prefix depth, concurrent executor count&lt;/li&gt;
&lt;li&gt;Include: FSx for ONTAP throughput utilization during test window&lt;/li&gt;
&lt;li&gt;Note: S3 AP via FSx for ONTAP may exhibit metadata warm-up effects and prefix layout sensitivity. Cold vs warm differences should be documented explicitly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Additional FSx for ONTAP metrics to capture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FSx for ONTAP throughput utilization (% of provisioned capacity)&lt;/li&gt;
&lt;li&gt;FSx for ONTAP CPU utilization&lt;/li&gt;
&lt;li&gt;Network throughput (inbound/outbound)&lt;/li&gt;
&lt;li&gt;S3 API request count by operation (List, Get, Head)&lt;/li&gt;
&lt;li&gt;File count per prefix&lt;/li&gt;
&lt;li&gt;Average object size&lt;/li&gt;
&lt;li&gt;NFS/SMB latency during concurrent S3 API reads (contention indicator)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expected output format (JSONL per executor):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"executor_host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ip-10-0-xx-yy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"partition_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list_objects_v2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"latency_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;183&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"objects_seen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"error_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Adoption Success Metrics
&lt;/h2&gt;

&lt;p&gt;For a controlled Databricks + FSx for ONTAP S3 AP PoC, define success criteria beyond technical pass/fail:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Baseline metrics (capture before validation):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Average search/access time (minutes) for target documents&lt;/li&gt;
&lt;li&gt;Monthly document access count via current path&lt;/li&gt;
&lt;li&gt;Current copy pipeline runtime (if applicable)&lt;/li&gt;
&lt;li&gt;Current data freshness lag (hours)&lt;/li&gt;
&lt;li&gt;Current support ticket count related to data access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;PoC outcome metrics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Number of target datasets evaluated&lt;/li&gt;
&lt;li&gt;Number of successful read operations&lt;/li&gt;
&lt;li&gt;Number of governance exceptions required&lt;/li&gt;
&lt;li&gt;Time to first successful access&lt;/li&gt;
&lt;li&gt;Number of support issues raised&lt;/li&gt;
&lt;li&gt;Whether the customer selected Athena, Databricks, or another engine after validation&lt;/li&gt;
&lt;li&gt;Decision outcome: proceed / adjust / stop&lt;/li&gt;
&lt;li&gt;Time saved by early boundary identification (vs discovering in production)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stop criteria:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No measurable business value after validation period&lt;/li&gt;
&lt;li&gt;Governance exception required for production path with no compensating control available&lt;/li&gt;
&lt;li&gt;Executor-scale validation fails with unacceptable error rate (define threshold before PoC)&lt;/li&gt;
&lt;li&gt;FSx for ONTAP workload impact exceeds approved threshold (e.g., throughput utilization &amp;gt; 80%)&lt;/li&gt;
&lt;li&gt;Vendor confirmation indicates unsupported path with no roadmap commitment&lt;/li&gt;
&lt;li&gt;Security review rejects the access path without remediation option&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Series Evaluation Criteria
&lt;/h2&gt;

&lt;p&gt;Across this series, each engine is evaluated by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read-path compatibility&lt;/li&gt;
&lt;li&gt;Write-path compatibility&lt;/li&gt;
&lt;li&gt;Governance model&lt;/li&gt;
&lt;li&gt;Operational impact&lt;/li&gt;
&lt;li&gt;Performance evidence&lt;/li&gt;
&lt;li&gt;Production readiness gap&lt;/li&gt;
&lt;li&gt;Best-fit use case&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Well-Architected Mapping
&lt;/h3&gt;

&lt;p&gt;These criteria align with the &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/analytics-lens/analytics-lens.html" rel="noopener noreferrer"&gt;AWS Well-Architected Data Analytics Lens&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Evaluation focus in this series&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Governance model, IAM/AP policy, audit evidence, session policy behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reliability&lt;/td&gt;
&lt;td&gt;Failure modes, rollback path, support case evidence, DR considerations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance Efficiency&lt;/td&gt;
&lt;td&gt;Throughput, executor-scale behavior, FSx for ONTAP utilization, latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost Optimization&lt;/td&gt;
&lt;td&gt;Engine-specific cost model, idle cost, cost per processed GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational Excellence&lt;/td&gt;
&lt;td&gt;Runbook, evidence template, support packet, monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Business Value of Negative Validation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Negative validation is not failure. It is risk reduction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A failed integration path can still create value when it prevents the wrong production architecture. This validation helps teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoid assuming&lt;/strong&gt; S3 API compatibility equals platform governance compatibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the right engine&lt;/strong&gt; for the right access pattern (Athena for read-only SQL, Databricks for lakehouse/ML)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identify early&lt;/strong&gt; when vendor confirmation is required before committing architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce time&lt;/strong&gt; spent on ambiguous troubleshooting by providing reproducible evidence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prevent wasted PoC investment&lt;/strong&gt; by documenting boundaries before production design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable informed conversations&lt;/strong&gt; with vendors, partners, and security reviewers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For enterprise customers, early boundary identification can save weeks of engineering time and prevent costly architecture rework after production deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Series index:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;Part 1: Athena — Query NAS Data In Place&lt;/a&gt; (validated read-oriented path, 9/9 negative tests pass)&lt;/li&gt;
&lt;li&gt;Part 2: Databricks (this article) — session policy deep dive&lt;/li&gt;
&lt;li&gt;Part 3: Snowflake — LIST Works, SELECT Doesn't (same session policy pattern)&lt;/li&gt;
&lt;li&gt;Part 4: DuckDB Lambda — lightweight serverless analytics validation&lt;/li&gt;
&lt;li&gt;Part 5: EMR Spark — read-write ETL pipeline (coming soon)&lt;/li&gt;
&lt;li&gt;Part 6: Redshift Spectrum — DWH meets NAS data (coming soon)&lt;/li&gt;
&lt;li&gt;Part 7: Trino — open-source SQL on NAS data (coming soon)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Open items:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support cases: Waiting for Databricks response on session policy and NFS mount questions&lt;/li&gt;
&lt;li&gt;FUSE NFS client: Investigating whether a user-space NFS client can bypass the runtime restriction&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution on FUSE/user-space NFS&lt;/strong&gt;: FUSE or user-space NFS clients should be treated as experimental only. They require separate validation for POSIX semantics, caching behavior, consistency, performance, failure recovery, and vendor supportability. Do not treat user-space NFS RPC success as a production workaround.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Related series by the same author&lt;/strong&gt; (FSx for ONTAP S3 Access Points with other AWS services):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/building-an-agentic-access-aware-rag-system-with-amazon-fsx-for-netapp-ontap-s3-vectors-and-s3-2b86"&gt;Building an Agentic Access-Aware RAG System with Amazon FSx for NetApp ONTAP, S3 Vectors, and S3 Access Points&lt;/a&gt; — Bedrock Knowledge Bases + permission-aware retrieval (&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-Agentic-Access-Aware-RAG" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/fsx-for-ontap-s3-access-points-as-a-serverless-automation-boundary-ai-data-pipelines-ili"&gt;FSx for ONTAP S3 Access Points as a Serverless Automation Boundary — AI Data Pipelines, Volume-Level SnapMirror DR, and Capacity Guardrails&lt;/a&gt; — Lambda, Bedrock, SageMaker, 17 industry use cases (&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/smart-routing-transfer-family-ingestion-and-voice-chat-permission-aware-rag-v42-3iml"&gt;Smart Routing, Transfer Family Ingestion, and Voice Chat — Permission-Aware RAG v4.2&lt;/a&gt; — Transfer Family + SFTP ingestion for RAG pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ONTAP S3 Multiprotocol vs FSx for ONTAP S3 Access Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.netapp.com/us-en/ontap/s3-multiprotocol/" rel="noopener noreferrer"&gt;ONTAP S3 multiprotocol&lt;/a&gt; (ONTAP 9.12.1+): S3 NAS bucket model on ONTAP SVM, enabling S3 clients to access NAS data directly on the ONTAP cluster&lt;/li&gt;
&lt;li&gt;FSx for ONTAP S3 Access Points: AWS-managed S3 Access Point endpoint attached to FSx for ONTAP volume, integrating with AWS IAM, VPC, and S3-compatible services&lt;/li&gt;
&lt;li&gt;Both expose NAS data via S3-style access, but the authorization path, service integration, and operational model differ. This article focuses on FSx for ONTAP S3 Access Points.&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/12/amazon-fsx-netapp-ontap-s3-access/" rel="noopener noreferrer"&gt;AWS What's New: Amazon FSx for NetApp ONTAP now supports Amazon S3 access (Dec 2, 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-access-points.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 Access Points documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;AWS Tutorial: Query files with Athena&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.databricks.com/en/security/network/classic/customer-managed-vpc.html" rel="noopener noreferrer"&gt;Databricks Customer-managed VPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.databricks.com/en/connect/storage/tutorial-s3-instance-profile.html" rel="noopener noreferrer"&gt;Databricks Instance Profiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.databricks.com/en/connect/unity-catalog/storage-credentials.html" rel="noopener noreferrer"&gt;Databricks Unity Catalog External Locations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This article is part of the "FSx for ONTAP S3 Access Points × Lakehouse Deep Dive" series. All tests were performed on a real AWS environment with FSx for ONTAP (ONTAP 9.17.1, ap-northeast-1) and Databricks (DBR 17.3 LTS, Premium tier) in May 2026.&lt;/em&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope reminder&lt;/strong&gt;: This article documents observed behavior in one validated environment. It does not validate production readiness, distributed executor-scale processing, or all Databricks runtime versions. Terminology uses "observed in this environment" rather than "unsupported" or "incompatible" — platform behavior may change with future updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Future updates&lt;/strong&gt;: If Databricks platform behavior changes or vendor confirmation becomes available, this article should be updated with the new validation result rather than treated as a permanent compatibility statement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article is an independent validation report and does not represent Databricks, AWS, or NetApp official guidance. Product behavior, support status, and platform capabilities may change. Always validate in your own environment and consult vendor documentation and support channels.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>databricks</category>
      <category>amazonfsxfornetappontap</category>
      <category>lakehouse</category>
    </item>
    <item>
      <title>FSx for ONTAP S3 Access Points Lakehouse — What Works, What Doesn't, and Why</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Sun, 24 May 2026 07:24:34 +0000</pubDate>
      <link>https://forem.com/aws-builders/fsx-for-ontap-s3-access-points-x-lakehouse-what-works-what-doesnt-and-why-1jo3</link>
      <guid>https://forem.com/aws-builders/fsx-for-ontap-s3-access-points-x-lakehouse-what-works-what-doesnt-and-why-1jo3</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Amazon FSx for ONTAP S3 Access Points let you access NAS file data through S3-compatible APIs — without first copying source files to S3.&lt;/p&gt;

&lt;p&gt;I tested multiple analytics, AI/ML, and lakehouse access patterns across AWS-native services, open-source engines, and third-party platforms. The results fall into four categories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verified in this series ✅&lt;/th&gt;
&lt;th&gt;Candidate (AWS-documented) 🔎&lt;/th&gt;
&lt;th&gt;Partially resolved, not production-ready ⚠️&lt;/th&gt;
&lt;th&gt;Not suitable for this path ❌&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Athena, Glue, EMR Spark, Redshift Spectrum, DuckDB Lambda, Trino, Snowflake (with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Bedrock KB, Lake Formation, Quick&lt;/td&gt;
&lt;td&gt;Databricks UC (session policy partially resolved; UC table creation and directory listing still blocked)&lt;/td&gt;
&lt;td&gt;Delta / Iceberg / Hudi transactional write paths&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The pattern&lt;/strong&gt;: Read-oriented analytics and flat-file writes (such as Parquet append) worked reliably in my validation environment. Transactional table-format write paths failed in this validation because they require commit semantics (atomic rename, conditional metadata update) that were not satisfied through the FSx S3 AP path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Validation Vocabulary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verified&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Worked in my test environment with evidence in &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack" rel="noopener noreferrer"&gt;verification-pack/&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Candidate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS-documented or related-series path that still requires workload-specific validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Blocked&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Failed due to integration-layer behavior observed in validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Not suitable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Failed because required table-format semantics were unavailable or incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When this article says "Verified," it means the behavior was observed in my test environment and evidence is available. It does not mean production certification or vendor support guarantee.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Enterprise organizations store petabytes of file data on NAS (NFS/SMB). To analyze this data with modern tools, they typically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy data from NAS to S3 (ETL pipeline)&lt;/li&gt;
&lt;li&gt;Register in a catalog (Glue, Unity Catalog)&lt;/li&gt;
&lt;li&gt;Query with analytics platform&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;FSx for ONTAP S3 Access Points eliminate step 1. The same files accessible via NFS/SMB are now queryable via S3 API — zero source-file movement, zero sync pipeline, zero duplicate storage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: NFS/SMB → [ETL Copy] → S3 → Analytics Platform
After:  NFS/SMB ←→ FSx for ONTAP ←→ S3 Access Point → Analytics Platform
                    (same data, same volume)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note for regulated workloads&lt;/strong&gt;: "Zero data movement" means source files do not need to be copied from FSx for ONTAP to S3 for the tested access paths. However, metadata, query results, logs, embeddings, temporary files, and derived datasets may still be created by the consuming service. See Note for Regulated Workloads below.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  From File Access to AI-Ready Data
&lt;/h2&gt;

&lt;p&gt;Eliminating the copy pipeline is step one. The real business value comes from what you do next — turning raw file data into &lt;strong&gt;AI-ready data products&lt;/strong&gt; that drive business outcomes.&lt;/p&gt;

&lt;p&gt;The engines validated in this series form a &lt;strong&gt;multi-engine data product journey&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSx for ONTAP (source of truth)
  ↓ S3 Access Point (zero-copy access)
  ├── Athena / Redshift Spectrum → Ad-hoc discovery, data profiling
  ├── Glue / EMR Spark → ETL, curated Parquet/Iceberg datasets
  ├── DuckDB Lambda → Lightweight validation, cost-optimized queries
  ├── Snowflake External Table → Governed analytics, Cortex AI (summarize, RAG, sentiment)
  ├── Lake Formation → Fine-grained access control (column/row/tag)
  └── Databricks (via DataSync → S3) → ML training, feature engineering, Mosaic AI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The key insight&lt;/strong&gt;: You don't need to pick one engine. Each platform excels at a different stage of the data product lifecycle:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Best engine&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discover&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Athena, DuckDB Lambda&lt;/td&gt;
&lt;td&gt;Cheapest way to explore what's in your NAS data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Profile &amp;amp; validate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Glue Crawler, DuckDB&lt;/td&gt;
&lt;td&gt;Schema discovery, data quality checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transform &amp;amp; curate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Glue ETL, EMR Spark&lt;/td&gt;
&lt;td&gt;Medallion architecture, write curated Parquet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Govern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lake Formation, Snowflake&lt;/td&gt;
&lt;td&gt;Column/row/tag access control, governance tags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analyze &amp;amp; share&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Snowflake, Redshift Spectrum&lt;/td&gt;
&lt;td&gt;Governed analytics, data sharing, Cortex AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Train &amp;amp; predict&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Databricks, EMR&lt;/td&gt;
&lt;td&gt;ML training, feature store, model serving&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is not "pick one platform" — it's "use the right engine for each stage, with FSx for ONTAP as the single source of truth."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Zero-ETL design principle&lt;/strong&gt;: The goal is not "zero processing" — it's "no hand-built copy pipelines, no duplicate storage management, no stale data." Where a platform requires data in S3 (Databricks UC, Delta/Iceberg writes), use DataSync as a managed bridge — not a custom ETL pipeline.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Security Model
&lt;/h2&gt;

&lt;p&gt;Every request to FSx for ONTAP S3 Access Points must pass two authorization layers (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-manage-access-fsxn.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;S3-side authorization&lt;/strong&gt;: IAM identity policy, S3 Access Point policy, VPC endpoint policy (if applicable), SCP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FSx-side authorization&lt;/strong&gt;: Associated UNIX or Windows file system user permissions on the underlying volume&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both layers must permit the request. A permissive IAM policy does not override restrictive file system permissions, and vice versa.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Compatibility Map
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verified (Evidence in verification-pack)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;Cost/Query&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Athena&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless SQL via Glue Catalog&lt;/td&gt;
&lt;td&gt;54.8 MB/s (5M rows in 2.2s)&lt;/td&gt;
&lt;td&gt;~$0.0005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DuckDB Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In-process analytics (arm64)&lt;/td&gt;
&lt;td&gt;10K rows in 452ms (warm)&lt;/td&gt;
&lt;td&gt;~$0.00001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMR Spark&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distributed Spark SQL&lt;/td&gt;
&lt;td&gt;10K rows read+write in 16s&lt;/td&gt;
&lt;td&gt;~$0.001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redshift Spectrum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DWH + external data JOIN&lt;/td&gt;
&lt;td&gt;5M rows in 4.3s&lt;/td&gt;
&lt;td&gt;~$0.005&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trino&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-source distributed SQL&lt;/td&gt;
&lt;td&gt;5M rows in 1.5s&lt;/td&gt;
&lt;td&gt;Compute cost only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Glue ETL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PySpark medallion pipeline&lt;/td&gt;
&lt;td&gt;10K rows transform in 64s&lt;/td&gt;
&lt;td&gt;~$0.02&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Candidate (AWS-documented, requires workload validation)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lake Formation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Governance overlay&lt;/td&gt;
&lt;td&gt;Table/column-level access behavior observed; production workload validation needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bedrock KB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RAG document ingestion&lt;/td&gt;
&lt;td&gt;Per &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-build-rag-with-bedrock.html" rel="noopener noreferrer"&gt;AWS tutorial&lt;/a&gt;; permission-aware retrieval requires separate validation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Blocked in Validation (Third-Party Platforms)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;th&gt;Workaround&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Databricks&lt;/strong&gt; (Unity Catalog)&lt;/td&gt;
&lt;td&gt;Subdirectory ls → AccessDenied; CREATE TABLE → fails&lt;/td&gt;
&lt;td&gt;Session policy partially resolved with &lt;code&gt;access_point&lt;/code&gt; field; prefix-level listing and UC table creation still blocked&lt;/td&gt;
&lt;td&gt;Explicit-path spark.read works but without UC table registration, governance features (lineage, tags, fine-grained access) cannot be applied; Instance Profile + boto3 for full access (bypasses UC entirely)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Snowflake&lt;/strong&gt; (External Stage)&lt;/td&gt;
&lt;td&gt;✅ Works with &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; stage parameter resolves session policy for GetObject&lt;/td&gt;
&lt;td&gt;Full zero-copy analytics: SELECT, External Table, Directory Table, Cortex AI (summarize/translate/sentiment), Governance Tags, Row/Column policies — all verified. See [Part 3]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Databricks update (2026-05-24)&lt;/strong&gt;: Setting the &lt;code&gt;access_point&lt;/code&gt; field on the UC External Location partially resolves the session policy issue. Top-level &lt;code&gt;dbutils.fs.ls&lt;/code&gt;, &lt;code&gt;dbutils.fs.head&lt;/code&gt;, and &lt;code&gt;spark.read&lt;/code&gt; with explicit file paths now succeed. However, &lt;strong&gt;UC table creation (&lt;code&gt;CREATE TABLE LOCATION&lt;/code&gt;) fails&lt;/strong&gt;, subdirectory listing is blocked, and write operations are denied. Without UC table registration, Unity Catalog governance features — lineage tracking, fine-grained access control, governance tags, and audit — cannot be applied to the data. This means the data is technically readable but not governable through UC. Support case active — awaiting guidance on table creation and prefix-level access.&lt;/p&gt;

&lt;p&gt;Support cases filed with both vendors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not Suitable for This Path (Table Format Constraints)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Write Operation&lt;/th&gt;
&lt;th&gt;Why It Failed in Validation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delta Lake&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;INSERT/MERGE/VACUUM&lt;/td&gt;
&lt;td&gt;Requires conditional writes (&lt;code&gt;If-None-Match&lt;/code&gt;) for &lt;code&gt;_delta_log/&lt;/code&gt; commit — FSx for ONTAP S3 AP returns 501 Not Implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apache Iceberg&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CREATE TABLE/INSERT&lt;/td&gt;
&lt;td&gt;S3FileIO metadata write requires conditional writes for atomic commit — same root cause as Delta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apache Hudi&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Upsert/Compaction&lt;/td&gt;
&lt;td&gt;Timeline commit requires atomic rename — not available on FSx for ONTAP S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important distinction (read vs write)&lt;/strong&gt;: The failures above are for &lt;strong&gt;write&lt;/strong&gt; operations only. &lt;strong&gt;Reading&lt;/strong&gt; pre-existing Iceberg/Delta tables (where metadata and data files already exist on storage) is theoretically possible via GetObject — but has not been validated on FSx for ONTAP S3 AP. If you have Iceberg tables written to standard S3 (via EMR/Glue), those can be registered in Glue Data Catalog and queried alongside FSx for ONTAP external tables from the same Athena/Redshift session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this validation, transactional table writes failed because the tested engines required conditional writes (&lt;code&gt;If-None-Match&lt;/code&gt; / put-if-absent) that FSx for ONTAP S3 AP does not support (returns 501 Not Implemented). AWS feature request submitted (May 2026). See &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/access-points-for-fsxn-object-api-support.html" rel="noopener noreferrer"&gt;API support documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What DOES work for writes&lt;/strong&gt;: Flat Parquet/CSV append via PutObject (Athena CTAS, Glue ETL write-back, EMR Spark write, DuckDB COPY TO).&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark Methodology
&lt;/h2&gt;

&lt;p&gt;All benchmark numbers should be read with the following context:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP deployment type&lt;/td&gt;
&lt;td&gt;Single-AZ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provisioned throughput&lt;/td&gt;
&lt;td&gt;128 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;ap-northeast-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dataset shape&lt;/td&gt;
&lt;td&gt;10K rows (250 KB) and 5M rows (103 MB), single Parquet file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run type&lt;/td&gt;
&lt;td&gt;Warm (unless noted as cold start)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network path&lt;/td&gt;
&lt;td&gt;Internet-origin AP (no VPC attachment for managed services)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Future benchmark runs will also capture: prefix depth, file count per prefix, average object size, p50/p90/p95/p99 latency where available, and cold/warm/repeated run count.&lt;/p&gt;

&lt;p&gt;FSx S3 AP latency is in the tens of milliseconds range, and throughput depends on the file system's provisioned throughput capacity (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/accessing-data-via-s3-access-points.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;). These benchmarks are sizing references from one test environment, not service limits or guarantees.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Decision Guide
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q: Do you need to WRITE transactional tables (Delta/Iceberg)?
  → Yes: Use native S3 for write path; FSx S3 AP for read-only source data
  → No: FSx S3 AP can handle the read-oriented and flat-file write patterns validated in this series

Q: Do you need sub-millisecond latency or unlimited concurrency?
  → Yes: Use native S3
  → No: FSx S3 AP (tens of ms, provisioned throughput)

Q: Do you have existing NAS data you want to analyze?
  → Yes: FSx S3 AP eliminates the copy pipeline
  → No: Native S3 may be simpler

Q: Do you need NFS/SMB access alongside S3 analytics?
  → Yes: FSx S3 AP (multi-protocol on same data)
  → No: Evaluate based on above
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Decision Criteria
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scale&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business metric improves (freshness, cost, time-to-insight)&lt;/li&gt;
&lt;li&gt;Governance path is approved&lt;/li&gt;
&lt;li&gt;Performance impact is within threshold&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Adjust&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Engine works but governance or performance needs redesign&lt;/li&gt;
&lt;li&gt;Staging to native S3 is required for write path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stop&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transactional table write semantics are mandatory on the same path&lt;/li&gt;
&lt;li&gt;Vendor session policy blocks production path with no approved workaround&lt;/li&gt;
&lt;li&gt;Security owner rejects the access model&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Business Value Hypotheses
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Business issue&lt;/th&gt;
&lt;th&gt;Baseline metric&lt;/th&gt;
&lt;th&gt;Expected value&lt;/th&gt;
&lt;th&gt;Validation path&lt;/th&gt;
&lt;th&gt;Decision owner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NAS analytics requires nightly copy to S3&lt;/td&gt;
&lt;td&gt;Copy pipeline runtime, freshness lag&lt;/td&gt;
&lt;td&gt;Reduce data freshness lag to near-zero&lt;/td&gt;
&lt;td&gt;Athena / Glue / EMR direct query&lt;/td&gt;
&lt;td&gt;Data platform owner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise documents are hard to search&lt;/td&gt;
&lt;td&gt;Avg search time per user&lt;/td&gt;
&lt;td&gt;Faster document discovery&lt;/td&gt;
&lt;td&gt;Bedrock KB / permission-aware RAG&lt;/td&gt;
&lt;td&gt;Information management owner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ETL pipeline duplicates storage&lt;/td&gt;
&lt;td&gt;Duplicate storage cost&lt;/td&gt;
&lt;td&gt;Lower copy and storage overhead&lt;/td&gt;
&lt;td&gt;Glue / EMR write-back to same volume&lt;/td&gt;
&lt;td&gt;Storage / FinOps owner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform selection is unclear&lt;/td&gt;
&lt;td&gt;Weeks spent on PoC&lt;/td&gt;
&lt;td&gt;Faster architecture decision&lt;/td&gt;
&lt;td&gt;This compatibility map&lt;/td&gt;
&lt;td&gt;Architecture lead&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Partner Offer Paths
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Customer need&lt;/th&gt;
&lt;th&gt;Suggested offer&lt;/th&gt;
&lt;th&gt;Exit decision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Query NAS data without copy&lt;/td&gt;
&lt;td&gt;Athena / Redshift Spectrum validation pilot&lt;/td&gt;
&lt;td&gt;Scale / adjust / stop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ETL from NAS to curated Parquet&lt;/td&gt;
&lt;td&gt;Glue or EMR Serverless validation sprint&lt;/td&gt;
&lt;td&gt;Production design / stage to S3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG over enterprise documents&lt;/td&gt;
&lt;td&gt;Bedrock KB / permission-aware RAG assessment&lt;/td&gt;
&lt;td&gt;Proceed only with authorization model validated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databricks lakehouse integration&lt;/td&gt;
&lt;td&gt;UC External Location with &lt;code&gt;access_point&lt;/code&gt; field for read; staging to native S3 for Delta write&lt;/td&gt;
&lt;td&gt;File-level read works under UC; subdirectory listing and table creation pending vendor resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transactional table write&lt;/td&gt;
&lt;td&gt;Native S3 table storage design&lt;/td&gt;
&lt;td&gt;FSx S3 AP as source, not table log storage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The purpose of these offers is not to force every workload onto FSx S3 AP, but to quickly identify the right access path, the right engine, and the right stop condition.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Technical Findings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Internet-Origin AP Required for Managed Services
&lt;/h3&gt;

&lt;p&gt;In this validation, managed service paths (Athena, Glue, Redshift Spectrum, Bedrock) required internet-origin access points because the service access path did not originate from the customer VPC. Validate this per service, region, and network configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Parquet Timestamp Compatibility
&lt;/h3&gt;

&lt;p&gt;pandas and DuckDB generate Parquet with nanosecond timestamps by default. Spark (Glue, EMR) cannot read these files. Always use microsecond resolution for cross-engine compatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. EMRFS vs S3A
&lt;/h3&gt;

&lt;p&gt;EMR's EMRFS (&lt;code&gt;s3://&lt;/code&gt;) natively supports S3 AP aliases. The S3A FileSystem (&lt;code&gt;s3a://&lt;/code&gt;) does NOT work with AP aliases (URL parsing error). Use &lt;code&gt;s3://&lt;/code&gt; prefix in EMR.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. DuckDB httpfs Configuration
&lt;/h3&gt;

&lt;p&gt;DuckDB requires &lt;code&gt;s3_url_style = 'path'&lt;/code&gt; and explicit &lt;code&gt;s3_endpoint&lt;/code&gt; to work with S3 AP aliases. In Lambda, also set &lt;code&gt;home_directory = '/tmp'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Trino Hive Connector
&lt;/h3&gt;

&lt;p&gt;Trino requires &lt;code&gt;hive.s3.path-style-access=true&lt;/code&gt; and explicit &lt;code&gt;hive.s3.endpoint&lt;/code&gt; to resolve S3 AP aliases. Same pattern as DuckDB — path-style access is the key.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. S3 Gateway Endpoint Routing
&lt;/h3&gt;

&lt;p&gt;VPC-attached compute (Lambda in VPC, EC2) may experience timeouts when accessing FSx S3 AP through an S3 Gateway VPC Endpoint. The FSx S3 AP alias resolves to &lt;code&gt;s3-r-w.&amp;lt;region&amp;gt;.amazonaws.com&lt;/code&gt; which may not route correctly through the Gateway endpoint. Workaround: use NAT Gateway or place compute outside VPC. See &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/blob/main/docs/en/fsxn-s3ap-networking.md" rel="noopener noreferrer"&gt;FSx S3 AP Networking Considerations&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Session Policy Is the Common Blocker for Third-Party Platforms
&lt;/h3&gt;

&lt;p&gt;The session policy issue is not unique to one vendor in this validation. It may affect any analytics platform that applies restrictive AssumeRole session policies designed around standard S3 bucket ARN patterns. AWS-native services work because they use IAM roles directly without intermediary session policies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Note for Regulated Workloads
&lt;/h2&gt;

&lt;p&gt;"Zero data movement" means source files do not need to be copied from FSx for ONTAP to S3 for the tested access paths. However, metadata, query results, logs, embeddings, temporary files, and derived datasets may still be created by the consuming service.&lt;/p&gt;

&lt;p&gt;For regulated workloads, validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data classification of source and derived data&lt;/li&gt;
&lt;li&gt;Derived data location (query results, embeddings, temp files)&lt;/li&gt;
&lt;li&gt;Encryption and key ownership at each layer&lt;/li&gt;
&lt;li&gt;Audit log coverage (CloudTrail, platform logs, ONTAP audit)&lt;/li&gt;
&lt;li&gt;Retention and deletion policy&lt;/li&gt;
&lt;li&gt;Approval owner and expiration date&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bedrock KB is a strong candidate for RAG over NAS documents, but regulated use cases must validate permission-aware retrieval, data classification, human review requirements, and residual risk acceptance before production use.&lt;/p&gt;

&lt;p&gt;For regulated workloads, do not start a PoC until the data owner, security owner, and platform owner agree on the allowed prefixes, derived data locations, logging scope, rollback plan, and approval expiration date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assurance artifacts to prepare:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Non-technical overview for stakeholders&lt;/li&gt;
&lt;li&gt;Data flow diagram (source → AP → service → output)&lt;/li&gt;
&lt;li&gt;Access control summary (dual-layer authorization)&lt;/li&gt;
&lt;li&gt;Audit evidence summary&lt;/li&gt;
&lt;li&gt;Rollback plan&lt;/li&gt;
&lt;li&gt;Residual risk register&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Store these artifacts with an approval ID, owner, review date, and expiration date so the PoC decision can be audited later.&lt;/p&gt;




&lt;h2&gt;
  
  
  GenAI / RAG Evaluation Metrics
&lt;/h2&gt;

&lt;p&gt;For GenAI and RAG workloads on FSx for ONTAP data, measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieval accuracy (relevant documents returned)&lt;/li&gt;
&lt;li&gt;Permission-aware retrieval pass rate (unauthorized documents NOT returned)&lt;/li&gt;
&lt;li&gt;Hallucination reduction vs baseline&lt;/li&gt;
&lt;li&gt;Data freshness lag (NFS write → S3 AP availability)&lt;/li&gt;
&lt;li&gt;Human review workload&lt;/li&gt;
&lt;li&gt;User time saved vs previous search method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with read-only, permission-aware, human-review-attached PoC before production deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Series Index
&lt;/h2&gt;

&lt;p&gt;This is the series overview for "FSx for ONTAP S3 Access Points × Lakehouse Deep Dive."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Athena — Query NAS Data In Place&lt;/td&gt;
&lt;td&gt;✅ Published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh"&gt;dev.to&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Databricks — A Layer-by-Layer Validation of Observed Boundaries&lt;/td&gt;
&lt;td&gt;✅ Published&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Snowflake — From 'Access Denied' to Working External Tables&lt;/td&gt;
&lt;td&gt;✅ Resolved&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DuckDB Lambda — Serverless for $0.00001/query&lt;/td&gt;
&lt;td&gt;Ready to publish&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EMR Spark — Read-Write ETL Pipeline&lt;/td&gt;
&lt;td&gt;Ready to publish&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Redshift Spectrum — DWH Meets NAS Data&lt;/td&gt;
&lt;td&gt;Coming soon&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trino — Open-Source SQL on NAS Data&lt;/td&gt;
&lt;td&gt;Coming soon&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;This article (Overview — What Works and What Doesn't)&lt;/td&gt;
&lt;td&gt;Ready to publish&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This overview article can be published as the final "summary" post in the series, or as a standalone reference.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Update to Part 1 (Athena)
&lt;/h3&gt;

&lt;p&gt;Since Part 1 was published, additional verification has been completed and published as a v1.1 update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CTAS write-back&lt;/strong&gt;: Verified as WORKING (3.7s, writes Parquet back to FSxN S3 AP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partition projection&lt;/strong&gt;: Verified with Hive-style partitioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark&lt;/strong&gt;: 54.8 MB/s peak throughput (5M rows, 103 MB scan in 2.2s)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9/9 negative tests pass&lt;/strong&gt;: Unauthorized access correctly denied&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Yoshiki0705/fsxn-lakehouse-integrations.git
&lt;span class="nb"&gt;cd &lt;/span&gt;fsxn-lakehouse-integrations

&lt;span class="c"&gt;# Deploy base infrastructure&lt;/span&gt;
aws cloudformation deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; shared/cloudformation/fsxn-s3ap-base.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; fsxn-lakehouse-base &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_IAM

&lt;span class="c"&gt;# Validate connectivity&lt;/span&gt;
python shared/scripts/validate-access.py &lt;span class="nt"&gt;--access-point-alias&lt;/span&gt; &amp;lt;your-ap-alias&amp;gt;

&lt;span class="c"&gt;# Choose your platform: integrations/athena/, integrations/duckdb/, etc.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each integration directory includes a README, CloudFormation template, deployment script, and sample queries.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Databricks UC + &lt;code&gt;access_point&lt;/code&gt; field — partial success confirmed (2026-05-24); awaiting vendor guidance on subdirectory listing and table creation&lt;/li&gt;
&lt;li&gt;Snowflake &lt;code&gt;AWS_ACCESS_POINT_ARN&lt;/code&gt; — &lt;strong&gt;resolved&lt;/strong&gt; (2026-05-24); SELECT and External Table work with stage parameter&lt;/li&gt;
&lt;li&gt;Apache Iceberg community engagement (S3FileIO + AP alias support)&lt;/li&gt;
&lt;li&gt;ONTAP feature quantification (dedup ratio, snapshot RTO) — resolved (DNS/AD orphan config removed, S3 AP recovered 2026-05-24)&lt;/li&gt;
&lt;li&gt;Redshift Spectrum and Trino deep-dive articles&lt;/li&gt;
&lt;li&gt;Customer PoC execution with measured business outcomes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Operational Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  S3 AP Timeout Caused by Orphaned DNS/AD Configuration (2026-05-24)
&lt;/h3&gt;

&lt;p&gt;During this series validation, all S3 APs on one SVM became unresponsive for 7+ days. Root cause: the SVM had DNS servers configured for an AD domain that no longer existed. When the S3 AP backend processes requests on an AD-joined SVM, ONTAP's name-service stack attempts DNS resolution for user-mapping — if DNS is unreachable, requests block until timeout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disabling customer-configured FPolicy did NOT fix the issue&lt;/li&gt;
&lt;li&gt;A separate SVM without DNS/AD worked normally on the same file system&lt;/li&gt;
&lt;li&gt;Removing the orphaned CIFS/DNS configuration restored S3 AP instantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt; Do not leave orphaned DNS/AD configurations on SVMs used for S3 AP access. If AD is decommissioned, clean up &lt;code&gt;vserver cifs&lt;/code&gt; and &lt;code&gt;vserver services dns&lt;/code&gt; settings. See &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/blob/main/docs/en/fsxn-s3ap-networking.md#7-svm-dnsad-configuration-and-s3-ap-availability" rel="noopener noreferrer"&gt;FSx S3 AP Networking — Section 7&lt;/a&gt; for full details.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/accessing-data-via-s3-access-points.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 Access Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/access-points-for-fsxn-object-api-support.html" rel="noopener noreferrer"&gt;Access point API compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-manage-access-fsxn.html" rel="noopener noreferrer"&gt;Managing access point access (dual-layer authorization)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This series is based on hands-on verification, not documentation review. Every "Verified" claim has a corresponding evidence record in the &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/tree/main/verification-pack" rel="noopener noreferrer"&gt;verification-pack/&lt;/a&gt; directory.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This article is an independent validation report and does not represent AWS, NetApp, Databricks, or Snowflake official guidance. Product behavior, support status, and platform capabilities may change. Always validate in your own environment and consult vendor documentation and support channels.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>amazonfsxfornetappontap</category>
      <category>lakehouse</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>From Serverless Patterns to Field-Ready Reference Architecture — FSx for ONTAP S3 Access Points, Phase 13.</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Fri, 22 May 2026 15:50:35 +0000</pubDate>
      <link>https://forem.com/aws-builders/from-serverless-patterns-to-field-ready-reference-architecture-fsx-for-ontap-s3-access-points-dhj</link>
      <guid>https://forem.com/aws-builders/from-serverless-patterns-to-field-ready-reference-architecture-fsx-for-ontap-s3-access-points-dhj</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Previous phases showed what can be built.&lt;br&gt;
Phase 13 shows how to evaluate, govern, deliver, and operate it.&lt;/p&gt;

&lt;p&gt;In this context, &lt;strong&gt;field-ready&lt;/strong&gt; means that the repository now includes not only deployable patterns, but also the guidance needed to evaluate, govern, size, deliver, and operate them in customer-facing scenarios.&lt;/p&gt;

&lt;p&gt;The repository now includes success metrics, readiness guidance, governance controls, and benchmark-backed sizing references.&lt;/p&gt;

&lt;p&gt;📊 &lt;strong&gt;Stats&lt;/strong&gt;: 17 industry use cases + event-driven FPolicy + 6 FlexCache/FlexClone patterns | 1,499+ tests | 126 test files | 6 deployed CloudFormation stacks | Python 3.12 + SAM Transform&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These stats represent repository validation coverage and sample stack verification, not a blanket production certification. The point of these numbers is not volume itself, but evidence that the repository now covers implementation, validation, delivery, and governance paths.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns" rel="noopener noreferrer"&gt;github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Phase 13 Was Needed
&lt;/h2&gt;

&lt;p&gt;As the repository grew from patterns into a multi-use-case library, the main question changed. It was no longer only "Can this be implemented?" but "How should teams evaluate it, govern it, size it, and explain it to customers?" Phase 13 adds that adoption layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;p&gt;Phase 13 is useful for four audiences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Architects&lt;/strong&gt; who need deployment, trigger-mode, and sizing guidance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and governance reviewers&lt;/strong&gt; who need authorization, audit, and human-review controls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partners and SIs&lt;/strong&gt; who need delivery assets for workshops and PoCs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform teams&lt;/strong&gt; who need production-readiness criteria and operational guardrails&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Phase 13 Delivers
&lt;/h2&gt;

&lt;p&gt;Phase 13 has two layers: &lt;strong&gt;technical implementation&lt;/strong&gt; and &lt;strong&gt;adoption guidance&lt;/strong&gt;. The technical layer provides the repository-validated building blocks; the adoption layer explains how to evaluate and deliver them safely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Implementation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FlexClone serverless automation&lt;/strong&gt;: Step Functions orchestrates Snapshot → Clone → ProcessFiles through S3AP → CIFS Share → Notify, with split VPC placement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FlexCache Anycast/DR and Dynamic FlexCache patterns&lt;/strong&gt;: FC1 (Anycast DR with health check + route decision + failover simulation), FC2 (dynamic FlexCache create/delete per job), FC3-FC6 (GenAI RAG, Automotive CAE, Life Sciences, Gaming) — 6 new use case patterns with CloudFormation templates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-driven and replay-safe processing&lt;/strong&gt;: FPolicy-based ingestion (not native S3 bucket notifications), replay storm testing (1000+ events), audit-oriented lineage (v2 fields + S3 Object Lock), protobuf wire validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational visibility&lt;/strong&gt;: Split-path S3AP monitoring, cost dashboard (Metrics Math), benchmark results (p50/p90/p99 + concurrent access)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adoption Guidance
&lt;/h3&gt;

&lt;p&gt;A key shift in Phase 13 is outcome-driven evaluation. Each use case now includes Success Metrics structured as Outcome, Metric, and Measurement Method, so teams can define what success means before running the PoC or deploying the pattern. This matters because teams can now align technical patterns with business outcomes before starting implementation, rather than treating deployment success as the PoC goal.&lt;/p&gt;

&lt;p&gt;The deployment profiles and trigger-mode guide also define where teams can safely experiment: start with polling and PoC profiles, add monitoring and governance controls, and move toward production only after exit criteria are met.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start and evaluate&lt;/strong&gt; — helps teams identify the right entry point and define success before deployment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick Start Guide + E2E Demo Script&lt;/li&gt;
&lt;li&gt;Customer Discovery Template&lt;/li&gt;
&lt;li&gt;17 UC Success Metrics (Outcome / Metric / Measurement Method)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decide and design&lt;/strong&gt; — helps architects choose the right trigger mode, deployment profile, authorization model, and sizing assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3AP Authorization Model + Troubleshooting Commands&lt;/li&gt;
&lt;li&gt;Deployment Profiles + Trigger Mode Decision Guide&lt;/li&gt;
&lt;li&gt;S3AP Benchmark Results + Fargate vs EC2 Decision Matrix&lt;/li&gt;
&lt;li&gt;Persistent Store Sizing Calculator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deliver and operate&lt;/strong&gt; — helps partners, platform teams, and reviewers move from PoC to governed operation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Partner/SI Delivery Checklist + PoC Proposal Example + Industry Expansion Guide&lt;/li&gt;
&lt;li&gt;Workshop Guide (1-day structure with participant roles)&lt;/li&gt;
&lt;li&gt;Production Readiness (4-level maturity model with exit criteria)&lt;/li&gt;
&lt;li&gt;Well-Architected 6-Pillar Mapping + Trade-offs&lt;/li&gt;
&lt;li&gt;Governance Checklist + Public Sector Adoption Roadmap&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FlexClone Serverless Pipeline (Technical Highlight)
&lt;/h2&gt;

&lt;p&gt;A Step Functions state machine orchestrates the complete FlexClone lifecycle with split VPC placement:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lambda&lt;/th&gt;
&lt;th&gt;VPC Config&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateSnapshot&lt;/td&gt;
&lt;td&gt;VPC-internal&lt;/td&gt;
&lt;td&gt;ONTAP REST API requires management LIF access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreateFlexClone&lt;/td&gt;
&lt;td&gt;VPC-internal&lt;/td&gt;
&lt;td&gt;ONTAP REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ProcessFiles&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VPC-external&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3AP object access uses a different network path from ONTAP management API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreateCIFSShare&lt;/td&gt;
&lt;td&gt;VPC-internal&lt;/td&gt;
&lt;td&gt;ONTAP REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Industry applications: Media/VFX (render QC), EDA (parallel simulation), Healthcare (dataset branching), Financial (audit copies), DevOps (DB refresh).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live verification&lt;/strong&gt;: Snapshot → Clone → WaitForOnline → Notify completed in &amp;lt; 10 seconds against real FSx for ONTAP.&lt;/p&gt;




&lt;h2&gt;
  
  
  FlexCache Anycast/DR Pattern (FC1)
&lt;/h2&gt;

&lt;p&gt;FlexCache AnyCast/DR provides geographic read distribution and disaster recovery for FSx for ONTAP volumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Health Check Lambda&lt;/strong&gt;: Monitors FlexCache origin and cache volumes via ONTAP REST API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route Decision Lambda&lt;/strong&gt;: Determines optimal read path based on cache health, latency, and availability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failover Simulation&lt;/strong&gt;: Validates route-decision behavior when the origin path or selected cache path is marked unavailable in the sample routing state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB Routing Table&lt;/strong&gt;: Tracks active/standby cache topology&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this sample, "Anycast" refers to application-level routing decisions based on cache health and availability, not a replacement for network-layer anycast design.&lt;/p&gt;

&lt;p&gt;This pattern addresses scenarios where read performance must be distributed across deployment locations, depending on the supported and tested FSx for ONTAP configuration — FlexCache provides read acceleration while the origin volume remains the single source of truth. The origin volume should be treated as the authoritative data source; cache volumes are acceleration paths whose access and route changes should be observable. This also helps governance discussions because teams can reason about where authoritative data resides and how cached access paths are audited.&lt;/p&gt;

&lt;p&gt;This pattern focuses on read-path resilience and cache-aware routing; it does not replace a full DR strategy such as backup, replication, and recovery planning. For regulated environments, failover decisions should also define decision ownership, approval flow, and audit evidence for route changes. Route changes and failover decisions should be logged as audit events so that teams can review who changed the active path, when, and why.&lt;/p&gt;

&lt;p&gt;The business outcome is faster and more resilient read access for distributed teams without requiring a full independent copy of the dataset.&lt;/p&gt;

&lt;p&gt;The repository also includes FC2 (Dynamic FlexCache per-job lifecycle), FC3 (GenAI RAG with permission-aware chunking — connecting back to governance by keeping RAG preprocessing permission-aware), FC4 (Automotive CAE solver output analysis), FC5 (Life Sciences research data classification), and FC6 (Gaming build pipeline asset QC). Each has a deployable CloudFormation template and tests. The FlexCache/FlexClone patterns follow the same outcome-driven structure: each pattern should be evaluated through workload-specific success metrics, not only deployment success. Future updates will extend the same Outcome / Metric / Measurement Method structure to the FlexCache/FlexClone pattern READMEs.&lt;/p&gt;

&lt;p&gt;Full documentation: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/flexcache-anycast-dr" rel="noopener noreferrer"&gt;flexcache-anycast-dr/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  S3AP Benchmark Results (Sizing References)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;These are sizing references from a specific test environment, not service-level guarantees. Validate in your own environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Test environment&lt;/strong&gt;: FSx for ONTAP Single-AZ, 128 MBps throughput, ap-northeast-1.&lt;/p&gt;

&lt;p&gt;S3AP object access was tested from the VPC-external path because ONTAP management API calls and S3AP object access used different network paths in the tested setup.&lt;/p&gt;

&lt;p&gt;The percentile table is based on 20 repeated runs per object size. See the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/s3ap-benchmark-results.md" rel="noopener noreferrer"&gt;full benchmark document&lt;/a&gt; for methodology and raw observations.&lt;/p&gt;

&lt;h3&gt;
  
  
  GetObject Latency (concurrency=1)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;P50&lt;/th&gt;
&lt;th&gt;P90&lt;/th&gt;
&lt;th&gt;P99&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 KB&lt;/td&gt;
&lt;td&gt;35.5 ms&lt;/td&gt;
&lt;td&gt;39.0 ms&lt;/td&gt;
&lt;td&gt;40.2 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 MB&lt;/td&gt;
&lt;td&gt;47.8 ms&lt;/td&gt;
&lt;td&gt;63.3 ms&lt;/td&gt;
&lt;td&gt;92.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 MB&lt;/td&gt;
&lt;td&gt;108.0 ms&lt;/td&gt;
&lt;td&gt;115.8 ms&lt;/td&gt;
&lt;td&gt;134.8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Concurrent Access (1 MB file)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concurrency&lt;/th&gt;
&lt;th&gt;Avg Latency&lt;/th&gt;
&lt;th&gt;Aggregate Throughput&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;63.8 ms&lt;/td&gt;
&lt;td&gt;35.6 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;112.9 ms&lt;/td&gt;
&lt;td&gt;108.9 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;151.7 ms&lt;/td&gt;
&lt;td&gt;137.6 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key finding&lt;/strong&gt;: In this test environment, FSx Throughput Capacity became the bottleneck for parallel access. At 128 MBps provisioned throughput, concurrency=10 reached the observed saturation point. Higher parallelism should be evaluated with a higher FSx throughput configuration. Short-duration aggregate throughput can appear slightly above the provisioned value due to measurement windows, rounding, and burst behavior; sustained throughput should be validated against the provisioned FSx throughput capacity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Range GET&lt;/strong&gt;: Confirmed working in this test environment. Useful for DICOM headers (4KB), GDS/OASIS headers (1KB), SEG-Y trace headers (3.6KB), PDF first-page OCR (100KB).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article uses MB/s; it is equivalent to the MBps notation shown in some AWS console contexts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Full results: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/s3ap-benchmark-results.md" rel="noopener noreferrer"&gt;S3AP Benchmark Results&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Important Architectural Clarifications
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;S3AP is an access boundary, not all S3 bucket semantics.&lt;/strong&gt; FSx for ONTAP S3 Access Points provide an S3-facing access boundary for file data. Data remains on FSx for ONTAP and continues to be accessible through NFS and SMB. Not all bucket-level features or integration patterns apply directly, such as native S3 bucket notifications, lifecycle policies, and versioning. See the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/README.md" rel="noopener noreferrer"&gt;S3AP compatibility notes&lt;/a&gt; for the current tested behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger strategy matters.&lt;/strong&gt; Because native S3AP event notifications are not available, the repository provides POLLING (simplest), EVENT_DRIVEN (FPolicy-based, near-real-time; not native S3 bucket notifications), and HYBRID modes. Default is POLLING.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance depends on FSx sizing.&lt;/strong&gt; S3 API access does not remove the need to size FSx for ONTAP correctly. S3AP, NFS, and SMB access share the provisioned throughput of the same FSx file system. The split-path design separates ONTAP management API access from S3AP object access because they use different network paths in the tested environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authorization is dual-layer.&lt;/strong&gt; Both AWS IAM and ONTAP file system identity must permit the request. S3 API access does not bypass ONTAP file-system permissions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Governance and Responsible AI
&lt;/h2&gt;

&lt;p&gt;Phase 13 explicitly adds governance guidance for regulated workloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Human Review&lt;/strong&gt;: High-risk scenarios such as healthcare, genomics, sensitive operations, and government archives are modeled with 100% human confirmation as the recommended default in these sample patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is because anonymization leaks, variant misclassification, alert errors, and redaction failures can affect patient privacy, sensitive operational decisions, citizen privacy, and public trust. These patterns treat AI outputs as assistive signals, not final decisions. Actual review thresholds should be adjusted based on each organization's risk assessment, data classification, and governance requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audit trail&lt;/strong&gt;: DynamoDB records (who/when/what reviewed), with S3 Object Lock or similar immutability controls for tamper-resistant retention. The sample uses DynamoDB as one implementation option; customer implementations should align with the organization's existing audit platform, retention policy, and access-control model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separation of duties&lt;/strong&gt;: Reviewer ≠ Approver ≠ Auditor ≠ Operator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Periodic review&lt;/strong&gt;: Example cadence: quarterly AI output quality review, annual compliance mapping update, and incident-triggered process revision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to automate final judgment, but to make AI-assisted processing reviewable, attributable, and auditable. Before moving beyond PoC, teams should identify who owns the decision to proceed, who approves AI-assisted outputs, and who reviews audit evidence. For regulated scenarios, this should be treated as a multi-stakeholder decision involving business owners, security, compliance, operations, and data owners.&lt;/p&gt;

&lt;p&gt;For public sector and regulated workloads, the first step is often confirming data readiness: where the data resides, how it is classified, who can access it, and how review and audit records are retained. The &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/public-sector-adoption-roadmap.md" rel="noopener noreferrer"&gt;Public Sector Adoption Roadmap&lt;/a&gt; maps PoC, controlled rollout, and broader adoption to governance checkpoints and stakeholder decisions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This checklist provides governance guidance for architectural and operational review. It does not replace legal, compliance, privacy, or regulatory assessment by the responsible organization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Full checklist: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/governance-checklist.md" rel="noopener noreferrer"&gt;Governance Checklist&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Reading Path
&lt;/h2&gt;

&lt;p&gt;Use the reading path below to choose the shortest route based on your role.&lt;/p&gt;

&lt;p&gt;For partners and system integrators, Phase 13 provides reusable delivery assets rather than only reference code.&lt;/p&gt;

&lt;p&gt;For a customer-facing motion:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the Partner/SI Delivery Checklist as the primary entry point.&lt;/li&gt;
&lt;li&gt;Use the Workshop Guide for facilitation.&lt;/li&gt;
&lt;li&gt;Use UC Success Metrics to define PoC success criteria.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The workshop assets are designed to end with a concrete decision package: selected use case, trigger mode, deployment profile, success criteria, stakeholders, and next-phase actions.&lt;/p&gt;

&lt;p&gt;Typical stakeholders include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Legal Ops&lt;/li&gt;
&lt;li&gt;Digital Transformation Office&lt;/li&gt;
&lt;li&gt;Medical IT&lt;/li&gt;
&lt;li&gt;Application Owner&lt;/li&gt;
&lt;li&gt;Operations / Risk&lt;/li&gt;
&lt;li&gt;Plant IT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The expected output is a customer-ready PoC plan: selected use case, architecture option, success metrics, governance considerations, estimated effort and cost assumptions, and next-phase criteria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are new to the repository:&lt;/strong&gt;&lt;br&gt;
Start with &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns#choose-your-path" rel="noopener noreferrer"&gt;Choose Your Path&lt;/a&gt; and &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/quick-start.md" rel="noopener noreferrer"&gt;Quick Start Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are a security or governance reviewer:&lt;/strong&gt;&lt;br&gt;
Start with &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/s3ap-authorization-model.md" rel="noopener noreferrer"&gt;S3AP Authorization Model&lt;/a&gt; and &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/governance-checklist.md" rel="noopener noreferrer"&gt;Governance Checklist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are a Partner or SI:&lt;/strong&gt;&lt;br&gt;
Start with &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/partner-si-delivery-checklist.md" rel="noopener noreferrer"&gt;Partner/SI Delivery Checklist&lt;/a&gt;, &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/workshop-guide.md" rel="noopener noreferrer"&gt;Workshop Guide&lt;/a&gt;, and the PoC Proposal Example in the checklist. For customers interested in distributed read performance, DR, or workload-specific cache/clone automation, also review the FlexCache/FlexClone patterns FC1–FC6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are planning production rollout:&lt;/strong&gt;&lt;br&gt;
Use &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/production-readiness.md" rel="noopener noreferrer"&gt;Production Readiness&lt;/a&gt;, &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/s3ap-performance-considerations.md" rel="noopener noreferrer"&gt;S3AP Performance&lt;/a&gt;, and &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/deployment-profiles.md" rel="noopener noreferrer"&gt;Deployment Profiles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are evaluating for public sector / regulated workloads:&lt;/strong&gt;&lt;br&gt;
Use &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/public-sector-adoption-roadmap.md" rel="noopener noreferrer"&gt;Public Sector Adoption Roadmap&lt;/a&gt;, &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/governance-checklist.md" rel="noopener noreferrer"&gt;Governance Checklist&lt;/a&gt;, and &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/customer-discovery-template.md" rel="noopener noreferrer"&gt;Customer Discovery Template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you only have 30 minutes:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns#choose-your-path" rel="noopener noreferrer"&gt;Choose Your Path&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deploy the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/quick-start.md" rel="noopener noreferrer"&gt;Quick Start&lt;/a&gt; pattern&lt;/li&gt;
&lt;li&gt;Review the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/legal-compliance/README.md#success-metrics" rel="noopener noreferrer"&gt;Success Metrics&lt;/a&gt; for the closest UC&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;If you are preparing a customer conversation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Review the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/partner-si-delivery-checklist.md" rel="noopener noreferrer"&gt;Partner/SI Delivery Checklist&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pick the closest industry use case from the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/partner-si-delivery-checklist.md" rel="noopener noreferrer"&gt;expansion guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Copy the PoC Proposal Example and adapt the Success Metrics&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The Phase 13 documentation alignment backlog is complete.&lt;/p&gt;

&lt;p&gt;Future validation may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optional FSx 256/512 MBps benchmark runs (requires throughput configuration change and additional cost)&lt;/li&gt;
&lt;li&gt;Additional customer-specific sample runs&lt;/li&gt;
&lt;li&gt;Expanded CloudWatch correlation during higher-throughput tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are optional evidence expansions and do not change the recommended reading path or the core architecture guidance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Phase 13 makes the pattern library &lt;strong&gt;field-ready&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Previous phases proved the architecture through 17 industry use cases, event-driven ingestion, multi-account distribution, FlexClone automation, and extensive repository validation.&lt;/p&gt;

&lt;p&gt;This phase makes it easier to &lt;strong&gt;evaluate&lt;/strong&gt;, &lt;strong&gt;govern&lt;/strong&gt;, &lt;strong&gt;deliver&lt;/strong&gt;, and &lt;strong&gt;operate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The repository is no longer just a collection of serverless patterns. It is a reference package that a partner can use in a customer workshop, a security reviewer can use in an architecture review, and a platform team can use to plan production rollout — with the core guidance available from the same GitHub repository.&lt;/p&gt;

&lt;p&gt;The constraints are documented. Benchmarks are provided as sizing references. Governance controls are explicit. The delivery path is structured.&lt;/p&gt;

&lt;p&gt;That is what field-ready means here: not a final endpoint, but a practical baseline for informed evaluation, governed experimentation, and structured delivery.&lt;/p&gt;

&lt;p&gt;If you are evaluating FSx for ONTAP S3 Access Points today, start with &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns#choose-your-path" rel="noopener noreferrer"&gt;Choose Your Path&lt;/a&gt;, pick the closest use case, and review its Success Metrics before deploying.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns" rel="noopener noreferrer"&gt;github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Previous phases&lt;/strong&gt;: &lt;a href="https://dev.to/yoshikifujiwara/fsx-for-ontap-s3-access-points-as-a-serverless-automation-boundary-ai-data-pipelines-ili"&gt;Phase 1&lt;/a&gt; · &lt;a href="https://dev.to/yoshikifujiwara/public-sector-use-cases-unified-output-destination-and-a-localization-batch-fsx-for-ontap-s3-2hmo"&gt;Phase 7&lt;/a&gt; · &lt;a href="https://dev.to/yoshikifujiwara/operational-hardening-ci-grade-validation-and-pattern-c-b-hybrid-fsx-for-ontap-s3-access-587h"&gt;Phase 8&lt;/a&gt; · &lt;a href="https://dev.to/yoshikifujiwara/production-rollout-vpc-endpoint-auto-detection-and-the-cdk-no-go-fsx-for-ontap-s3-access-3lni"&gt;Phase 9&lt;/a&gt; · &lt;a href="https://dev.to/yoshikifujiwara/fpolicy-event-driven-pipeline-multi-account-stacksets-and-cost-optimization-fsx-for-ontap-s3-access-points-phase-10"&gt;Phase 10&lt;/a&gt; · &lt;a href="https://dev.to/yoshikifujiwara/production-ready-fpolicy-event-pipeline-across-17-ucs-fsx-for-ontap-s3-access-points-phase-11"&gt;Phase 11&lt;/a&gt; · &lt;a href="https://dev.to/yoshikifujiwara/operational-hardening-guardrails-secrets-rotation-slo-fsx-ontap-s3ap-phase-12"&gt;Phase 12&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>amazonfsxfornetappontap</category>
      <category>s3accesspoints</category>
    </item>
    <item>
      <title>Query NAS Data In Place with Athena and FSx for ONTAP S3 Access Points</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Fri, 22 May 2026 08:28:29 +0000</pubDate>
      <link>https://forem.com/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh</link>
      <guid>https://forem.com/aws-builders/query-nas-data-in-place-with-athena-and-fsx-for-ontap-s3-access-points-3lhh</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can query files stored on Amazon FSx for NetApp ONTAP directly from Amazon Athena through an FSx-attached S3 Access Point — without copying the source data to an S3 bucket. The source files remain on the FSx for ONTAP volume and are accessed through S3 object APIs.&lt;/p&gt;

&lt;p&gt;I verified this end-to-end: Parquet files written via NFS are immediately queryable from Athena using the &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;official AWS tutorial pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is Part 1 of a series exploring how FSx for ONTAP S3 Access Points integrate with various Lakehouse platforms. Part 2 covers Databricks — where platform security boundaries make things significantly more complex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to reproduce this validation, start from the repository's &lt;code&gt;integrations/athena/&lt;/code&gt; directory, which contains CloudFormation templates, sample data generators, and query scripts.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;v1.1 Update (2026-05-24)&lt;/strong&gt;: Since initial publication, additional verification has been completed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;CTAS write-back&lt;/strong&gt;: Athena CTAS successfully writes Parquet back to FSx S3 AP (3.7s)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Benchmark&lt;/strong&gt;: 54.8 MB/s peak throughput (5M rows, 103 MB full scan in 2.2s)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Partition projection&lt;/strong&gt;: Hive-style partitioning with columnar pruning confirmed&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Security Verified&lt;/strong&gt;: 9/9 negative security tests PASS + CloudTrail audit confirmed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See Benchmark Results and Security Verification sections below.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Is Verified in This Article
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Verified:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NFS-written Parquet file is visible via FSx S3 AP (&lt;code&gt;ListObjectsV2&lt;/code&gt;, &lt;code&gt;StorageClass: FSX_ONTAP&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Athena can query the file through Glue Data Catalog&lt;/li&gt;
&lt;li&gt;Standard S3 bucket result location works as the documented pattern&lt;/li&gt;
&lt;li&gt;Experimental FSx S3 AP result output worked in my environment&lt;/li&gt;
&lt;li&gt;CTAS write-back to FSx S3 AP (v1.1 — previously listed as failed)&lt;/li&gt;
&lt;li&gt;Benchmark: 54.8 MB/s peak throughput on 5M row dataset (v1.1)&lt;/li&gt;
&lt;li&gt;Partition projection with Hive-style partitioning (v1.1)&lt;/li&gt;
&lt;li&gt;Security Verified: 9/9 negative tests PASS (v1.1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Not verified:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delta / Hudi / Iceberg writes&lt;/li&gt;
&lt;li&gt;S3 bucket event notification semantics&lt;/li&gt;
&lt;li&gt;Large-scale performance limits&lt;/li&gt;
&lt;li&gt;CloudTrail data event coverage for production (validated for PoC in v1.1)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Enterprise file servers hold massive amounts of data — design files, inspection images, research documents, log archives. Traditionally, to analyze this data with cloud-native tools like Athena, you had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy data from NFS/SMB to S3 (DataSync, scripts, etc.)&lt;/li&gt;
&lt;li&gt;Maintain sync pipelines&lt;/li&gt;
&lt;li&gt;Pay for duplicate storage&lt;/li&gt;
&lt;li&gt;Deal with stale data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;FSx for ONTAP S3 Access Points&lt;/strong&gt; (launched December 2025) change this. The same volume that serves NFS/SMB clients now exposes an S3-compatible API. Athena queries hit the same bytes that your NFS clients read — no copy required for the source dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Users (NFS/SMB)                    Athena (S3 API)
      │                                  │
      ▼                                  ▼
┌─────────────────────────────────────────────┐
│         FSx for ONTAP Volume                │
│         /analytics/sensor_data.parquet      │
│         /analytics/logs/*.json              │
└─────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Use Cases This Unlocks
&lt;/h2&gt;

&lt;p&gt;This pattern is useful when enterprise data already lives on NFS/SMB file shares and analytics teams want to query it without building a copy pipeline to S3.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manufacturing&lt;/strong&gt;: Sensor logs, inspection results, quality reports produced by factory systems → &lt;em&gt;Outcome: Reduce data freshness from 24h (nightly batch copy) to near-zero (direct query)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAP / ERP&lt;/strong&gt;: Batch export files, operational reports, reconciliation extracts, and analytics copies — not direct replacement for application-native persistence or HA design → &lt;em&gt;Outcome: Eliminate sync pipeline maintenance&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Financial services&lt;/strong&gt;: Reconciliation files, transaction logs, regulatory extracts → &lt;em&gt;Outcome: Audit evidence available in minutes instead of hours&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare research&lt;/strong&gt;: De-identified datasets, imaging metadata, study outputs → &lt;em&gt;Outcome: Researchers query data without waiting for IT to provision S3 copies&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EDA / Semiconductor&lt;/strong&gt;: Design artifacts, simulation outputs, verification logs → &lt;em&gt;Outcome: Engineers find relevant past designs without manual search&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise file services&lt;/strong&gt;: Archives for compliance analysis, audit evidence → &lt;em&gt;Outcome: Compliance queries run on-demand instead of requiring pre-staged data&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Mission-critical workload note&lt;/strong&gt;&lt;br&gt;
This pattern provides an analytics read-access layer for existing file data. It does not replace workload-specific HA, backup, Snapshot, SnapMirror, or DR designs. For SAP, databases, VDI, and enterprise file services, treat Athena-on-FSx as an analytics and evidence layer, not as the primary resilience architecture.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Workload Isolation Guidance
&lt;/h2&gt;

&lt;p&gt;For mission-critical workloads, do not point exploratory analytics directly at the same directory used by latency-sensitive application writes unless the operational impact has been tested.&lt;/p&gt;

&lt;p&gt;Recommended pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application-owned path&lt;/strong&gt;: &lt;code&gt;/prod/app-output/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics landing path&lt;/strong&gt;: &lt;code&gt;/analytics/curated/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena query result path&lt;/strong&gt;: Standard S3 bucket (conservative), or a separately validated output path&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot / backup policy&lt;/strong&gt;: Owned by the workload team&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue/Athena access&lt;/strong&gt;: Owned by the analytics platform team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For SAP, database exports, or ERP file drops, treat this pattern as a read-access analytics layer. Do not change application HA, backup, restore, or DR design just because the files are queryable through S3 APIs.&lt;/p&gt;

&lt;p&gt;In this context, an analytics copy means an application-produced or batch-exported file that is safe for downstream analytics, not the primary application persistence path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Impact Validation
&lt;/h3&gt;

&lt;p&gt;Before production use, validate operational impact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Baseline NFS/SMB workload latency and throughput before enabling analytics queries&lt;/li&gt;
&lt;li&gt;Athena query behavior during normal application write activity&lt;/li&gt;
&lt;li&gt;FSx provisioned throughput utilization during scans (analytics and application workloads share the same backend throughput)&lt;/li&gt;
&lt;li&gt;Query concurrency limits for the analytics team&lt;/li&gt;
&lt;li&gt;Rollback plan if analytics workload affects application workload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended metrics include FSx throughput utilization, client-side NFS/SMB latency, Athena query runtime, bytes scanned, and application-side error or timeout rates during query execution.&lt;/p&gt;

&lt;p&gt;Rollback plan examples include disabling the Athena workgroup, revoking the S3 Access Point policy for analytics roles, reducing analytics query concurrency, or moving analytics to an isolated curated path.&lt;/p&gt;

&lt;h3&gt;
  
  
  What This Means for Production
&lt;/h3&gt;

&lt;p&gt;For production, treat this as a shared-storage analytics access pattern. The value is eliminating source data copy; the responsibility is validating workload isolation, throughput impact, governance, and rollback.&lt;/p&gt;

&lt;p&gt;This article is not a production certification. It is intended to start a production readiness discussion around workload isolation, governance, and rollback.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│  AWS Account                                                    │
│                                                                 │
│  ┌──────────────┐     ┌──────────────┐     ┌────────────────┐   │
│  │ FSx for ONTAP│     │ S3 Access    │     │ Athena         │   │
│  │ Volume       │◄────│ Point        │◄────│ (Serverless)   │   │
│  │              │     │ (Internet    │     │                │   │
│  │ /analytics/  │     │  origin)     │     │ SELECT ...     │   │
│  └──────────────┘     └──────────────┘     │ FROM table     │   │
│        ▲                     ▲             └────────────────┘   │
│        │                     │                      │           │
│   NFS/SMB clients      Glue Crawler          Query results      │
│   (write data)         (schema discovery)    (→ S3 bucket)      │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The access point must use &lt;strong&gt;Internet network origin&lt;/strong&gt;. Athena accesses S3 from managed infrastructure outside your VPC. The &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;AWS tutorial&lt;/a&gt; requires internet network origin for this path. VPC-origin access points deny requests from Athena.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue Data Catalog&lt;/strong&gt; provides the schema layer between Athena and the S3 AP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query results&lt;/strong&gt; are written to an S3 bucket (the standard Athena pattern), not back to the FSx volume. See Observed Behavior for an experimental alternative.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;FSx for ONTAP file system (ONTAP 9.17.1+)&lt;/li&gt;
&lt;li&gt;A volume with data (Parquet, CSV, JSON, etc.)&lt;/li&gt;
&lt;li&gt;S3 Access Point created with &lt;strong&gt;Internet network origin&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;An Athena workgroup with a query results location (standard S3 bucket)&lt;/li&gt;
&lt;li&gt;IAM permissions for Athena, Glue, and S3 AP access&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Create the S3 Access Point
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws fsx create-and-attach-s3-access-point &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-analytics-ap &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; ONTAP &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ontap-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{
    "VolumeId": "&amp;lt;YOUR_VOLUME_ID&amp;gt;",
    "FileSystemIdentity": {
      "Type": "UNIX",
      "UnixUser": {"Name": "fsxn_athena_reader"}
    }
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;YOUR_REGION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the lifecycle to become &lt;code&gt;AVAILABLE&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws fsx describe-s3-access-point-attachments &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;volume-id,Values&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_VOLUME_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;YOUR_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'S3AccessPointAttachments[].{Name:Name,Lifecycle:Lifecycle,Alias:S3AccessPoint.Alias}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-analytics-ap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Lifecycle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AVAILABLE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Alias"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-analytics-ap-xxxxxxxxxxxxxxxxxxxxxxxxxxxx-ext-s3alias"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The alias ending in &lt;code&gt;-ext-s3alias&lt;/code&gt; identifies this as an FSx for ONTAP S3 Access Point (as opposed to regular S3 Access Points which end in &lt;code&gt;-s3alias&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security note for file-system identity&lt;/strong&gt;&lt;br&gt;
This walkthrough uses a dedicated read-only identity (&lt;code&gt;fsxn_athena_reader&lt;/code&gt;). Make sure the corresponding UNIX/Windows permissions allow read access to the analytics path. Avoid using &lt;code&gt;root&lt;/code&gt; in production — scope the identity to the minimum permissions required.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Set the Access Point Policy
&lt;/h2&gt;

&lt;p&gt;This walkthrough uses role-based principals for Athena and Glue. Replace the placeholder role ARNs with the IAM roles used by your Athena workgroup and Glue crawler. Avoid account-wide principals in production.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3control put-access-point-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--account-id&lt;/span&gt; &amp;lt;YOUR_ACCOUNT_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; my-analytics-ap &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Version": "2012-10-17",
    "Statement": [{
      "Sid": "AllowAnalyticsRead",
      "Effect": "Allow",
      "Principal": {"AWS": [
        "arn:aws:iam::&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:role/&amp;lt;ATHENA_QUERY_ROLE&amp;gt;",
        "arn:aws:iam::&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:role/&amp;lt;GLUE_CRAWLER_ROLE&amp;gt;"
      ]},
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:&amp;lt;YOUR_REGION&amp;gt;:&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:accesspoint/my-analytics-ap",
        "arn:aws:s3:&amp;lt;YOUR_REGION&amp;gt;:&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:accesspoint/my-analytics-ap/object/*"
      ]
    }]
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;YOUR_REGION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The policy above is the conservative &lt;strong&gt;read-only analytics&lt;/strong&gt; policy. If you intentionally test query result output to the FSx S3 Access Point (see Observed Behavior), add &lt;code&gt;s3:PutObject&lt;/code&gt; scoped to the experimental output prefix only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowExperimentalResultWrite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:role/&amp;lt;ATHENA_QUERY_ROLE&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:&amp;lt;YOUR_REGION&amp;gt;:&amp;lt;YOUR_ACCOUNT_ID&amp;gt;:accesspoint/my-analytics-ap/object/athena-results/*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: FSx for ONTAP S3 Access Points enforce S3 Block Public Access by default — this cannot be disabled. All requests require valid IAM credentials. Additionally, the file system user associated with the access point must have read permission on the files being queried.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy note&lt;/strong&gt;: The policy above is the minimum that worked in my validation. If your Glue crawler or Athena workgroup reports location-related access errors, compare the policy with the &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;official tutorial&lt;/a&gt; and CloudTrail events, and add only the required actions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Upload Test Data via NFS
&lt;/h2&gt;

&lt;p&gt;On a machine with NFS access to the FSx volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="c1"&gt;# Generate 10,000 rows of sensor data
&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;n_rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date_range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2026-01-01&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;periods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n_rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1min&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sensor_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sensor_A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sensor_B&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sensor_C&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sensor_D&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sensor_E&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;n_rows&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_rows&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;humidity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_rows&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pressure&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1013&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_rows&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;normal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;warning&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;n_rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Write as Parquet to the NFS-mounted volume
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/mnt/fsxn/analytics/sensor-data/sensor_data.parquet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Written &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memory_usage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; KB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same file is now accessible via both NFS (&lt;code&gt;/mnt/fsxn/analytics/sensor-data/sensor_data.parquet&lt;/code&gt;) and S3 API (&lt;code&gt;s3://&amp;lt;AP_ALIAS&amp;gt;/sensor-data/sensor_data.parquet&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Verify S3 AP Access
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api list-objects-v2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$AP_ALIAS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prefix&lt;/span&gt; &lt;span class="s2"&gt;"sensor-data/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;YOUR_REGION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Contents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sensor-data/sensor_data.parquet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;252858&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"StorageClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FSX_ONTAP"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;StorageClass: FSX_ONTAP&lt;/code&gt; — this confirms the data lives on FSx, not S3.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Create Glue Database and Table
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws glue create-database &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--database-input&lt;/span&gt; &lt;span class="s1"&gt;'{"Name": "fsxn_analytics"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;YOUR_REGION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can either run a Glue Crawler for automatic schema discovery (recommended by the &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;AWS tutorial&lt;/a&gt;), or create the table manually via Athena:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;EXTERNAL&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;fsxn_analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sensor_data&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;sensor_id&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;temperature&lt;/span&gt; &lt;span class="nb"&gt;DOUBLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;humidity&lt;/span&gt; &lt;span class="nb"&gt;DOUBLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;pressure&lt;/span&gt; &lt;span class="nb"&gt;DOUBLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="n"&gt;STRING&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;STORED&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;PARQUET&lt;/span&gt;
&lt;span class="k"&gt;LOCATION&lt;/span&gt; &lt;span class="s1"&gt;'s3://&amp;lt;AP_ALIAS&amp;gt;/sensor-data/'&lt;/span&gt;
&lt;span class="n"&gt;TBLPROPERTIES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'parquet.compression'&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'SNAPPY'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Query with Athena
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic aggregation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;humidity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_humidity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'critical'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;critical_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;fsxn_analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sensor_data&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;sensor_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;critical_count&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verified result
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sensor_id | readings | avg_temp | avg_humidity | critical_count
----------|----------|----------|--------------|---------------
sensor_A  |    2027  |   24.89  |    59.84     |      68
sensor_B  |    1986  |   25.11  |    60.23     |      62
sensor_C  |    2013  |   24.95  |    59.91     |      59
sensor_D  |    1974  |   25.03  |    60.15     |      55
sensor_E  |    2000  |   24.98  |    60.02     |      56
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query time: 1.46 seconds&lt;/strong&gt; | &lt;strong&gt;Data scanned: 67 KB&lt;/strong&gt; | &lt;strong&gt;Engine: Athena v3&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Observed Behavior: Query Results Written to the FSx S3 Access Point
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;AWS tutorial&lt;/a&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Athena reads data from your FSx for ONTAP volume through the access point. Athena query results are written to the Amazon S3 results bucket, not back to the FSx for ONTAP volume."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my validation, however, setting &lt;code&gt;OutputLocation&lt;/code&gt; to the FSx for ONTAP S3 Access Point alias succeeded and wrote the &lt;code&gt;.csv&lt;/code&gt; and &lt;code&gt;.metadata&lt;/code&gt; files back to the FSx volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws athena start-query-execution &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query-string&lt;/span&gt; &lt;span class="s2"&gt;"SELECT 1 AS test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--result-configuration&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"OutputLocation=s3://&amp;lt;AP_ALIAS&amp;gt;/athena-results/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--work-group&lt;/span&gt; primary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;YOUR_REGION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result: SUCCEEDED in 584ms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The result files appeared on the FSx volume and were immediately accessible via NFS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat this as observed behavior from my environment, not a general production recommendation.&lt;/strong&gt; The conservative production pattern is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source data&lt;/strong&gt;: FSx for ONTAP S3 Access Point&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena query results&lt;/strong&gt;: Standard S3 bucket (as documented)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The experimental pattern validated in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source data&lt;/strong&gt;: FSx for ONTAP S3 Access Point&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena query results&lt;/strong&gt;: FSx for ONTAP S3 Access Point (observed to work, not documented)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Validate this in your own environment before relying on it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Governance warning&lt;/strong&gt;: Do not enable experimental query result output to FSx S3 AP for sensitive datasets unless query result retention, encryption, audit evidence, and file-system permissions are reviewed. Query results may contain derived sensitive information. For sensitive datasets, experimental result output should require approval from the data owner, security owner, and workload owner.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Performance Characteristics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Observed&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple SELECT query&lt;/td&gt;
&lt;td&gt;584 ms&lt;/td&gt;
&lt;td&gt;Includes result write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aggregation (10K rows, 67KB)&lt;/td&gt;
&lt;td&gt;1.46 s&lt;/td&gt;
&lt;td&gt;GROUP BY with 5 aggregations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data scan cost&lt;/td&gt;
&lt;td&gt;Standard Athena pricing&lt;/td&gt;
&lt;td&gt;$5 per TB scanned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage class&lt;/td&gt;
&lt;td&gt;FSX_ONTAP&lt;/td&gt;
&lt;td&gt;Confirmed in ListObjects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Performance note&lt;/strong&gt;&lt;br&gt;
These numbers validate functional compatibility, not performance limits. The dataset is intentionally small (67 KB, 10K rows). For real analytics workloads, test with realistic file sizes, object counts, partition layouts, concurrent queries, and FSx provisioned throughput. The throughput available through the S3 API depends on the FSx file system's provisioned throughput capacity (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-access-points.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  S3 API Compatibility Boundary
&lt;/h2&gt;

&lt;p&gt;FSx for ONTAP S3 Access Points expose file data through S3 object APIs, but they should not be treated as standard S3 buckets.&lt;/p&gt;

&lt;p&gt;The safe mental model is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use S3 APIs for object read/write access to files on FSx&lt;/li&gt;
&lt;li&gt;Use Glue and Athena for read-oriented analytics&lt;/li&gt;
&lt;li&gt;Do not assume S3 bucket-level features exist (event notifications, versioning, lifecycle policies)&lt;/li&gt;
&lt;li&gt;Do not assume lakehouse commit semantics (rename, conditional writes)&lt;/li&gt;
&lt;li&gt;Validate every platform integration separately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, the verified pattern is &lt;strong&gt;read-oriented analytics&lt;/strong&gt; over Parquet/CSV/JSON files. Transactional table formats and commit protocols are outside the safe default boundary.&lt;/p&gt;




&lt;h2&gt;
  
  
  Compatibility Matrix
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Validated by legend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This validation&lt;/strong&gt;: Actually executed commands or queries in this environment and confirmed the result&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supported operations review&lt;/strong&gt;: Confirmed based on the &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-supported-operations.html" rel="noopener noreferrer"&gt;supported operations documentation&lt;/a&gt; or official tutorial&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supported operations review required&lt;/strong&gt;: Not yet confirmed; additional validation needed before use&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Validated by&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ListObjectsV2&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;This validation&lt;/td&gt;
&lt;td&gt;S3 AP alias worked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetObject (Parquet scan)&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;This validation&lt;/td&gt;
&lt;td&gt;Athena v3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PutObject (small result file)&lt;/td&gt;
&lt;td&gt;⚠️ Observed&lt;/td&gt;
&lt;td&gt;This validation&lt;/td&gt;
&lt;td&gt;Not documented as Athena result pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue table over S3 AP&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;This validation&lt;/td&gt;
&lt;td&gt;Manual DDL and Crawler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CTAS to S3 AP&lt;/td&gt;
&lt;td&gt;✅ Verified (v1.1)&lt;/td&gt;
&lt;td&gt;Post-publication validation&lt;/td&gt;
&lt;td&gt;Writes Parquet via &lt;code&gt;external_location&lt;/code&gt; parameter. 3.7s for GROUP BY → 3 rows.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta Lake writes&lt;/td&gt;
&lt;td&gt;❌ Not supported&lt;/td&gt;
&lt;td&gt;Part 7 validation (May 2026)&lt;/td&gt;
&lt;td&gt;Requires conditional writes (&lt;code&gt;If-None-Match&lt;/code&gt;) for &lt;code&gt;_delta_log/&lt;/code&gt; commit — FSx for ONTAP S3 AP returns 501 Not Implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg writes&lt;/td&gt;
&lt;td&gt;❌ Not supported&lt;/td&gt;
&lt;td&gt;Part 7 validation (May 2026)&lt;/td&gt;
&lt;td&gt;S3FileIO metadata write requires conditional writes — same root cause as Delta. AWS feature request submitted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iceberg reads (pre-existing table)&lt;/td&gt;
&lt;td&gt;⚠️ Not validated&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Theoretically possible if metadata files accessible via GetObject; requires separate validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hudi writes&lt;/td&gt;
&lt;td&gt;❌ Not supported&lt;/td&gt;
&lt;td&gt;Part 7 validation (May 2026)&lt;/td&gt;
&lt;td&gt;Timeline commit requires atomic rename — not available on FSx for ONTAP S3 AP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 bucket event notifications&lt;/td&gt;
&lt;td&gt;❌ Not part of verified pattern&lt;/td&gt;
&lt;td&gt;Supported operations review required&lt;/td&gt;
&lt;td&gt;Do not assume bucket-level eventing; validate against &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-supported-operations.html" rel="noopener noreferrer"&gt;supported operations&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CTAS is a write-path pattern, not just a read query. Treat CTAS separately from read-oriented SELECT validation because it writes new table data to a target S3 location and may leave partial/orphaned files on failure. CTAS should not be included in the initial read-oriented validation scope.&lt;/p&gt;

&lt;p&gt;Transactional lakehouse formats may require semantics beyond simple object read/write, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atomic commit behavior&lt;/li&gt;
&lt;li&gt;Rename or move-like commit operations&lt;/li&gt;
&lt;li&gt;Conditional writes (If-None-Match)&lt;/li&gt;
&lt;li&gt;Manifest consistency&lt;/li&gt;
&lt;li&gt;Concurrent writer coordination&lt;/li&gt;
&lt;li&gt;Cleanup of partial/orphaned files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article does not validate those semantics. It validates read-oriented analytics over existing files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Governance and Compliance Considerations
&lt;/h2&gt;

&lt;p&gt;This pattern keeps the source files on FSx for ONTAP, but it does not remove the need for data governance.&lt;/p&gt;

&lt;p&gt;Before using this pattern with regulated or sensitive datasets, review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data classification&lt;/strong&gt; of source files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM and S3 Access Point policy&lt;/strong&gt; scope (least privilege)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File system identity&lt;/strong&gt; mapped to the access point (UNIX/Windows user permissions apply)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue Data Catalog permissions&lt;/strong&gt; (who can see the table metadata)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena workgroup controls&lt;/strong&gt; (query limits, result encryption)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query result location and retention&lt;/strong&gt; (results may contain derived sensitive data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudTrail / audit evidence&lt;/strong&gt; requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot, backup, retention, and deletion&lt;/strong&gt; policy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Query results can be more sensitive than the original dataset because they may aggregate, filter, or derive new information. Apply encryption, retention, and access controls to the Athena result location as carefully as the source dataset.&lt;/p&gt;

&lt;p&gt;This article is a technical validation, not a compliance attestation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production Controls Checklist
&lt;/h2&gt;

&lt;p&gt;For regulated or sensitive datasets, define the following before production use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Athena workgroup result location (standard S3 bucket)&lt;/li&gt;
&lt;li&gt;[ ] Whether workgroup settings override client-side result settings&lt;/li&gt;
&lt;li&gt;[ ] Query result encryption mode and KMS key ownership&lt;/li&gt;
&lt;li&gt;[ ] Query result retention and deletion policy&lt;/li&gt;
&lt;li&gt;[ ] IAM principals allowed to query the Glue table&lt;/li&gt;
&lt;li&gt;[ ] File-system identity mapped to the S3 Access Point (dedicated, not root)&lt;/li&gt;
&lt;li&gt;[ ] Audit evidence approach defined and validated (e.g., CloudTrail coverage for the S3 Access Point where applicable, with sample events captured as PoC evidence)&lt;/li&gt;
&lt;li&gt;[ ] Approval process for enabling experimental result output to FSx S3 AP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For regulated workloads, consider enabling Athena workgroup override so that query result location and encryption cannot be changed by client-side settings. This prevents individual clients from changing where query results are written or how they are encrypted.&lt;/p&gt;

&lt;p&gt;For regulated workloads, experimental writeback should be disabled by default and enabled only after explicit approval from the data owner, security owner, and workload owner.&lt;/p&gt;

&lt;p&gt;Experimental writeback may be enabled only when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Approval scope is documented&lt;/li&gt;
&lt;li&gt;Output path is isolated from source data&lt;/li&gt;
&lt;li&gt;Encryption and retention are defined for the output path&lt;/li&gt;
&lt;li&gt;Cleanup and rollback procedures are documented&lt;/li&gt;
&lt;li&gt;Review expiration date is set&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Minimum audit evidence artifacts for PoC completion:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scope statement: what the audit evidence demonstrates and what it does not (e.g., "validates access path and query result control for PoC scope; does not demonstrate full production compliance")&lt;/li&gt;
&lt;li&gt;Access path description (IAM → AP policy → file-system identity)&lt;/li&gt;
&lt;li&gt;Sample successful read event&lt;/li&gt;
&lt;li&gt;Sample denied access event (if applicable)&lt;/li&gt;
&lt;li&gt;Query result location configuration&lt;/li&gt;
&lt;li&gt;Encryption configuration&lt;/li&gt;
&lt;li&gt;Workgroup override setting (if used)&lt;/li&gt;
&lt;li&gt;Reviewer sign-off (name, role, date, decision)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  30-Minute Validation Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create or verify the FSx S3 Access Point (&lt;code&gt;AVAILABLE&lt;/code&gt; lifecycle)&lt;/li&gt;
&lt;li&gt;Write one Parquet file through NFS to the analytics path&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;StorageClass: FSX_ONTAP&lt;/code&gt; with &lt;code&gt;list-objects-v2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create the Glue table (manual DDL or crawler)&lt;/li&gt;
&lt;li&gt;Run one Athena query&lt;/li&gt;
&lt;li&gt;Capture the validation artifacts (see below)&lt;/li&gt;
&lt;li&gt;Decide Go / No-Go using the PoC Success Criteria&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  First Success Path
&lt;/h2&gt;

&lt;p&gt;If you are validating this for the first time, keep the scope small.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected outcome:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Parquet file written through NFS is visible through the S3 Access Point&lt;/li&gt;
&lt;li&gt;Glue table creation or crawler schema discovery succeeds&lt;/li&gt;
&lt;li&gt;Athena can query the file in place&lt;/li&gt;
&lt;li&gt;Query result location behavior is validated and documented&lt;/li&gt;
&lt;li&gt;NFS/SMB clients can still access the original file&lt;/li&gt;
&lt;li&gt;IAM and file-system identity boundaries are understood&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not start with Delta Lake, Hudi, Iceberg writes, large scans, or concurrent workloads. Prove the read path first.&lt;/p&gt;




&lt;h2&gt;
  
  
  PoC Success Criteria
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Minimum success:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 Access Point attachment is &lt;code&gt;AVAILABLE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ListObjectsV2&lt;/code&gt; returns the expected test file&lt;/li&gt;
&lt;li&gt;Glue table points to the S3 AP alias&lt;/li&gt;
&lt;li&gt;Athena query succeeds and returns correct results&lt;/li&gt;
&lt;li&gt;Results are reproducible from a clean workgroup/session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operational success:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IAM role and S3 AP policy are scoped to the analytics roles&lt;/li&gt;
&lt;li&gt;Athena workgroup controls are defined&lt;/li&gt;
&lt;li&gt;Query result location and retention are documented&lt;/li&gt;
&lt;li&gt;Dataset size and scan cost are measured&lt;/li&gt;
&lt;li&gt;FSx throughput impact is measured during query&lt;/li&gt;
&lt;li&gt;Existing NFS/SMB application workload impact is measured during Athena queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Go / No-Go criteria:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go&lt;/strong&gt;: Read-only analytics on Parquet/CSV/JSON works with acceptable latency and cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No-Go&lt;/strong&gt;: Workload requires Delta/Hudi/Iceberg write commits through the S3 AP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No-Go&lt;/strong&gt;: Platform governance requires Unity Catalog external locations and the platform cannot yet authorize the S3 AP (see Part 2)
&amp;lt;!-- TODO: Replace with actual Part 2 URL after publication --&amp;gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Performance Test Plan
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This section defines the performance test plan and metrics to collect. It does not present benchmark results. Actual benchmark outputs will be added under &lt;code&gt;verification-pack/&lt;/code&gt; after validation runs are completed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next validation should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 GB / 10 GB / 100 GB datasets&lt;/li&gt;
&lt;li&gt;Many small files vs fewer large Parquet files&lt;/li&gt;
&lt;li&gt;Partitioned layout (&lt;code&gt;date=YYYY-MM-DD/sensor_id=...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Concurrent Athena queries&lt;/li&gt;
&lt;li&gt;Different FSx throughput capacity settings (128 / 256 / 512+ MBps)&lt;/li&gt;
&lt;li&gt;NFS writer activity during Athena scans&lt;/li&gt;
&lt;li&gt;Standard S3 result bucket vs observed FSx S3 AP result output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to separate Athena scan behavior, Glue metadata behavior, and FSx provisioned-throughput impact.&lt;/p&gt;

&lt;p&gt;Additional request pattern considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sequential vs parallel S3 API reads&lt;/li&gt;
&lt;li&gt;Prefix layout impact on listing performance&lt;/li&gt;
&lt;li&gt;Small object listing overhead&lt;/li&gt;
&lt;li&gt;Repeated query behavior with warm Glue/Athena metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Metrics collection sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FSx metrics: CloudWatch (FSx namespace)&lt;/li&gt;
&lt;li&gt;Athena query metrics: &lt;code&gt;get-query-execution&lt;/code&gt; API (EngineExecutionTimeInMillis, DataScannedInBytes)&lt;/li&gt;
&lt;li&gt;Client-side latency: CLI timing or SDK instrumentation&lt;/li&gt;
&lt;li&gt;Error/timeout sources: Athena query execution status and failure reason, client-side logs, application-side timeout logs, CloudTrail events where applicable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Record results separately for cold run (1+), warm metadata run (1+), repeated run (3+ executions). Report average, min, max, and notable outliers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validation Artifacts
&lt;/h2&gt;

&lt;p&gt;For reproducibility, capture the following artifacts in your PoC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 Access Point attachment lifecycle output (&lt;code&gt;describe-s3-access-point-attachments&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list-objects-v2&lt;/code&gt; output showing &lt;code&gt;StorageClass: FSX_ONTAP&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Glue table DDL or crawler output&lt;/li&gt;
&lt;li&gt;Athena query execution ID&lt;/li&gt;
&lt;li&gt;Athena query runtime and scanned bytes&lt;/li&gt;
&lt;li&gt;Query result location and file listing&lt;/li&gt;
&lt;li&gt;NFS listing showing the original source file is unchanged&lt;/li&gt;
&lt;li&gt;IAM policy and access point policy used for the test&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt;, I cover what happens when you try to connect &lt;strong&gt;Databricks&lt;/strong&gt; to FSx for ONTAP S3 Access Points — where Unity Catalog's session policy, seccomp filters, and platform security boundaries create a significantly more complex picture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond Athena: The Multi-Engine Data Product Journey
&lt;/h2&gt;

&lt;p&gt;Athena is often the &lt;strong&gt;first step&lt;/strong&gt; in turning NAS file data into AI-ready data products. Here's how it fits in a broader multi-engine architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FSx for ONTAP (source of truth)
  ↓ S3 Access Point
Athena (discover &amp;amp; profile)
  ↓ CTAS → curated Parquet on FSx for ONTAP or S3
  ├── Snowflake External Table → Cortex AI (summarize, RAG, sentiment)
  ├── Glue / EMR → further transformation, Iceberg tables on S3
  ├── Lake Formation → fine-grained governance (column/row/tag)
  └── Databricks (via DataSync → S3) → ML training, feature engineering
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters for AI&lt;/strong&gt;: The fastest path from "files on NAS" to "AI-ready data" is often:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Athena&lt;/strong&gt; to discover what data exists and validate quality ($0.005/query)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snowflake External Table&lt;/strong&gt; for immediate Cortex AI functions (zero-copy, no COPY INTO needed for text AI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glue ETL&lt;/strong&gt; to create curated Parquet datasets for ML training&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databricks&lt;/strong&gt; for full ML lifecycle (if already in the ecosystem)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each engine adds value at a different stage. Athena's role is &lt;strong&gt;discovery and validation&lt;/strong&gt; — the cheapest way to confirm that your NAS data is queryable and useful before investing in downstream pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Series index:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: Athena (this article) — Security Verified&lt;/li&gt;
&lt;li&gt;Part 2: Databricks — session policy deep dive (coming soon)&lt;/li&gt;
&lt;li&gt;Part 3: DuckDB Lambda — serverless for $0.00001/query (coming soon)&lt;/li&gt;
&lt;li&gt;Part 4: EMR Spark — read-write ETL pipeline (coming soon)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/12/amazon-fsx-netapp-ontap-s3-access/" rel="noopener noreferrer"&gt;AWS What's New: Amazon FSx for NetApp ONTAP now supports Amazon S3 access (Dec 2, 2025)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-query-data-with-athena.html" rel="noopener noreferrer"&gt;AWS Tutorial: Query files with Athena&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-access-points.html" rel="noopener noreferrer"&gt;FSx for ONTAP S3 Access Points documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-supported-operations.html" rel="noopener noreferrer"&gt;Supported S3 operations for FSx S3 AP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;GitHub: fsxn-lakehouse-integrations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This article is part of the "FSx for ONTAP S3 Access Points × Lakehouse Deep Dive" series. All tests were performed on a real AWS environment with FSx for ONTAP (ONTAP 9.17.1, ap-northeast-1) in May 2026.&lt;/em&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope reminder&lt;/strong&gt;: This article verifies a limited read-oriented scenario. It does not validate production readiness, write-path behavior, distributed executor-scale processing, or all third-party analytics engines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Article update plan&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;v1.0 (2026-05-22) — Scope, observed behavior, validation plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v1.1 (2026-05-24, current)&lt;/strong&gt; — Benchmark (54.8 MB/s), CTAS verified, partition projection, Security Verified (9/9 tests).&lt;/li&gt;
&lt;li&gt;v1.2 (planned) — Production workload isolation, concurrent query scaling.&lt;/li&gt;
&lt;li&gt;v1.3 (planned) — Cross-platform comparison (DuckDB, EMR, Redshift on same dataset).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>amazonfsxfornetappontap</category>
      <category>athena</category>
      <category>lakehouse</category>
    </item>
    <item>
      <title>Direct-to-Grafana: Shipping FSx for ONTAP Logs to Grafana Cloud Loki via OTLP Gateway</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Thu, 21 May 2026 09:12:09 +0000</pubDate>
      <link>https://forem.com/aws-builders/direct-to-grafana-shipping-fsx-for-ontap-logs-to-grafana-cloud-loki-via-otlp-gateway-33hk</link>
      <guid>https://forem.com/aws-builders/direct-to-grafana-shipping-fsx-for-ontap-logs-to-grafana-cloud-loki-via-otlp-gateway-33hk</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;We built a direct Lambda-to-Grafana Cloud pipeline that ships FSx for ONTAP audit logs to Loki without an intermediate OTel Collector. Three Lambda functions cover all event sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FSx for ONTAP audit logs&lt;/strong&gt; → EventBridge Scheduler (every 5 min) → Lambda (polls &amp;amp; reads via S3 Access Point) → OTLP Gateway → Loki&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EMS webhooks&lt;/strong&gt; (ransomware alerts, quota warnings) → API Gateway → Lambda → OTLP Gateway → Loki&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FPolicy file operations&lt;/strong&gt; (real-time CIFS/SMB events) → ECS Fargate → SQS → Bridge Lambda → EventBridge → Lambda → OTLP Gateway → Loki&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is CloudFormation-templated, parameterized, and deployable with a single script. No hardcoded values, and the infrastructure is fully parameterized. This is a Grafana-specific direct integration by design; use the Collector path from Part 5 when you need backend portability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you only want to validate the path quickly, jump to First Success Path and deploy the audit poller first.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;This is the single-backend counterpart to Part 5: simpler when Grafana Cloud is the chosen destination, less flexible when backend portability, enrichment, redaction, or multi-backend routing is required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Direct Send (Without OTel Collector)?
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/escape-vendor-lock-in-multi-backend-log-delivery-with-otel-collector-for-fsx-for-ontap"&gt;Part 5&lt;/a&gt;, we showed how the OTel Collector decouples Lambda from backends. That's the right choice when you need multi-backend delivery or vendor migration flexibility.&lt;/p&gt;

&lt;p&gt;But if Grafana Cloud is your single observability platform and your goal is a simple serverless path, direct OTLP can be a good starting point. For production pipelines that need richer buffering, metadata enrichment, redaction, or routing, &lt;a href="https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/" rel="noopener noreferrer"&gt;Grafana recommends an Alloy / Collector-based architecture&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Components&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OTel Collector&lt;/td&gt;
&lt;td&gt;Lambda → Collector (ECS/EC2) → Grafana&lt;/td&gt;
&lt;td&gt;+50-100ms&lt;/td&gt;
&lt;td&gt;Collector compute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direct send&lt;/td&gt;
&lt;td&gt;Lambda → Grafana OTLP Gateway&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Lambda only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The direct path is simpler, cheaper, and has fewer failure points. You can always graduate to the Collector path later (Part 5 shows how). Direct send is a good fit when operational simplicity is more important than in-pipeline enrichment, redaction, buffering, and multi-backend routing. If those requirements become mandatory, move the same OTLP payload model behind Alloy or the OpenTelemetry Collector.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Direct send reduces moving parts, but it also removes the Collector / Alloy queueing layer. For production, decide whether Lambda retry and DLQ are sufficient, or whether you need SQS buffering, DLQ replay, or the Collector / Alloy path for stronger delivery guarantees during endpoint outages or throttling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delivery guarantee decision&lt;/strong&gt; (see &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/delivery-guarantees.md" rel="noopener noreferrer"&gt;full pattern guide&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Quickstart (this template)&lt;/em&gt;: Scheduler retry + Scheduler DLQ + Lambda reserved concurrency + checkpoint retry&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Medium volume&lt;/em&gt;: add Lambda failure destination and operational replay procedures&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Higher reliability&lt;/em&gt;: insert SQS before shipping, or place Alloy / OTel Collector behind Lambda for batching, retry with persistent queue, transform, redaction, and multi-backend routing&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Multi-backend or redaction/routing&lt;/em&gt;: use Part 5 Collector path&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│ Event Sources                                        │
├─────────────────────────────────────────────────────┤
│                                                      │
│  EventBridge Scheduler                               │
│  rate(5 minutes) ──→ Lambda                          │
│                       │ lists new files via           │
│                       │ S3 Access Point              │
│                       │ (checkpoint in SSM)          │
│                       ▼                              │
│                OTLP Gateway                          │
│                (Grafana Cloud)                        │
│                       │                              │
│  EMS Webhook          │                              │
│  ──→ API GW ──→ Lambda ────────────┤                │
│     (ems_handler)                   │                │
│                                     ▼                │
│  FPolicy                           Loki             │
│  ──→ ECS Fargate ──→ SQS          (Explore,        │
│  ──→ Bridge Lambda                  Dashboard)      │
│  ──→ EventBridge                                    │
│  ──→ Lambda (fpolicy_handler) ─────────────────────┤
└─────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The audit log path uses a &lt;strong&gt;polling pattern&lt;/strong&gt;: EventBridge Scheduler invokes Lambda every 5 minutes. Lambda lists new objects via the S3 Access Point, reads and processes them, then updates an SSM Parameter Store checkpoint to track progress. This avoids reliance on S3 Event Notifications, which are not supported by FSx for ONTAP S3 Access Points.&lt;/p&gt;

&lt;p&gt;The same S3 Access Point boundary can be reused for other automation patterns (AI/ML, analytics, compliance archival) because the audit files remain on FSx for ONTAP while Lambda reads them through standard S3 object APIs — no data copy or NFS/SMB mount required.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This pattern does not replace ONTAP audit, EMS, or FPolicy configuration; it provides an AWS-native delivery and visualization layer for those ONTAP-native signals.&lt;/p&gt;

&lt;p&gt;For business-critical workloads such as SAP, databases, VDI, or enterprise file services, treat this pipeline as an observability and evidence layer. It complements, but does not replace, workload-specific HA, backup, restore, and DR designs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Use cases this unlocks&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Investigate file access activity for FSx for ONTAP-hosted enterprise file shares&lt;/li&gt;
&lt;li&gt;Monitor available ONTAP EMS alerts, such as ransomware-related events, quota warnings, and storage/system events&lt;/li&gt;
&lt;li&gt;Correlate audit logs, EMS, and FPolicy file operations in a single Grafana dashboard&lt;/li&gt;
&lt;li&gt;Provide a lightweight observability path for SAP, database, VDI, and file service workloads using FSx for ONTAP&lt;/li&gt;
&lt;li&gt;Start with direct OTLP delivery and graduate to Alloy / Collector when governance or multi-backend routing is required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The FPolicy path has two Lambda roles: a &lt;strong&gt;bridge Lambda&lt;/strong&gt; that converts ECS/FPolicy server SQS output into EventBridge events, and &lt;strong&gt;&lt;code&gt;fpolicy_handler.py&lt;/code&gt;&lt;/strong&gt;, which ships those normalized EventBridge events to Grafana Cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Discovery: OTLP Gateway, Not Loki Push API
&lt;/h2&gt;

&lt;p&gt;During E2E verification, the Loki Push API returned HTTP 530 in my trial account. The OTLP Gateway worked reliably in this project and is the &lt;a href="https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/" rel="noopener noreferrer"&gt;recommended Grafana Cloud OTLP ingestion path&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For logs, Grafana Cloud routes OTLP log data to Loki, where it becomes queryable with LogQL.&lt;/p&gt;

&lt;p&gt;Our Lambda auto-detects the endpoint mode from the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_is_otlp_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Detect Grafana OTLP Gateway or generic OTLP/HTTP logs endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;otlp-gateway&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/otlp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/otlp/v1/logs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/logs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;USE_OTLP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_is_otlp_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOKI_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using the OTLP Gateway, configure &lt;code&gt;LOKI_ENDPOINT&lt;/code&gt; as the base OTLP endpoint ending in &lt;code&gt;/otlp&lt;/code&gt;. The Lambda appends &lt;code&gt;/v1/logs&lt;/code&gt; when sending logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Configure as base endpoint (Lambda appends /v1/logs)&lt;/span&gt;
&lt;span class="nv"&gt;LOKI_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://otlp-gateway-prod-ap-northeast-0.grafana.net/otlp
&lt;span class="c"&gt;# Lambda POSTs to: https://otlp-gateway-prod-ap-northeast-0.grafana.net/otlp/v1/logs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler also accepts the full path (&lt;code&gt;/otlp/v1/logs&lt;/code&gt;) without double-appending.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;URL Pattern&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OTLP Gateway (preferred)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://otlp-gateway-prod-&amp;lt;region&amp;gt;.grafana.net/otlp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Recommended by Grafana Cloud docs; verified in this project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loki Push API (fallback)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://logs-prod-&amp;lt;region&amp;gt;.grafana.net/loki/api/v1/push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;⚠️ May behave differently by account state; returned 530 in my trial validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosted Loki OTLP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://&amp;lt;loki-host&amp;gt;/otlp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requires Loki OTLP ingestion support and structured metadata configuration; Loki 3.0+ enables structured metadata by default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Authentication: Basic Auth with base64 Encoding
&lt;/h2&gt;

&lt;p&gt;Grafana Cloud uses Basic Auth for both endpoints. The critical detail: the value is &lt;code&gt;base64(instanceId:apiToken)&lt;/code&gt;, not plain text concatenation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;b64encode&lt;/span&gt;

&lt;span class="n"&gt;instance_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123456&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# From Grafana Cloud console
&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;glc_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# logs:write scope
&lt;/span&gt;
&lt;span class="n"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;auth_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Basic &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Credentials are stored in AWS Secrets Manager as JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"instance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"api_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;token&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Lambda reads this at cold start and caches the auth header for subsequent invocations. For production, use the shared &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/shared/python/auth_cache.py" rel="noopener noreferrer"&gt;&lt;code&gt;auth_cache.py&lt;/code&gt;&lt;/a&gt; module which provides TTL-based caching with automatic reload-on-401/403, so credential rotation does not require waiting for a new Lambda execution environment.&lt;/p&gt;

&lt;p&gt;Internally, normalized records are now converted directly to OTLP as the primary path. Loki Push formatting is kept only as a fallback mode. This aligns with Part 5's "OTLP as producer contract" principle. For the full OTLP resource/log-record/body mapping and &lt;code&gt;fsxn.*&lt;/code&gt; attribute naming policy, see the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/operations.md#otlp-mapping" rel="noopener noreferrer"&gt;Grafana Operations Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Lambda Handlers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. FSx Audit Log Handler via S3 Access Point (&lt;code&gt;handler.py&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Polls for new FSx ONTAP audit log files via S3 Access Point, parses JSON/EVTX, and ships to Grafana Cloud. Uses SSM Parameter Store to checkpoint progress between invocations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;auth_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_auth_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Cached from Secrets Manager
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scheduler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Polling mode: list new files, process, update checkpoint
&lt;/span&gt;        &lt;span class="n"&gt;last_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_checkpoint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# SSM Parameter Store
&lt;/span&gt;        &lt;span class="n"&gt;new_keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list_new_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S3_ACCESS_POINT_ARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MAX_KEYS_PER_RUN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_remaining_time_in_millis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;SAFETY_THRESHOLD_MS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;  &lt;span class="c1"&gt;# Stop early, resume on next scheduled run
&lt;/span&gt;            &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;S3_ACCESS_POINT_ARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;ship_to_grafana&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Raises on failure
&lt;/span&gt;            &lt;span class="nf"&gt;set_checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Only after confirmed delivery
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Manual test mode using an S3-event-shaped payload
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;extract_s3_records&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;S3_ACCESS_POINT_ARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;logs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nf"&gt;ship_to_grafana&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query in Grafana Explore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{service_name="fsxn-audit"} | json | Operation="create"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. EMS Webhook Handler (&lt;code&gt;ems_handler.py&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Receives ONTAP EMS events via API Gateway, parses with the shared EMS parser layer, and forwards to Grafana.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_ems_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Shared Lambda Layer
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USE_OTLP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;format_for_otlp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;format_for_loki&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;ship_to_grafana&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Labels: &lt;code&gt;{service_name="fsxn-ems", source="ontap", severity="alert"}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: Do not expose the EMS webhook endpoint as an unauthenticated public API in production. Use API Gateway authorization controls such as an API key, IAM authorization, Lambda authorizer, resource policy, WAF, or source IP restrictions based on your network design. The quickstart template uses &lt;code&gt;AuthorizationType: NONE&lt;/code&gt; for simplicity — add appropriate controls before production use. See the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/webhook-security.md" rel="noopener noreferrer"&gt;webhook security guide&lt;/a&gt; for a full comparison of auth modes and a recommended shared-secret Lambda authorizer pattern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. FPolicy Handler (&lt;code&gt;fpolicy_handler.py&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Subscribes to EventBridge events from the FPolicy ECS Fargate server and forwards file operation events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# EventBridge event
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USE_OTLP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;format_for_otlp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;format_for_loki&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;ship_to_grafana&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Labels: &lt;code&gt;{service_name="fsxn-fpolicy", source="ontap", operation="create"}&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudFormation: Three Templates, Zero Hardcoded Values
&lt;/h2&gt;

&lt;p&gt;Each template is fully parameterized:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Template&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Key Parameters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;template.yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FSx audit log poller Lambda&lt;/td&gt;
&lt;td&gt;S3AccessPointArn, GrafanaCredentialsSecretArn, LokiEndpoint, ScheduleExpression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;template-ems.yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;EMS webhook Lambda&lt;/td&gt;
&lt;td&gt;GrafanaCredentialsSecretArn, LokiEndpoint, EmsParserLayerArn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;template-fpolicy.yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FPolicy EventBridge Lambda&lt;/td&gt;
&lt;td&gt;GrafanaCredentialsSecretArn, LokiEndpoint, EventBusName&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;LokiEndpoint&lt;/code&gt; parameter accepts both OTLP Gateway and Loki Push API URLs — the Lambda auto-detects the mode. The quickstart template also sets Lambda reserved concurrency to 1 and provisions a Scheduler DLQ with retry policy to avoid overlapping poller runs and preserve failed scheduled invocations. Processing bounds (&lt;code&gt;MAX_KEYS_PER_RUN&lt;/code&gt;, &lt;code&gt;SAFETY_THRESHOLD_MS&lt;/code&gt;) are configured via Lambda environment variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trigger Model: EventBridge Scheduler Polling
&lt;/h3&gt;

&lt;p&gt;FSx for ONTAP S3 Access Points do &lt;strong&gt;not&lt;/strong&gt; support S3 Event Notifications or EventBridge &lt;code&gt;ObjectCreated&lt;/code&gt; events. Instead, this integration uses an &lt;strong&gt;EventBridge Scheduler polling pattern&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge Scheduler&lt;/strong&gt; invokes the Lambda every 5 minutes (configurable via &lt;code&gt;ScheduleExpression&lt;/code&gt; parameter)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda lists new files&lt;/strong&gt; via &lt;code&gt;ListObjectsV2&lt;/code&gt; on the S3 Access Point, using &lt;code&gt;StartAfter&lt;/code&gt; to skip already-processed keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda reads and processes&lt;/strong&gt; each new file, shipping logs to Grafana Cloud&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checkpoint&lt;/strong&gt; (SSM Parameter Store) tracks the last successfully processed S3 key — on the next invocation, only newer files are processed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This pattern is simple, cost-effective, and works with AWS S3 API-compatible read paths such as FSx for ONTAP S3 Access Points. The trade-off is polling latency (up to 5 minutes by default) vs. the near-real-time delivery of event-driven triggers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CloudTrail alternative&lt;/strong&gt;: CloudTrail data events &lt;em&gt;do&lt;/em&gt; work with FSx ONTAP S3 Access Points (confirmed by NetApp Workload Factory's Journal table feature). However, CloudTrail data events add additional delivery latency and $0.10/100K events cost (in my validation, the CloudTrail-based path had 5–15 minutes of end-to-end delay), making the polling pattern the better default for this use case. See the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/cloudtrail-trigger-alternative.md" rel="noopener noreferrer"&gt;CloudTrail trigger alternative&lt;/a&gt; for a full analysis and CloudFormation example.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# CloudFormation: EventBridge Scheduler with retry and DLQ&lt;/span&gt;
&lt;span class="na"&gt;AuditLogSchedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Scheduler::Schedule&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ScheduleExpression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ScheduleExpression&lt;/span&gt;  &lt;span class="c1"&gt;# default: rate(5 minutes)&lt;/span&gt;
    &lt;span class="na"&gt;FlexibleTimeWindow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OFF'&lt;/span&gt;
    &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LogShipperFunction.Arn&lt;/span&gt;
      &lt;span class="na"&gt;RoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;SchedulerRole.Arn&lt;/span&gt;
      &lt;span class="na"&gt;Input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{"source":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"scheduler",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"s3_access_point_arn":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${S3AccessPointArn}",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"prefix":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"${S3KeyPrefix}"}'&lt;/span&gt;
      &lt;span class="na"&gt;RetryPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;MaximumRetryAttempts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
        &lt;span class="na"&gt;MaximumEventAgeInSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;
      &lt;span class="na"&gt;DeadLetterConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;SchedulerDLQ.Arn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler also accepts S3 event format for manual testing via &lt;code&gt;aws lambda invoke&lt;/code&gt;, so you can still test individual files without waiting for the scheduler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checkpoint Semantics
&lt;/h3&gt;

&lt;p&gt;The quickstart uses a simple high-watermark checkpoint: the last successfully processed object key is stored in SSM Parameter Store, and the next run lists keys after that value.&lt;/p&gt;

&lt;p&gt;This works when audit log object keys are monotonically increasing and immutable. For production, validate your audit log naming and rotation behavior. If files can arrive late, be overwritten, or appear out of lexical order, use a stronger checkpoint model such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeping a short lookback window&lt;/li&gt;
&lt;li&gt;Deduplicating by object key + ETag or LastModified&lt;/li&gt;
&lt;li&gt;Storing per-object processing state in DynamoDB&lt;/li&gt;
&lt;li&gt;Updating the checkpoint only after confirmed Grafana delivery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The checkpoint is advanced only after Grafana returns a successful response for that object. If delivery fails after retries, the Lambda raises an error and the next scheduled run will retry from the last checkpoint.&lt;/p&gt;

&lt;p&gt;Failure-path tests verify this behavior: if OTLP delivery returns failure after retries, the Lambda raises and the checkpoint does not advance past the failed object.&lt;/p&gt;

&lt;p&gt;Files that parse successfully but contain no shippable records are treated as successfully processed and checkpointed; only delivery failures or parse errors prevent checkpoint advancement.&lt;/p&gt;

&lt;p&gt;For production, add a poison-pill policy for files that repeatedly fail parsing or delivery; otherwise one bad file can block later audit logs when using a high-watermark checkpoint. See the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/operations.md" rel="noopener noreferrer"&gt;Grafana operations guide&lt;/a&gt; for poison-pill handling, pipeline health alarms, and custom metrics.&lt;/p&gt;

&lt;p&gt;Use SSM Parameter Store for the quickstart high-watermark checkpoint. Move to DynamoDB when you need per-object state, deduplication, replay tracking, or concurrent workers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Delivery semantics&lt;/strong&gt;: This pipeline provides at-least-once delivery, not exactly-once. If a Lambda invocation succeeds in sending logs to Grafana but fails before updating the checkpoint (e.g., timeout or transient SSM error), the next run will re-process and re-send those objects. For most observability use cases, duplicate log entries are acceptable. If deduplication is required, implement it explicitly using object key + ETag, event ID, or payload hash in DynamoDB. Do not rely on backend-side deduplication as the primary correctness mechanism.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Avoid Overlapping Poller Runs
&lt;/h3&gt;

&lt;p&gt;Because the audit-log poller is schedule-driven, overlapping Lambda executions can race on the same key range and checkpoint. The quickstart template sets &lt;code&gt;ReservedConcurrentExecutions: 1&lt;/code&gt; to prevent this.&lt;/p&gt;

&lt;p&gt;For higher-volume production pipelines, use a distributed lock (e.g., DynamoDB conditional write) and per-object processing state instead of relying on single-concurrency.&lt;/p&gt;

&lt;p&gt;The quickstart also configures EventBridge Scheduler with a retry policy (2 retries, 1-hour event age) and a dedicated DLQ. If a scheduled invocation is throttled or fails, the event is preserved in the Scheduler DLQ for visibility and replay.&lt;/p&gt;

&lt;p&gt;The quickstart uses 2 retries and 1-hour maximum event age to surface persistent failures quickly while avoiding unbounded retry storms. Increase these values only if your Grafana endpoint outage tolerance and duplicate-handling strategy are defined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Processing Bounds
&lt;/h3&gt;

&lt;p&gt;The poller bounds work per invocation to avoid timeout-related checkpoint corruption:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Max keys per run&lt;/strong&gt; (&lt;code&gt;MAX_KEYS_PER_RUN&lt;/code&gt;, default: 100): caps the number of files processed in a single invocation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety threshold&lt;/strong&gt; (&lt;code&gt;SAFETY_THRESHOLD_MS&lt;/code&gt;, default: 30000): stops processing when remaining Lambda time falls below 30 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MAX_KEYS_PER_RUN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum audit log files processed per invocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SAFETY_THRESHOLD_MS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;30000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop processing before Lambda timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tune these values after observing Lambda duration, checkpoint age, Scheduler DLQ depth, FSx S3 Access Point read throughput, and Grafana send latency.&lt;/p&gt;

&lt;p&gt;Because the checkpoint advances after each successfully delivered object, the next scheduled run resumes safely from where the previous run stopped.&lt;/p&gt;

&lt;h3&gt;
  
  
  S3 API Compatibility Boundary
&lt;/h3&gt;

&lt;p&gt;FSx for ONTAP S3 Access Points provide S3 object API access (GetObject, ListObjectsV2, etc.) to file data that remains on the FSx for ONTAP file system. They should not be assumed to have the same bucket-level features or eventing behavior as standard S3 buckets. In this integration, the important difference is eventing: the audit log path uses Scheduler polling instead of S3 Event Notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimum Read-Path Permissions
&lt;/h3&gt;

&lt;p&gt;For the audit-log Lambda, verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;s3:ListBucket&lt;/code&gt; on the S3 Access Point ARN&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;s3:GetObject&lt;/code&gt; on the S3 Access Point object ARN (&lt;code&gt;{arn}/object/*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;S3 Access Point policy allows the Lambda execution role&lt;/li&gt;
&lt;li&gt;The file-system user associated with the access point has read permission on the audit log path&lt;/li&gt;
&lt;li&gt;If the access point is VPC-restricted, the Lambda network path can reach the S3 endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IAM resource ARN examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# List access (s3:ListBucket)&lt;/span&gt;
&lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;access-point-name&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;# Object read (s3:GetObject)&lt;/span&gt;
&lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:s3:&amp;lt;region&amp;gt;:&amp;lt;account&amp;gt;:accesspoint/&amp;lt;access-point-name&amp;gt;/object/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  First Success Path
&lt;/h2&gt;

&lt;p&gt;If this is your first deployment, start small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy only the audit log poller&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MAX_KEYS_PER_RUN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SAFETY_THRESHOLD_MS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30000
bash integrations/grafana/scripts/deploy.sh &lt;span class="nt"&gt;--audit-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then validate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm &lt;code&gt;{service_name="fsxn-audit"}&lt;/code&gt; in Grafana Explore&lt;/li&gt;
&lt;li&gt;Check the Scheduler DLQ is empty&lt;/li&gt;
&lt;li&gt;Verify the SSM checkpoint advanced&lt;/li&gt;
&lt;li&gt;Create the dashboard&lt;/li&gt;
&lt;li&gt;Add EMS and FPolicy only after the audit path works (&lt;code&gt;deploy.sh --all&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;deploy.sh&lt;/code&gt; passes &lt;code&gt;MAX_KEYS_PER_RUN&lt;/code&gt; and &lt;code&gt;SAFETY_THRESHOLD_MS&lt;/code&gt; as Lambda environment variables. If unset, the template defaults (100 / 30000) are used.&lt;/p&gt;

&lt;p&gt;The first validation should prove three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One audit file is visible in Grafana (&lt;code&gt;{service_name="fsxn-audit"}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The SSM checkpoint advanced to the processed key&lt;/li&gt;
&lt;li&gt;The Scheduler DLQ remains empty&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One-Command Deploy and Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy all 3 stacks + update Lambda code (default is --all)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GRAFANA_SECRET_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:secretsmanager:ap-northeast-1:&amp;lt;account&amp;gt;:secret:grafana/fsxn-loki-credentials-XXXXXX"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;S3_ACCESS_POINT_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:ap-northeast-1:&amp;lt;account&amp;gt;:accesspoint/fsxn-audit-ap"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LOKI_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://otlp-gateway-prod-ap-northeast-0.grafana.net/otlp"&lt;/span&gt;

bash integrations/grafana/scripts/deploy.sh &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cleanup script removes CloudFormation stacks and optionally deletes synthetic test objects. It does &lt;strong&gt;not&lt;/strong&gt; delete production FSx audit files through the FSx-attached S3 Access Point — those remain on the FSx file system. Pass &lt;code&gt;--s3-bucket&lt;/code&gt; and &lt;code&gt;--s3-prefix&lt;/code&gt; only if you uploaded test data to a regular S3 bucket during validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Tear down everything (dependency-safe order)&lt;/span&gt;
bash integrations/grafana/scripts/cleanup.sh &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--s3-bucket&lt;/span&gt; your-bucket &lt;span class="nt"&gt;--s3-prefix&lt;/span&gt; audit/svm-prod-01/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cleanup script deletes stacks in dependency-safe order (API Gateway before Lambda) and handles DELETE_FAILED states gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  LogQL Query Examples
&lt;/h2&gt;

&lt;p&gt;High-cardinality fields such as &lt;code&gt;UserName&lt;/code&gt; and &lt;code&gt;ObjectName&lt;/code&gt; remain in the log body and are extracted at query time with &lt;code&gt;| json&lt;/code&gt;; they are intentionally not promoted to Loki labels to avoid index bloat and cost.&lt;/p&gt;

&lt;p&gt;Once logs arrive, Grafana Explore becomes your investigation tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# All audit logs
{service_name="fsxn-audit"}

# Filter by operation
{service_name="fsxn-audit"} | json | Operation="delete"

# Failed access attempts (security investigation)
{service_name="fsxn-audit"} | json | Result="Failure"

# EMS ransomware alerts
{service_name="fsxn-ems"} | json | event_name="arw.volume.state"

# FPolicy file operations
{service_name="fsxn-fpolicy"} | json | operation="create"

# Human-readable format
{service_name="fsxn-audit"} | json | line_format "{{.UserName}} {{.Operation}} {{.ObjectName}}"

# Log volume over time (for dashboards)
count_over_time({service_name="fsxn-audit"}[5m])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dashboard: 4 Panels for Storage Observability
&lt;/h2&gt;

&lt;p&gt;The following panel queries are the exact queries generated by &lt;code&gt;scripts/create-dashboard.sh&lt;/code&gt; and verified against this project's OTLP-ingested log shape. The repository includes a dashboard creation script that provisions a Grafana dashboard via API with four panels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Log Volume&lt;/strong&gt; (Time series): &lt;code&gt;count_over_time({service_name="fsxn-audit"}[5m])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations Breakdown&lt;/strong&gt; (Pie chart): &lt;code&gt;sum by (Operation) (count_over_time({service_name="fsxn-audit"} | json [1h]))&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Activity Top 10&lt;/strong&gt; (Bar gauge): &lt;code&gt;topk(10, sum by (UserName) (count_over_time({service_name="fsxn-audit"} | json [1h])))&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failed Events&lt;/strong&gt; (Time series): &lt;code&gt;count_over_time({service_name="fsxn-audit"} | json | Result="Failure" [5m])&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Alerting: Ransomware Detection and Security Monitoring
&lt;/h2&gt;

&lt;p&gt;Beyond dashboards, the integration includes three Grafana alerting rules provisioned via &lt;code&gt;scripts/create-alerts.sh&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;The table below shows the alert conditions. The provisioning script wraps these into Grafana alert expressions using count/reduce/threshold steps.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Alert&lt;/th&gt;
&lt;th&gt;Detection Query (alert condition)&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ransomware Detection (ARP)&lt;/td&gt;
&lt;td&gt;`count_over_time({service_name="fsxn-ems"} \&lt;/td&gt;
&lt;td&gt;json \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quota Soft Limit Exceeded&lt;/td&gt;
&lt;td&gt;{% raw %}`count_over_time({service_name="fsxn-ems"} \&lt;/td&gt;
&lt;td&gt;json \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failed Access Spike&lt;/td&gt;
&lt;td&gt;{% raw %}`count_over_time({service_name="fsxn-audit"} \&lt;/td&gt;
&lt;td&gt;json \&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rules use Grafana's unified alerting format and are deployed to a "FSxN Alerts" folder. Configure contact points (Slack, PagerDuty, email) and notification policies in the Grafana UI to route alerts by severity or team label. The rule definitions are available as {% raw %}&lt;code&gt;alerting/rules.yaml&lt;/code&gt;; see the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/alerting/README.md" rel="noopener noreferrer"&gt;alerting README&lt;/a&gt; for provisioning details, no-data behavior, contact point caveats, and threshold tuning guidance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;API compatibility&lt;/strong&gt;: This script uses Grafana's Alerting Provisioning HTTP API (&lt;code&gt;/api/v1/provisioning&lt;/code&gt;). Grafana 13+ introduces newer &lt;code&gt;/apis&lt;/code&gt; routes while legacy &lt;code&gt;/api&lt;/code&gt; routes remain available; check your Grafana Cloud version if provisioning fails. Provisioning alert rules does not automatically configure notification delivery — create or map contact points and notification policies before relying on these alerts for production response.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The sample rules treat "No data" as OK, because absence of matching ransomware, quota, or failed-access events is expected in normal operation. Query execution errors are routed as Error state for operator attention. These thresholds are starter defaults — tune them per SVM, workload, and normal user behavior before enabling production paging.&lt;/p&gt;

&lt;p&gt;For production, monitor the pipeline itself: Scheduler DLQ depth, Lambda errors/throttles/duration, checkpoint age, and Grafana send failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduler DLQ Replay
&lt;/h2&gt;

&lt;p&gt;The Scheduler DLQ message is primarily an operational signal and replay payload. Because the poller uses a checkpoint, the next scheduled run may already retry the failed key range automatically.&lt;/p&gt;

&lt;p&gt;When a scheduled invocation fails and lands in the Scheduler DLQ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect the DLQ message (contains the scheduler input payload)&lt;/li&gt;
&lt;li&gt;Check the current checkpoint in SSM Parameter Store&lt;/li&gt;
&lt;li&gt;Check whether a later scheduled run has already advanced the checkpoint and delivered the missed objects&lt;/li&gt;
&lt;li&gt;If the checkpoint has advanced and Grafana shows the data, the failure was auto-recovered — delete the DLQ message&lt;/li&gt;
&lt;li&gt;If the checkpoint has NOT advanced, the next scheduled run will retry automatically from the last checkpoint&lt;/li&gt;
&lt;li&gt;For manual replay (if auto-retry is insufficient): invoke the Lambda directly with the scheduler payload, then delete the DLQ message&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before manually replaying a DLQ message, compare the DLQ payload with the current SSM checkpoint and Grafana ingestion state to avoid duplicate delivery.&lt;/p&gt;

&lt;p&gt;For production, set a CloudWatch alarm on &lt;code&gt;ApproximateNumberOfMessagesVisible &amp;gt; 0&lt;/code&gt; for the Scheduler DLQ.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Grafana Cloud OTLP endpoint is the recommended ingestion path; in my trial validation, OTLP Gateway succeeded while Loki Push API returned 530&lt;/td&gt;
&lt;td&gt;Use OTLP Gateway as default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Basic Auth = &lt;code&gt;base64(instanceId:apiToken)&lt;/code&gt;, not plain text&lt;/td&gt;
&lt;td&gt;Auth failures if wrong encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Loki / Grafana Cloud can reject old timestamps depending on tenant limits; in my validation, logs older than 7 days were rejected&lt;/td&gt;
&lt;td&gt;Use current timestamps in test data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Grafana HTTP API needs a Grafana Service Account token, not the Grafana Cloud ingestion token used for OTLP writes&lt;/td&gt;
&lt;td&gt;Dashboard creation fails with wrong token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;OTLP-ingested logs use &lt;code&gt;service_name&lt;/code&gt; label, not &lt;code&gt;job&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Different query syntax than Loki Push API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;CloudFormation stack deletion order matters (API GW before Lambda)&lt;/td&gt;
&lt;td&gt;DELETE_FAILED if wrong order&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Verified Query Matrix
&lt;/h2&gt;

&lt;p&gt;In this Grafana Cloud environment, &lt;code&gt;service.name&lt;/code&gt; was exposed as the &lt;code&gt;service_name&lt;/code&gt; index label via Loki's default OTLP attribute-to-label mapping. This mapping is &lt;a href="https://grafana.com/docs/loki/latest/get-started/labels/modify-default-labels/" rel="noopener noreferrer"&gt;configurable per tenant&lt;/a&gt;, so validate labels in your own environment if queries return unexpected results.&lt;/p&gt;

&lt;p&gt;All queries tested with OTLP-ingested fields in this project's Grafana Cloud instance:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Expected&lt;/th&gt;
&lt;th&gt;Verified&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{service_name="fsxn-audit"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audit logs visible&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`{service_name="fsxn-audit"} \&lt;/td&gt;
&lt;td&gt;json \&lt;/td&gt;
&lt;td&gt;Operation="delete"`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`{service_name="fsxn-audit"} \&lt;/td&gt;
&lt;td&gt;json \&lt;/td&gt;
&lt;td&gt;Result="Failure"`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`{service_name="fsxn-ems"} \&lt;/td&gt;
&lt;td&gt;json \&lt;/td&gt;
&lt;td&gt;event_name="arw.volume.state"`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`{service_name="fsxn-fpolicy"} \&lt;/td&gt;
&lt;td&gt;json \&lt;/td&gt;
&lt;td&gt;operation="create"`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;count_over_time({service_name="fsxn-audit"}[5m])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time series data&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Production and PoC Resources
&lt;/h2&gt;

&lt;p&gt;For deeper validation and production planning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/delivery-guarantees.md" rel="noopener noreferrer"&gt;Delivery Guarantee Patterns&lt;/a&gt; — Quickstart → Medium → Higher reliability → Multi-backend&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/webhook-security.md" rel="noopener noreferrer"&gt;Webhook Security Guide&lt;/a&gt; — Auth modes, Lambda authorizer, production baseline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/operations.md" rel="noopener noreferrer"&gt;Grafana Operations Guide&lt;/a&gt; — Alarms, tuning, poison-pill, ownership, compliance&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/cloudtrail-trigger-alternative.md" rel="noopener noreferrer"&gt;CloudTrail Trigger Alternative&lt;/a&gt; — Event-driven alternative analysis&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/poc-checklist.md" rel="noopener noreferrer"&gt;PoC Checklist&lt;/a&gt; — Go/No-Go criteria for stakeholder sign-off&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cost-model.md" rel="noopener noreferrer"&gt;Cost Model&lt;/a&gt; — Direct send vs Collector vs Firehose cost comparison&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/alerting/README.md" rel="noopener noreferrer"&gt;Alerting README&lt;/a&gt; — Provisioning details, thresholds, contact point caveats&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/operations.md#graduating-to-alloy" rel="noopener noreferrer"&gt;Graduating to Alloy&lt;/a&gt; — Move from direct Lambda OTLP send to an Alloy-backed telemetry pipeline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/grafana/docs/en/partner-solution-brief.md" rel="noopener noreferrer"&gt;Partner Solution Brief&lt;/a&gt; — Target customers, PoC scope, deliverables, and responsibility boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 7&lt;/strong&gt;: Splunk HEC — serverless log delivery with built-in Firehose support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elastic integration&lt;/strong&gt;: Bulk API with date-based indices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost model refinement&lt;/strong&gt;: validate the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cost-model.md" rel="noopener noreferrer"&gt;Cost Model&lt;/a&gt; with measured volume tiers from real-world FSx for ONTAP workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Series Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/why-your-fsx-for-ontap-audit-logs-deserve-better-than-ec2-kod"&gt;Why Your FSx for ONTAP Logs Deserve Better&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/shipping-fsx-for-ontap-logs-to-datadog-the-serverless-way-n9c"&gt;Shipping FSx for ONTAP Logs to Datadog — The Serverless Way&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Event-Driven Ransomware Detection with ONTAP ARP + Datadog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/fpolicy-file-activity-pipeline-ontap-to-datadog-via-ecs-fargate-2ing"&gt;FPolicy File Activity Pipeline — ONTAP to Datadog via ECS Fargate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/escape-vendor-lock-in-multi-backend-log-delivery-with-otel-collector-for-fsx-for-ontap-2inb"&gt;Escape Vendor Lock-in with OTel Collector&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6&lt;/strong&gt;: Direct-to-Grafana: Shipping Logs via OTLP Gateway (this post)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Questions about the Grafana Cloud integration or OTLP Gateway? Drop a comment below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Previous: &lt;a href="https://dev.to/aws-builders/escape-vendor-lock-in-multi-backend-log-delivery-with-otel-collector-for-fsx-for-ontap"&gt;Part 5 — Escape Vendor Lock-in with OTel Collector&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations" rel="noopener noreferrer"&gt;github.com/Yoshiki0705/fsxn-observability-integrations&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>grafana</category>
      <category>observability</category>
      <category>amazonfsxfornetappontap</category>
    </item>
    <item>
      <title>Escape Vendor Lock-in: Multi-Backend Log Delivery with OTel Collector for FSx for ONTAP.</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Tue, 19 May 2026 09:10:53 +0000</pubDate>
      <link>https://forem.com/aws-builders/escape-vendor-lock-in-multi-backend-log-delivery-with-otel-collector-for-fsx-for-ontap-2inb</link>
      <guid>https://forem.com/aws-builders/escape-vendor-lock-in-multi-backend-log-delivery-with-otel-collector-for-fsx-for-ontap-2inb</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;We shipped the same FSx for ONTAP audit logs to &lt;strong&gt;three backends simultaneously&lt;/strong&gt; — Datadog, Grafana Cloud, and Honeycomb — without changing a single line of Lambda code. The OpenTelemetry Collector sits between our Lambda and the backends as a routing layer. Adding or removing a backend is a YAML config change, not a code deployment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same audit logs → 3 backends simultaneously&lt;/li&gt;
&lt;li&gt;Zero Lambda code changes between backends (SHA-256 verified)&lt;/li&gt;
&lt;li&gt;OTel Collector as the vendor-neutral routing layer&lt;/li&gt;
&lt;li&gt;All 3 event sources work: FSx audit logs via S3 Access Point, EMS webhooks, FPolicy file operations&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/shipping-fsx-for-ontap-logs-to-datadog-the-serverless-way-n9c"&gt;Part 2&lt;/a&gt;, we built a Lambda that speaks Datadog's API directly. It works great — but what happens when your security team wants Splunk, your SRE team wants Grafana, and your platform team is evaluating Honeycomb?&lt;/p&gt;

&lt;p&gt;You'd need three separate Lambdas, each with vendor-specific formatting, auth, and retry logic. That's vendor lock-in expressed as infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Vendor-Specific APIs = Lock-in
&lt;/h3&gt;

&lt;p&gt;Every observability vendor has their own wire format:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vendor&lt;/th&gt;
&lt;th&gt;Auth Header&lt;/th&gt;
&lt;th&gt;Payload Format&lt;/th&gt;
&lt;th&gt;Endpoint Pattern&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DD-API-KEY: &amp;lt;key&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom JSON schema&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://http-intake.logs.{site}/api/v2/logs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Splunk&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authorization: Splunk &amp;lt;token&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HEC &lt;code&gt;event&lt;/code&gt; wrapper&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://&amp;lt;host&amp;gt;:8088/services/collector/event&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grafana Cloud&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authorization: Basic &amp;lt;b64&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OTLP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://otlp-gateway-prod-&amp;lt;region&amp;gt;.grafana.net/otlp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Honeycomb&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x-honeycomb-team: &amp;lt;key&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OTLP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.honeycomb.io&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your Lambda speaks Datadog's API, switching to Grafana Cloud means rewriting your Lambda. That's the lock-in.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: OTLP as the Producer-to-Collector Contract
&lt;/h3&gt;

&lt;p&gt;OpenTelemetry Protocol (OTLP) is the vendor-neutral producer-to-Collector contract. Our Lambda speaks OTLP — period. The OTel Collector handles routing, processing, and backend-specific export.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────┐
│ AWS Account                                                         │
│                                                                     │
│  ┌──────────────┐     ┌──────────────────┐     ┌─────────────────┐  │
│  │ Audit Logs   │────▶│ Lambda           │     │ OTel Collector  │  │
│  │ (via S3 AP)  │────▶│ (OTLP Shipper)   │────▶│ (Docker/Fargate)│  │
│  │ EMS/FPolicy  │────▶│                  │     │                 │  │
│  └──────────────┘     └──────────────────┘     └─┬──────┬──────┬─┘  │
│                                                  │      │      │    │
└──────────────────────────────────────────────────┼──────┼──────┼────┘
                                                   │      │      │
                                                   ▼      ▼      ▼
                                              Datadog  Grafana Honeycomb
                                               (AP1)    Cloud    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Lambda sends OTLP/HTTP to the Collector. The Collector fans out to any combination of backends. Adding Honeycomb? Add 5 lines of YAML. Dropping Datadog? Remove 4 lines. No Lambda redeployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, you need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;FSx for ONTAP with audit logging&lt;/strong&gt; configured (see &lt;a href="https://dev.to/aws-builders/shipping-fsx-for-ontap-logs-to-datadog-the-serverless-way-n9c"&gt;Part 2&lt;/a&gt; for setup)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; installed locally (Colima works — see troubleshooting for compose compatibility)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At least one backend account&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Datadog: API key + site (e.g., &lt;code&gt;ap1.datadoghq.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Grafana Cloud: Instance ID + API token (Cloud Portal → OTLP)&lt;/li&gt;
&lt;li&gt;Honeycomb: Ingest API key (starts with &lt;code&gt;hcaik_&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS account&lt;/strong&gt; with Lambda deployment capability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parts 1–4 context&lt;/strong&gt; (recommended but not required — this integration works standalone)&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FSx for ONTAP S3 Access Point note&lt;/strong&gt;: The Lambda reads audit logs through an S3 Access Point attached to the FSx for ONTAP volume. Data remains on the FSx file system — it is not copied to a separate S3 bucket. S3 API throughput via FSx depends on the file system's &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/performance.html" rel="noopener noreferrer"&gt;provisioned throughput capacity&lt;/a&gt;, not standard S3 scaling. Validate FSx read throughput separately from Collector and backend ingest throughput.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The OTel Collector Configuration
&lt;/h2&gt;

&lt;p&gt;The Collector config is the heart of this pattern. Here's the full verified configuration for multi-backend delivery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# otel-collector-config.yaml&lt;/span&gt;
&lt;span class="c1"&gt;# ✅ VERIFIED WORKING (2026-05-18)&lt;/span&gt;
&lt;span class="c1"&gt;# Image: otel/opentelemetry-collector-contrib:0.152.0&lt;/span&gt;
&lt;span class="c1"&gt;# Backends: Grafana Cloud (ap-northeast-0) + Honeycomb&lt;/span&gt;

&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4318&lt;/span&gt;

&lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# memory_limiter:        # Recommended for production&lt;/span&gt;
  &lt;span class="c1"&gt;#   check_interval: 1s&lt;/span&gt;
  &lt;span class="c1"&gt;#   limit_mib: 512&lt;/span&gt;
  &lt;span class="c1"&gt;#   spike_limit_mib: 128&lt;/span&gt;
  &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;send_batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;

&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp_http/grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:GRAFANA_OTLP_ENDPOINT}&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Basic&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${env:GRAFANA_BASIC_AUTH}"&lt;/span&gt;

  &lt;span class="na"&gt;otlp_http/honeycomb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://api.honeycomb.io&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;x-honeycomb-team&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:HONEYCOMB_API_KEY}&lt;/span&gt;
      &lt;span class="na"&gt;x-honeycomb-dataset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:HONEYCOMB_DATASET}&lt;/span&gt;

&lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;health_check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:13133&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;health_check&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp_http/grafana&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;otlp_http/honeycomb&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Depending on your Honeycomb environment and dataset model, &lt;code&gt;x-honeycomb-dataset&lt;/code&gt; may be optional or handled differently. Refer to your &lt;a href="https://docs.honeycomb.io/send-data/opentelemetry/" rel="noopener noreferrer"&gt;Honeycomb OTLP setup page&lt;/a&gt; for the recommended configuration.&lt;/p&gt;

&lt;p&gt;This article uses &lt;code&gt;otlp_http&lt;/code&gt; (the forward-compatible component name). If your Collector version does not recognize it, use the older &lt;code&gt;otlphttp&lt;/code&gt; alias or upgrade the Collector.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Section Breakdown
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Key Settings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;receivers.otlp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accepts OTLP/HTTP from Lambda&lt;/td&gt;
&lt;td&gt;Port 4318 (OTLP standard)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;processors.batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Buffers logs before export&lt;/td&gt;
&lt;td&gt;5s timeout OR 1000 records (whichever first)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exporters.otlp_http/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sends to each backend&lt;/td&gt;
&lt;td&gt;Per-backend auth headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;extensions.health_check&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Liveness probe&lt;/td&gt;
&lt;td&gt;Port 13133 for &lt;code&gt;curl -f&lt;/code&gt; checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;service.pipelines&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wires components together&lt;/td&gt;
&lt;td&gt;logs: receiver → processor → exporters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Production note&lt;/strong&gt;: This configuration is suitable for development and validation. For production, add &lt;code&gt;retry_on_failure&lt;/code&gt; and &lt;code&gt;sending_queue&lt;/code&gt; settings to exporters, configure &lt;code&gt;memory_limiter&lt;/code&gt; processor, and consider persistent storage extensions. Without persistent buffering, telemetry in the Collector's in-memory batch can be lost during Collector restarts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding Datadog as a Third Backend
&lt;/h3&gt;

&lt;p&gt;To send to all three simultaneously, add the Datadog exporter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... existing grafana + honeycomb exporters ...&lt;/span&gt;

  &lt;span class="na"&gt;datadog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:DD_API_KEY}&lt;/span&gt;
      &lt;span class="na"&gt;site&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:DD_SITE}&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp_http/grafana&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;otlp_http/honeycomb&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;datadog&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Restart the Collector. Same Lambda, same OTLP payload, now three destinations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For Datadog, this example uses the Collector's dedicated &lt;code&gt;datadog&lt;/code&gt; exporter rather than generic &lt;code&gt;otlp_http&lt;/code&gt;, because it handles Datadog-specific intake behavior, metadata mapping, and host tagging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Lambda Handler (OTLP Shipper)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Design Decisions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Why OTLP?&lt;/strong&gt; — It gives the Lambda a single producer-to-Collector contract. The Collector then handles each backend's supported exporter or intake path. One format to maintain, not three.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why no vendor SDK?&lt;/strong&gt; — SDKs add cold start latency, dependency management, and vendor coupling. Pure &lt;code&gt;urllib3&lt;/code&gt; + JSON keeps the Lambda lean.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why AUTH_MODE?&lt;/strong&gt; — Different Collectors may need different auth. The Lambda supports &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;basic&lt;/code&gt;, and &lt;code&gt;bearer&lt;/code&gt; modes without code changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Field Mapping: FSx ONTAP → OTLP Attributes
&lt;/h3&gt;

&lt;p&gt;The Lambda maps FSx ONTAP audit fields to semantic OTLP attribute keys:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;FSx ONTAP Field&lt;/th&gt;
&lt;th&gt;OTLP Attribute Key&lt;/th&gt;
&lt;th&gt;Example Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EventID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;event.type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4663&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;admin@corp.local&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ClientIP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.address&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10.0.1.50&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Operation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fsxn.operation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ReadData&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ObjectName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fsxn.path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/vol/data/reports/q4.xlsx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Result&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fsxn.result&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Success&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SVMName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fsxn.svm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;svm-prod-01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The examples above focus on S3 audit logs because they are the highest-volume path. The same OTLP shipper pattern is reused for EMS webhook events and FPolicy file operations using source-specific field mappers (&lt;code&gt;ems_handler.py&lt;/code&gt;, &lt;code&gt;fpolicy_handler.py&lt;/code&gt;), while preserving the same Collector-facing OTLP contract. For EMS and FPolicy, source-specific service names are used (&lt;code&gt;fsxn-ems&lt;/code&gt;, &lt;code&gt;fsxn-fpolicy&lt;/code&gt;) to distinguish event sources in the backend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Resource-level attributes (set once per payload, not per log record):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;service.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fsxn-audit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service identification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cloud.provider&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cloud context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cloud.platform&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws_fsx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Platform context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;cloud.platform=aws_fsx&lt;/code&gt; is a project-specific value used to identify FSx for ONTAP as the data source. It is not part of the &lt;a href="https://opentelemetry.io/docs/specs/semconv/resource/cloud/" rel="noopener noreferrer"&gt;OpenTelemetry semantic conventions&lt;/a&gt; standard &lt;code&gt;cloud.platform&lt;/code&gt; values (which include &lt;code&gt;aws_ec2&lt;/code&gt;, &lt;code&gt;aws_ecs&lt;/code&gt;, &lt;code&gt;aws_eks&lt;/code&gt;, &lt;code&gt;aws_lambda&lt;/code&gt;, etc.).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Severity Determination Logic
&lt;/h3&gt;

&lt;p&gt;The Lambda determines OTLP severity from the &lt;code&gt;Result&lt;/code&gt; field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;WARN_KEYWORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;denied&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;determine_severity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Determine OTLP severity from FSx ONTAP Result field.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;WARN_KEYWORDS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means failed access attempts (&lt;code&gt;Result: "Failure"&lt;/code&gt;) automatically get &lt;code&gt;severityNumber: 13&lt;/code&gt; (WARN), making them easy to filter in any backend.&lt;/p&gt;

&lt;p&gt;The Lambda sets both &lt;code&gt;severityNumber&lt;/code&gt; and &lt;code&gt;severityText&lt;/code&gt; according to the &lt;a href="https://opentelemetry.io/docs/specs/otel/logs/data-model/#severity-fields" rel="noopener noreferrer"&gt;OpenTelemetry Logs Data Model&lt;/a&gt; severity level definitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  OTLP Payload Construction
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_otlp_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Build OTLP Log Data Model payload.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;log_records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;map_log_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resourceLogs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stringValue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloud.provider&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stringValue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloud.platform&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stringValue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws_fsx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scopeLogs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scope&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fsxn-otel-shipper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logRecords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;log_records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No vendor SDK. No vendor-specific formatting. Just the OTLP Log Data Model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retry with Exponential Backoff
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;BASE_INTERVAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;  &lt;span class="c1"&gt;# seconds
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_send_otlp_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Send OTLP payload via HTTP POST with retry logic.

    Retries on HTTP 429 and 5xx. Does not retry on 4xx (except 429).
    Exponential backoff: 2s, 4s, 8s with jitter.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/logs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;auth_headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;json_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json_body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;30.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;wait_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BASE_INTERVAL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="c1"&gt;# Client error (4xx except 429) — don't retry
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AUTH_MODE Support
&lt;/h3&gt;

&lt;p&gt;The Lambda supports three authentication modes via the &lt;code&gt;AUTH_MODE&lt;/code&gt; environment variable:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AUTH_MODE&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No auth headers sent&lt;/td&gt;
&lt;td&gt;Local Collector (no auth needed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;basic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authorization: Basic &amp;lt;base64(token)&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grafana Cloud direct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bearer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generic OTLP endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When using the Collector pattern, set &lt;code&gt;AUTH_MODE=none&lt;/code&gt; on the Lambda — the Collector handles backend auth via its own config.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Direct auth modes (&lt;code&gt;basic&lt;/code&gt;, &lt;code&gt;bearer&lt;/code&gt;) are useful for testing or bypassing the Collector. In the multi-backend pattern, keep &lt;code&gt;AUTH_MODE=none&lt;/code&gt; and let the Collector handle backend credentials.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Local Development: Docker Run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Configure credentials&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;integrations/otel-collector
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit .env with your backend credentials:&lt;/span&gt;
&lt;span class="c"&gt;#   GRAFANA_OTLP_ENDPOINT=https://otlp-gateway-prod-ap-northeast-0.grafana.net/otlp&lt;/span&gt;
&lt;span class="c"&gt;#   GRAFANA_BASIC_AUTH=&amp;lt;base64(instanceId:apiToken)&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#   HONEYCOMB_API_KEY=hcaik_&amp;lt;your-ingest-key&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#   HONEYCOMB_DATASET=fsxn-audit&lt;/span&gt;

&lt;span class="c"&gt;# 2. Start OTel Collector&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; otel-collector &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 4318:4318 &lt;span class="nt"&gt;-p&lt;/span&gt; 13133:13133 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env &lt;span class="se"&gt;\&lt;/span&gt;
  otel/opentelemetry-collector-contrib:0.152.0

&lt;span class="c"&gt;# 3. Verify health&lt;/span&gt;
curl &lt;span class="nt"&gt;-f&lt;/span&gt; http://localhost:13133/
&lt;span class="c"&gt;# Expected: HTTP 200 — {"status":"Server available", ...}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;health_check&lt;/code&gt; extension confirms the Collector process is available; it does not guarantee that each backend exporter is successfully delivering logs. Monitor exporter errors separately using the Collector's internal telemetry metrics if enabled and exposed.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 4. Send a test payload&lt;/span&gt;
bash scripts/generate-otlp-payload.sh &lt;span class="nt"&gt;--output&lt;/span&gt; /tmp/payload.json
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:4318/v1/logs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; @/tmp/payload.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Colima users&lt;/strong&gt;: &lt;code&gt;docker compose&lt;/code&gt; v2 plugin is NOT available in Colima. All scripts in this repo detect this and fall back to &lt;code&gt;docker run&lt;/code&gt;. If you see "docker compose: command not found", this is expected behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  First Success Path
&lt;/h3&gt;

&lt;p&gt;If you're trying this for the first time, start small:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the Collector locally with &lt;strong&gt;one&lt;/strong&gt; backend.&lt;/li&gt;
&lt;li&gt;Send one fresh OTLP payload.&lt;/li&gt;
&lt;li&gt;Confirm the event appears in that backend.&lt;/li&gt;
&lt;li&gt;Add the second exporter.&lt;/li&gt;
&lt;li&gt;Only then move to multi-backend or AWS deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This keeps the first validation focused on the producer-to-Collector contract before introducing backend parity and production networking.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Deployment: CloudFormation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; integrations/otel-collector/template.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; fsxn-otel-integration &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameter-overrides&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;S3AccessPointArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:s3:ap-northeast-1:123456789012:accesspoint/fsxn-audit-ap &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;OtlpEndpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://&amp;lt;your-collector-endpoint&amp;gt;:4318 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;ApiKeySecretArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:fsxn-otel-key-XXXXXX &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;AuthMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_IAM &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This template deploys the Lambda-side OTLP shipper. The Collector endpoint must already be reachable from the Lambda — for example, a local Collector for development, an EC2-hosted Collector, or an ECS/Fargate-based Collector in the same VPC. If the Lambda is in a VPC, ensure security groups allow outbound TCP 4318 to the Collector. See the repository's &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/vpc-deployment.md" rel="noopener noreferrer"&gt;VPC Deployment Guide&lt;/a&gt; and &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/security-hardening.md" rel="noopener noreferrer"&gt;Security Hardening Guide&lt;/a&gt; for production Collector deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the Collector handles auth, set &lt;code&gt;AuthMode=none&lt;/code&gt; on the Lambda. The Collector config contains the per-backend credentials via environment variables (sourced from &lt;code&gt;.env&lt;/code&gt; or Secrets Manager in production).&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Lambda&lt;/th&gt;
&lt;th&gt;Collector&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OTLP_ENDPOINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Collector URL (e.g., &lt;code&gt;http://collector:4318&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AUTH_MODE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;none&lt;/code&gt; / &lt;code&gt;basic&lt;/code&gt; / &lt;code&gt;bearer&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SERVICE_NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;OTLP &lt;code&gt;service.name&lt;/code&gt; attribute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GRAFANA_OTLP_ENDPOINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Grafana Cloud OTLP gateway URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GRAFANA_BASIC_AUTH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;base64(instanceId:apiToken)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HONEYCOMB_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Ingest key (hcaik_...)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HONEYCOMB_DATASET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Dataset name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DD_API_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Datadog API key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DD_SITE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Datadog site (&lt;code&gt;datadoghq.com&lt;/code&gt;, &lt;code&gt;datadoghq.eu&lt;/code&gt;, &lt;code&gt;ap1.datadoghq.com&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Verified Results
&lt;/h2&gt;

&lt;p&gt;All backends were tested on 2026-05-18 using &lt;code&gt;otel/opentelemetry-collector-contrib:0.152.0&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;Region/Site&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Event Sources&lt;/th&gt;
&lt;th&gt;Auth Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Datadog&lt;/td&gt;
&lt;td&gt;ap1.datadoghq.com&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;S3 audit + EMS + FPolicy&lt;/td&gt;
&lt;td&gt;Datadog exporter (&lt;code&gt;DD-API-KEY&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grafana Cloud&lt;/td&gt;
&lt;td&gt;ap-northeast-0&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;S3 audit + EMS + FPolicy&lt;/td&gt;
&lt;td&gt;Basic Auth via &lt;code&gt;otlp_http&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Honeycomb&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;S3 audit + EMS + FPolicy&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;x-honeycomb-team&lt;/code&gt; via &lt;code&gt;otlp_http&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Backend&lt;/td&gt;
&lt;td&gt;Grafana + Honeycomb&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;Simultaneous delivery&lt;/td&gt;
&lt;td&gt;Both auth methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Backend&lt;/td&gt;
&lt;td&gt;Datadog + Grafana + Honeycomb&lt;/td&gt;
&lt;td&gt;✅ Verified&lt;/td&gt;
&lt;td&gt;Simultaneous 3-way delivery&lt;/td&gt;
&lt;td&gt;All three exporters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All three backends received the same structured attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;event.type&lt;/code&gt;, &lt;code&gt;user.name&lt;/code&gt;, &lt;code&gt;client.address&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fsxn.operation&lt;/code&gt;, &lt;code&gt;fsxn.path&lt;/code&gt;, &lt;code&gt;fsxn.result&lt;/code&gt;, &lt;code&gt;fsxn.svm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cloud.provider=aws&lt;/code&gt;, &lt;code&gt;cloud.platform=aws_fsx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;OTLP standardizes the producer-to-Collector contract, but backend-specific indexing, query semantics, and retention behavior still need to be validated per destination. OpenTelemetry is not a backend — it defines APIs, protocols, and Collector components for telemetry generation, collection, processing, and export. Storage, visualization, and alerting are handled by the backends themselves. See the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/backend-parity-matrix.md" rel="noopener noreferrer"&gt;Backend Parity Matrix&lt;/a&gt; and &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/poc-checklist.md" rel="noopener noreferrer"&gt;PoC Checklist&lt;/a&gt; for backend-specific validation details.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Proof: Zero Code Changes
&lt;/h2&gt;

&lt;p&gt;Here's the key evidence. The Lambda handler's SHA-256 hash is identical regardless of which backend receives the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;shasum &lt;span class="nt"&gt;-a&lt;/span&gt; 256 integrations/otel-collector/lambda/handler.py
&lt;span class="c"&gt;# Same hash whether targeting Datadog, Grafana Cloud, or Honeycomb&lt;/span&gt;
&lt;span class="c"&gt;# The file never changes — only the Collector config does&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What changes between backends? &lt;strong&gt;Only the OTel Collector config file.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Demonstration: Adding a Backend
&lt;/h3&gt;

&lt;p&gt;Starting state: Grafana Cloud only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before: single backend&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp_http/grafana&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding Honeycomb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# After: add 5 lines to exporters section + update pipeline&lt;/span&gt;
&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp_http/honeycomb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://api.honeycomb.io&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;x-honeycomb-team&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:HONEYCOMB_API_KEY}&lt;/span&gt;
      &lt;span class="na"&gt;x-honeycomb-dataset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:HONEYCOMB_DATASET}&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp_http/grafana&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;otlp_http/honeycomb&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the Collector. Done. No Lambda redeployment, no code review, no CI/CD pipeline for the shipper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demonstration: Removing a Backend
&lt;/h3&gt;

&lt;p&gt;Dropping Datadog during a migration to Grafana Cloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Remove from exporters list — that's it&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp_http/grafana&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# removed: datadog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Timestamp Rejection / Static Payload Gotcha
&lt;/h3&gt;

&lt;p&gt;Datadog documents that logs older than 18 hours are dropped at intake (&lt;a href="https://docs.datadoghq.com/api/latest/logs/" rel="noopener noreferrer"&gt;Datadog Logs API docs&lt;/a&gt;). Other backends may also reject or hide events with timestamps outside their accepted windows. In my testing, future timestamps also caused ingestion issues on some backends. When testing with static payloads, always generate fresh timestamps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Use the payload generator to create fresh timestamps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash scripts/generate-otlp-payload.sh &lt;span class="nt"&gt;--output&lt;/span&gt; /tmp/fresh-payload.json
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:4318/v1/logs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; @/tmp/fresh-payload.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Grafana Cloud Auth Format
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;loki&lt;/code&gt; exporter is &lt;strong&gt;NOT&lt;/strong&gt; the correct approach for OTLP → Grafana Cloud.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;code&gt;loki&lt;/code&gt; exporter with Loki push API&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;otlp_http/grafana&lt;/code&gt; with OTLP gateway endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Basic Auth value must be &lt;code&gt;base64(instanceId:apiToken)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate the auth value&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;your-instance-id&amp;gt;:&amp;lt;your-grafana-cloud-api-token&amp;gt;"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the instance ID is your numeric Grafana Cloud instance ID (found in Cloud Portal → OTLP configuration).&lt;/p&gt;

&lt;h3&gt;
  
  
  Honeycomb Key Types
&lt;/h3&gt;

&lt;p&gt;Honeycomb has two key types. Only ingest keys work for data ingestion:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key Prefix&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Works for OTLP?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hcaik_&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ingest API key&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hcxik_&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Environment key&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you see &lt;code&gt;401 Unauthorized&lt;/code&gt; from Honeycomb, check your key prefix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Colima Docker Compose Compatibility
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker compose&lt;/code&gt; v2 plugin is not available in Colima environments. All scripts in this repository detect this automatically and fall back to &lt;code&gt;docker run&lt;/code&gt;. This is expected — not an error.&lt;/p&gt;

&lt;p&gt;If you need compose-like orchestration on Colima, use the explicit &lt;code&gt;docker run&lt;/code&gt; commands shown in the Deployment section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Mistake: loki Exporter vs otlp_http
&lt;/h3&gt;

&lt;p&gt;A frequent misconfiguration when targeting Grafana Cloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ WRONG — loki exporter uses Loki-specific push API&lt;/span&gt;
&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;loki&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://logs-prod-&amp;lt;region&amp;gt;.grafana.net/loki/api/v1/push&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ CORRECT — otlp_http uses the OTLP gateway&lt;/span&gt;
&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp_http/grafana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://otlp-gateway-prod-&amp;lt;region&amp;gt;.grafana.net/otlp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OTLP gateway is Grafana Cloud's native OTLP ingestion endpoint. It handles logs, metrics, and traces through a single URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Model: How to Think About It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lambda Cost (OTLP Path vs Direct Send)
&lt;/h3&gt;

&lt;p&gt;In my validation, the OTLP Lambda was simpler and shorter-lived than the vendor-specific direct-send path. Your duration will vary depending on batching, payload size, network path, and backend response time.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Direct Send (Part 2)&lt;/th&gt;
&lt;th&gt;OTLP + Collector&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda complexity&lt;/td&gt;
&lt;td&gt;Vendor formatting + HTTP + retry&lt;/td&gt;
&lt;td&gt;OTLP POST to nearby Collector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda memory&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vendor SDK deps&lt;/td&gt;
&lt;td&gt;Yes (adds cold start)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry complexity&lt;/td&gt;
&lt;td&gt;Per-vendor&lt;/td&gt;
&lt;td&gt;Delegated to Collector&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  OTel Collector Cost
&lt;/h3&gt;

&lt;p&gt;The Collector introduces a fixed infrastructure cost that is independent of event volume:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Docker on local machine&lt;/td&gt;
&lt;td&gt;Development, testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker on EC2 Spot (t3.small)&lt;/td&gt;
&lt;td&gt;Low-volume production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECS Fargate (0.5 vCPU, 1GB)&lt;/td&gt;
&lt;td&gt;Production (no OS management)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECS Fargate + NAT Gateway&lt;/td&gt;
&lt;td&gt;VPC-internal production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  When to Use Each Pattern
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single vendor, low volume&lt;/td&gt;
&lt;td&gt;Direct Send (Part 2 pattern) — no Collector overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single vendor, high volume&lt;/td&gt;
&lt;td&gt;Collector (buffering + backpressure benefits)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-vendor evaluation&lt;/td&gt;
&lt;td&gt;Collector (add/remove exporters freely)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vendor migration in progress&lt;/td&gt;
&lt;td&gt;Collector (parallel delivery during cutover)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance: logs in multiple systems&lt;/td&gt;
&lt;td&gt;Collector (fan-out is a config change)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Collector has fixed infrastructure costs regardless of volume. As volume increases or vendors multiply, the Collector path becomes more cost-effective because it processes once and fans out. The Collector path centralizes fan-out outside the Lambda. Direct-send can also fan out within one Lambda, but that pushes vendor-specific formatting, retry behavior, and failure isolation back into application code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Backend ingest/retention costs are not included in these AWS-side estimates. Datadog, Grafana Cloud, and Honeycomb each have their own pricing models that can become the dominant cost at scale.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  When to Use This Pattern
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multi-Vendor Evaluation
&lt;/h3&gt;

&lt;p&gt;Want to try Honeycomb for a month alongside your existing Datadog setup? Add one exporter to the Collector config. No Lambda redeployment. No risk to your existing pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compliance: Logs in Multiple Systems
&lt;/h3&gt;

&lt;p&gt;Some organizations require audit logs in multiple systems — security team uses Splunk, dev team uses Datadog, compliance team needs a cold archive. The Collector fans out to all simultaneously from a single OTLP stream.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migration Between Vendors
&lt;/h3&gt;

&lt;p&gt;Moving from Datadog to Grafana Cloud? Run both exporters in parallel during migration. Verify data parity in the new system. Remove the old exporter when satisfied. Zero-downtime vendor migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Optimization: Route by Volume
&lt;/h3&gt;

&lt;p&gt;Use the Collector's processor pipeline to route high-volume noisy logs (read operations) to a cheaper backend while keeping security-critical events (deletes, permission changes) on a premium platform with alerting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;For production hardening, the repository includes guides covering VPC deployment, health monitoring, persistent buffering, security hardening, and benchmarking. Auto-scaling and Multi-AZ deployment are natural next steps for production Collector operations.&lt;/p&gt;

&lt;p&gt;For production and partner-led deployments, the repository includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/architecture-decision.md" rel="noopener noreferrer"&gt;Architecture Decision Record&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/vpc-deployment.md" rel="noopener noreferrer"&gt;VPC Deployment Guide&lt;/a&gt; — private networking, security groups, and Collector reachability from Lambda&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/config-governance.md" rel="noopener noreferrer"&gt;Config Governance Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/security-hardening.md" rel="noopener noreferrer"&gt;Security Hardening Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/operations-guide.md" rel="noopener noreferrer"&gt;Operations Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/cost-model.md" rel="noopener noreferrer"&gt;Cost Model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/poc-checklist.md" rel="noopener noreferrer"&gt;PoC Checklist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/routing-filtering-examples.md" rel="noopener noreferrer"&gt;Routing and Filtering Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/compliance-note.md" rel="noopener noreferrer"&gt;Compliance Evidence Note&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/migration-guide.md" rel="noopener noreferrer"&gt;Migration Guide&lt;/a&gt; — zero-downtime migration from direct-send to the Collector path&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/otel-semantic-mapping.md" rel="noopener noreferrer"&gt;OTel Semantic Mapping Guide&lt;/a&gt; — standard vs project-specific attributes, schema evolution, and what OTLP does not solve&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/backend-parity-matrix.md" rel="noopener noreferrer"&gt;Backend Parity Matrix&lt;/a&gt; — visibility and query behavior across Datadog, Grafana Cloud, and Honeycomb&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/glossary.md" rel="noopener noreferrer"&gt;Glossary / 用語集&lt;/a&gt; — English/Japanese OTel terminology used in this project&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/enterprise-workload-addendum.md" rel="noopener noreferrer"&gt;Enterprise Workload Addendum&lt;/a&gt; — SAP, VMware, and mission-critical workload considerations&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/storage-service-selection.md" rel="noopener noreferrer"&gt;Storage Service Selection Note&lt;/a&gt; — when to use FSx for ONTAP, Amazon S3, Amazon EFS, and Amazon EBS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;OTLP is the stable producer contract&lt;/strong&gt;. Your Lambda speaks one protocol; the Collector handles backend-specific exporters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OTel Collector is the routing and processing layer&lt;/strong&gt; that decouples log producers from observability backends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Lambda code changes&lt;/strong&gt; when switching or adding backends — verified with SHA-256 hash comparison.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-backend delivery is a config change&lt;/strong&gt;, not a code change. Add 5 lines of YAML, restart the Collector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All three FSx ONTAP event sources work&lt;/strong&gt;: FSx audit logs via S3 Access Point (Part 2), EMS webhooks (Part 3), and FPolicy file operations (Part 4).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collector economics improve&lt;/strong&gt; as volume increases or vendors multiply — fixed Collector cost is amortized across all destinations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with direct send&lt;/strong&gt; (Part 2) for simplicity. &lt;strong&gt;Graduate to the Collector&lt;/strong&gt; when you need multi-backend, vendor migration, or volume-based routing.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Series Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/why-your-fsx-for-ontap-audit-logs-deserve-better-than-ec2-kod"&gt;Why Your FSx for ONTAP Logs Deserve Better&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/shipping-fsx-for-ontap-logs-to-datadog-the-serverless-way-n9c"&gt;Shipping FSx for ONTAP Logs to Datadog — The Serverless Way&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Event-Driven Ransomware Detection with ONTAP ARP + Datadog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/fpolicy-file-activity-pipeline-ontap-to-datadog-via-ecs-fargate-2ing"&gt;FPolicy File Activity Pipeline — ONTAP to Datadog via ECS Fargate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: Escape Vendor Lock-in with OTel Collector (this post)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Questions about the OTel Collector pattern or multi-backend delivery? Drop a comment below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Previous: &lt;a href="https://dev.to/aws-builders/fpolicy-file-activity-pipeline-ontap-to-datadog-via-ecs-fargate-2ing"&gt;Part 4 — FPolicy File Activity Pipeline&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations" rel="noopener noreferrer"&gt;github.com/Yoshiki0705/fsxn-observability-integrations&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>aws</category>
      <category>devops</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>FPolicy File Activity Pipeline — ONTAP to Datadog via ECS Fargate</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Mon, 18 May 2026 02:31:34 +0000</pubDate>
      <link>https://forem.com/aws-builders/fpolicy-file-activity-pipeline-ontap-to-datadog-via-ecs-fargate-2ing</link>
      <guid>https://forem.com/aws-builders/fpolicy-file-activity-pipeline-ontap-to-datadog-via-ecs-fargate-2ing</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;ONTAP FPolicy pushes file operation notifications over a persistent TCP connection. We run a lightweight Python server on ECS Fargate that receives these events, normalizes them, and forwards them to SQS → Lambda → Datadog. In my validation environment, create events reached Datadog in about 6 seconds. Rename/delete behavior depends on FPolicy mode, protocol, and FSx for ONTAP behavior, so this post documents both the working path and the limitations observed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update — production hardening path&lt;/strong&gt;&lt;br&gt;
This article remains the Datadog-specific introduction to the FPolicy file activity pipeline. Since publishing it, the repository has been expanded with production-readiness guidance, governance and security review checklists, sample payloads, CI policy, cfn-guard rules, and shared Python helpers for observability and idempotent object processing.&lt;/p&gt;

&lt;p&gt;For production planning, start from the repository README:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose Your Path&lt;/li&gt;
&lt;li&gt;Recommended first 30 minutes&lt;/li&gt;
&lt;li&gt;Production Readiness Levels&lt;/li&gt;
&lt;li&gt;PoC Success Criteria&lt;/li&gt;
&lt;li&gt;Security Review Checklist&lt;/li&gt;
&lt;li&gt;Governance and Compliance Guide&lt;/li&gt;
&lt;li&gt;CI Policy&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The FPolicy pattern has also been expanded with Persistent Store guidance, idempotent object processing, EventBridge dispatch, and a hybrid polling/event-driven migration path. This Part 4 article focuses on the Datadog delivery path; the repository now documents the broader production baseline.
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why FPolicy Needs Fargate
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Part 3&lt;/a&gt;, we showed how EMS webhooks deliver ARP alerts via API Gateway → Lambda. That works because EMS uses standard HTTPS.&lt;/p&gt;

&lt;p&gt;FPolicy is different. ONTAP's FPolicy subsystem uses a &lt;strong&gt;proprietary binary protocol over persistent TCP connections&lt;/strong&gt;. ONTAP initiates the connection to the FPolicy server and maintains it with periodic KeepAlive messages. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Lambda&lt;/strong&gt; — No persistent TCP connections, max 15-minute timeout&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;API Gateway&lt;/strong&gt; — HTTP/HTTPS only, no raw TCP&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;ECS Fargate&lt;/strong&gt; — Persistent TCP listener, private IP, auto-restart&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why I Did Not Use an NLB in This Validation
&lt;/h3&gt;

&lt;p&gt;I tested an NLB-based approach, but it did not work reliably in my validation. The issue was not that NLB cannot forward binary TCP traffic; it can. The challenge was FPolicy's stateful session negotiation and ONTAP's expectation of configured FPolicy server IPs. Health checks and connection behavior introduced additional complexity. For this validation, the simplest reliable path was to let ONTAP connect directly to the Fargate task's private IP and automate external-engine IP updates on task restart.&lt;/p&gt;

&lt;p&gt;The Fargate task runs a Python server that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Listens on TCP:9898&lt;/li&gt;
&lt;li&gt;Handles FPolicy protocol negotiation (version handshake)&lt;/li&gt;
&lt;li&gt;Receives KeepAlive messages (connection health)&lt;/li&gt;
&lt;li&gt;Parses file operation notifications&lt;/li&gt;
&lt;li&gt;Forwards structured events to SQS&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SMB/NFS Client
    │ file create/write/rename/delete
    ▼
FSx for ONTAP (FPolicy enabled)
    │ proprietary TCP protocol
    ▼
ECS Fargate (TCP:9898)
    │ parse → normalize → forward
    ▼
SQS Queue
    │ event source mapping
    ▼
Lambda (fpolicy_handler)
    │ format → ship
    ▼
Datadog Logs API v2 (source:fsxn-fpolicy)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key design decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ONTAP connects TO Fargate&lt;/strong&gt; — the Fargate task must be reachable on a private IP. Because that IP can change on task restart, the ONTAP external engine must be updated automatically or operationally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS decouples&lt;/strong&gt; the TCP server from the shipping logic — if Datadog is slow, events buffer in SQS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda handles Datadog shipping&lt;/strong&gt; — retry logic, batch formatting, API key management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No NLB&lt;/strong&gt; — ONTAP connects directly to the Fargate task's private IP&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production Boundary: Why FPolicy Needs More Than Lambda
&lt;/h2&gt;

&lt;p&gt;The audit-log and EMS paths are natural fits for Lambda:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit logs are file/object reads through the FSx for ONTAP S3 Access Point read path&lt;/li&gt;
&lt;li&gt;EMS events are HTTPS webhook payloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FPolicy is different. ONTAP FPolicy uses a persistent TCP connection to an external FPolicy server. That makes it a poor fit for API Gateway + Lambda as the first receiver.&lt;/p&gt;

&lt;p&gt;This is why the production-oriented path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ONTAP FPolicy
  → ECS Fargate TCP listener
  → SQS
  → Lambda shipper
  → Datadog

## Deployment

### Prerequisites

- FSx for ONTAP file system with a CIFS-enabled SVM
- VPC with private subnets (same as FSx for ONTAP)
- ECR repository with the FPolicy server image
- Private subnet egress for Fargate: either a NAT Gateway or VPC endpoints for ECR image pull, CloudWatch Logs, and SQS access

### Step 1: Deploy the Fargate Stack

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
aws cloudformation deploy \&lt;br&gt;
  --template-file shared/templates/fpolicy-server-fargate.yaml \&lt;br&gt;
  --stack-name fsxn-fpolicy-server \&lt;br&gt;
  --parameter-overrides \&lt;br&gt;
    VpcId= \&lt;br&gt;
    SubnetIds= \&lt;br&gt;
    FsxnSvmSecurityGroupId= \&lt;br&gt;
    ContainerImage=.dkr.ecr..amazonaws.com/fsxn-fpolicy-server:latest \&lt;br&gt;
  --capabilities CAPABILITY_NAMED_IAM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
This creates:
- ECS Cluster + Fargate Service (1 task)
- SQS Queue for FPolicy events
- Security Group (inbound TCP:9898 from FSx SG)
- CloudWatch Log Group

### Step 2: Deploy the Datadog Shipping Lambda

The template accepts the SQS queue ARN as a parameter and automatically creates the event source mapping:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;/p&gt;
&lt;h1&gt;
  
  
  Get the SQS queue ARN from Step 1 outputs
&lt;/h1&gt;

&lt;p&gt;SQS_ARN=$(aws cloudformation describe-stacks \&lt;br&gt;
  --stack-name fsxn-fpolicy-server \&lt;br&gt;
  --query "Stacks[0].Outputs[?OutputKey=='FPolicyQueueArn'].OutputValue" \&lt;br&gt;
  --output text)&lt;/p&gt;

&lt;p&gt;aws cloudformation deploy \&lt;br&gt;
  --template-file integrations/datadog/template-ems-fpolicy.yaml \&lt;br&gt;
  --stack-name fsxn-datadog-ems-fpolicy \&lt;br&gt;
  --parameter-overrides \&lt;br&gt;
    DatadogApiKeySecretArn= \&lt;br&gt;
    DatadogSite=ap1.datadoghq.com \&lt;br&gt;
    FPolicySqsQueueArn=${SQS_ARN} \&lt;br&gt;
  --capabilities CAPABILITY_NAMED_IAM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
This creates the Lambda function with an SQS event source mapping — no manual `create-event-source-mapping` needed.

### Step 3: Get the Fargate Task IP

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
TASK_ARN=$(aws ecs list-tasks \&lt;br&gt;
  --cluster fsxn-fpolicy-server-cluster \&lt;br&gt;
  --service-name fsxn-fpolicy-server-service \&lt;br&gt;
  --query "taskArns[0]" --output text)&lt;/p&gt;

&lt;p&gt;aws ecs describe-tasks \&lt;br&gt;
  --cluster fsxn-fpolicy-server-cluster \&lt;br&gt;
  --tasks $TASK_ARN \&lt;br&gt;
  --query "tasks[0].containers[0].networkInterfaces[0].privateIpv4Address" \&lt;br&gt;
  --output text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## ONTAP FPolicy Configuration

&amp;gt; **CLI note**: Some ONTAP versions show these commands under `vserver fpolicy ...`, while newer CLI contexts may allow shortened forms. Use the command form supported by your ONTAP version. The examples below use the form validated in my environment (FSx for ONTAP 9.17.1). See [NetApp CLI reference](https://docs.netapp.com/us-en/ontap-cli-9151/vserver-fpolicy-policy-external-engine-create.html) for the full command syntax.

FPolicy requires three components: an External Engine (where to send events), an Event (what to monitor), and a Policy (linking them together).

### Create the External Engine

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
shell&lt;br&gt;
vserver fpolicy policy external-engine create -vserver  \&lt;br&gt;
  -engine-name fpolicy_aws_engine \&lt;br&gt;
  -primary-servers  \&lt;br&gt;
  -port 9898 \&lt;br&gt;
  -extern-engine-type asynchronous \&lt;br&gt;
  -ssl-option no-auth&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;gt; **Production note**: For production deployments, evaluate `server-auth` or `mutual-auth` instead of `no-auth`, and validate certificate handling between ONTAP and the FPolicy server. See [NetApp FPolicy external engine documentation](https://docs.netapp.com/us-en/ontap/nas-audit/create-fpolicy-external-engine-task.html).

### Create the FPolicy Event

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
shell&lt;br&gt;
vserver fpolicy policy event create -vserver  \&lt;br&gt;
  -event-name cifs_file_events \&lt;br&gt;
  -protocol cifs \&lt;br&gt;
  -file-operations create,write,rename,delete&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;gt; **Tip**: For write-heavy workloads, review the protocol-specific FPolicy filters supported by your ONTAP version and protocol. Where supported, use close/modify-oriented filters to reduce duplicate or noisy write events.

### Create and Enable the Policy

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
shell&lt;br&gt;
vserver fpolicy policy create -vserver  \&lt;br&gt;
  -policy-name fpolicy_aws \&lt;br&gt;
  -events cifs_file_events \&lt;br&gt;
  -engine fpolicy_aws_engine \&lt;br&gt;
  -is-mandatory false&lt;/p&gt;

&lt;p&gt;vserver fpolicy enable -vserver  \&lt;br&gt;
  -policy-name fpolicy_aws \&lt;br&gt;
  -sequence-number 1&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
This example uses an asynchronous, non-mandatory policy so client file operations are not blocked by FPolicy server processing or Datadog delivery. If the FPolicy server is unavailable, file operations continue unimpeded — but notifications may be buffered or lost depending on your ONTAP version and configuration.

### Verify Connection

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
shell&lt;br&gt;
vserver fpolicy show-engine -vserver  -engine-name fpolicy_aws_engine&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
You should see `connected` status. In the ECS logs, KeepAlive messages confirm the connection:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
console&lt;br&gt;
[INFO] fpolicy-server: [+] Connection from ('10.0.x.x', 44107)&lt;br&gt;
[INFO] fpolicy-server: [Handshake] Policy=fpolicy_aws | Session=... | VsUUID=...&lt;br&gt;
[INFO] fpolicy-server: [Send] NEGO_RESP | Version=1.2 | Policy=fpolicy_aws&lt;br&gt;
[INFO] fpolicy-server: [KeepAlive] Received — connection healthy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## E2E Validation Results

File operations on the SMB share produce events that flow through the entire pipeline:

| Operation | ECS Log | SQS | Lambda | Datadog | Latency |
|-----------|---------|-----|--------|---------|---------|
| create `blog_demo_create.txt` | ✅ | ✅ | ✅ shipped:1 | ✅ | ~6 seconds |
| create `blog_demo_write.txt` | ✅ | ✅ | ✅ shipped:1 | ✅ | ~6 seconds |
| create `confidential_report_2026.xlsx` | ✅ | ✅ | ✅ shipped:1 | ✅ | ~6 seconds |

### ECS Fargate Logs — Connection Lifecycle

The FPolicy server logs show the complete lifecycle: server start → ONTAP connection → protocol handshake → KeepAlive → file events → SQS delivery.

![ECS Fargate CloudWatch Logs](https://raw.githubusercontent.com/Yoshiki0705/fsxn-observability-integrations/main/docs/screenshots/aws-ecs-fpolicy-logs.png)

### Lambda CloudWatch Logs — Event Processing

Each SQS message triggers a Lambda invocation. Processing time is typically 30-50ms per event.

![Lambda CloudWatch Logs](https://raw.githubusercontent.com/Yoshiki0705/fsxn-observability-integrations/main/docs/screenshots/aws-lambda-fpolicy-logs.png)

### Datadog Log Explorer

Query: `source:fsxn-fpolicy`

Each event contains structured attributes:
- `operation_type`: The file operation (create, write, rename, delete)
- `file_path`: The file that was operated on
- `client_ip`: The client that performed the operation
- `volume_name`: The ONTAP volume
- `svm`: The ONTAP SVM name (may show "unknown" if not resolved from handshake context)
- `timestamp`: When the operation occurred

![FPolicy events in Datadog Log Explorer](https://raw.githubusercontent.com/Yoshiki0705/fsxn-observability-integrations/main/docs/screenshots/datadog-fpolicy-full-path.png)

![FPolicy event detail — structured attributes visible in the side panel](https://raw.githubusercontent.com/Yoshiki0705/fsxn-observability-integrations/main/docs/screenshots/datadog-fpolicy-detail.png)

## Correlating FPolicy with ARP

The real power emerges when you combine FPolicy file activity with ARP ransomware detection from Part 3:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;br&gt;
source:(fsxn-fpolicy OR fsxn-ems) @attributes.svm:svm-prod-01&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
This correlation query shows:
1. **ARP alert** (from EMS): "Ransomware detected on volume X"
2. **File operations** (from FPolicy): Which user, from which IP, created/renamed which files

Together they answer the critical incident response questions: *What happened, who did it, and from where?*

### Security Use Case: Detecting Suspicious File Creation Bursts

With FPolicy create events in Datadog, you can create a Monitor that fires when a single client creates more than 50 files in 5 minutes — a potential indicator of ransomware encryption or unauthorized bulk operations:

**Datadog Monitor query:**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;br&gt;
logs("source:fsxn-fpolicy @attributes.operation_type:create").rollup("count").by("@attributes.client_ip").last("5m") &amp;gt; 50&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
**Alert message:**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;br&gt;
🚨 Suspicious file creation burst detected on FSx for ONTAP&lt;/p&gt;

&lt;p&gt;Client IP: {{@attributes.client_ip}}&lt;br&gt;
Volume: {{@attributes.volume_name}}&lt;br&gt;
Count: {{value}} file creations in 5 minutes&lt;/p&gt;

&lt;p&gt;Investigate immediately — check if this is authorized batch processing or potential ransomware activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;gt; **Note on delete monitoring**: If your FPolicy configuration and ONTAP version reliably deliver delete events (e.g., synchronous mode or a future ONTAP release), you can extend this pattern to bulk deletion detection. In my async-mode validation, delete notifications were not reliably delivered — I recommend using audit logs from [Part 2](https://dev.to/aws-builders/shipping-fsx-for-ontap-logs-to-datadog-the-serverless-way-n9c) for delete-event completeness.

This is difficult to achieve with traditional audit log polling, which depends on rotation and scheduler intervals. FPolicy's event-driven delivery makes sub-minute detection possible for the operations it reliably captures.

## Operational Considerations

### Fargate Task IP Changes

When a Fargate task restarts (deployment, crash, scaling), it gets a new private IP. ONTAP's External Engine must be updated with the new IP. Options:

1. **Manual update**: `vserver fpolicy policy external-engine modify -primary-servers &amp;lt;new-ip&amp;gt;`
2. **Automated**: Lambda triggered by ECS task state change → ONTAP REST API update

The repository includes a helper script (`shared/scripts/fpolicy-update-engine-ip.sh --auto`) that detects the current task IP and updates the ONTAP engine. For full automation, wire an EventBridge rule on ECS task state changes to an update Lambda — this is not included in the base stack but is straightforward to add. Automated updates require network reachability to the ONTAP management endpoint and credentials (stored in Secrets Manager) with permission to modify the FPolicy external engine.

### Restart Resilience — Validated

I tested the full restart cycle to confirm the pipeline recovers gracefully:

| Step | Result | Time |
|------|--------|------|
| Stop Fargate (scale to 0) | Task stopped | ~30s |
| Restart Fargate (scale to 1) | New task, new IP | ~45s |
| Update ONTAP Engine IP | Reconnection | ~20s |
| File operation after restart | Event delivered to Datadog | ~6s |
| **Total recovery time** | | **~2 minutes** |

The Lambda's retry logic also proved itself: on the first request after reconnection, a transient `RemoteDisconnected` error occurred. The exponential backoff retry succeeded on the second attempt — exactly the behavior we designed for.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
console&lt;br&gt;
[WARNING] HTTP error shipping to Datadog (attempt 1/3): RemoteDisconnected&lt;br&gt;
[INFO]    Processing complete: {"statusCode": 200, "body": {"shipped": 1}}&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
### Cost Profile

| Component | Monthly Cost (estimate) |
|-----------|------------------------|
| Fargate (0.25 vCPU, 0.5 GB) | ~$10 |
| SQS (low volume) | &amp;lt; $1 |
| Lambda (event-driven) | &amp;lt; $1 |
| CloudWatch Logs | ~$2 |
| **Total** | **~$14/month** |

Compare this to an always-on EC2-based collector, plus OS patching, agent management, and HA considerations. Exact EC2 costs vary by region and instance type.

&amp;gt; This is an AWS-side estimate and excludes Datadog ingest/retention costs, NAT Gateway or VPC endpoint charges, ECR storage, and high-volume CloudWatch Logs.

### Scaling

A single Fargate task is sufficient for the low-volume validation scenarios in this post. The architecture can scale by tuning Fargate CPU/memory, SQS buffering, and Lambda concurrency, but you should benchmark your own workload before assuming a specific events/sec capacity.

### Monitoring

Key CloudWatch metrics to watch:
- `ECS/CPUUtilization` — Fargate task health
- `SQS/ApproximateNumberOfMessagesVisible` — Queue depth (should stay near 0)
- `Lambda/Errors` — Shipping failures
- `Lambda/Duration` — Processing time per batch

## The FPolicy Server

The FPolicy server (`shared/fpolicy-server/fpolicy_server.py`) implements:

- **Protocol negotiation**: Responds to ONTAP's version handshake
- **KeepAlive handling**: Acknowledges connection health checks
- **Event parsing**: Extracts file path, operation, user, client IP from binary frames
- **SQS forwarding**: Sends normalized JSON events to the queue
- **Write coalescing**: Configurable delay to batch rapid write events (default: 5 seconds)

The server runs in `realtime` mode — events are forwarded as they arrive, with optional write-complete delay to avoid duplicate notifications for multi-write operations.

## Limitations and Future Work

### Rename/Delete Events Not Delivered in Async Mode

In my E2E testing, ONTAP did not deliver rename or delete notifications to the FPolicy server in asynchronous mode — even though these operations are configured in the FPolicy event definition. Only create events were reliably delivered. This appears to be a limitation of FSx for ONTAP's FPolicy implementation in async mode for certain operation types.

**Workaround options:**
- Use synchronous mode (adds latency to file operations — not recommended for production)
- Combine FPolicy (event-driven create) with audit log polling (catches rename/delete in EVTX)
- Accept create-only monitoring for event-driven alerting, use audit logs for forensic completeness

### NFS Protocol Support

| Protocol | FPolicy Support | Notes |
|----------|----------------|-------|
| SMB/CIFS | ✅ Verified | Primary validation protocol |
| NFSv3 | ✅ Supported | Requires explicit `vers=3` mount option |
| NFSv4.0 | ✅ Supported | Requires explicit `vers=4.0` |
| NFSv4.1 | ✅ Supported | Requires ONTAP 9.15.1+, explicit `vers=4.1` |
| NFSv4.2 | ❌ Not supported | ONTAP FPolicy does not monitor NFSv4.2 operations |

For protocol support details, verify your ONTAP version. NetApp [documents](https://kb.netapp.com/onprem/ontap/da/NAS/Does_ONTAP_support_FPolicy_for_NFS_4.2) that FPolicy does not currently support NFSv4.2; supported NFS protocols include NFSv3, NFSv4.0, and NFSv4.1 (ONTAP 9.15.1+).

**Critical gotcha:** `mount -o vers=4` on modern Linux negotiates to NFSv4.2, which ONTAP FPolicy does **not** support. Always use explicit version: `mount -o vers=4.1` or `vers=3`.

**NFS + FPolicy latency:** NFSv3 lacks close semantics, so the FPolicy server cannot know when a write is complete. The server uses a configurable `WRITE_COMPLETE_DELAY_SEC` (default: 5s) to wait before forwarding the event. This adds latency but prevents premature processing of incomplete files.

**NFS write hang (observed):** In some configurations, NFS write operations may hang when FPolicy is enabled — even with `is-mandatory=false`. This is a [known ONTAP behavior](https://kb.netapp.com/onprem/ontap/da/NAS/NFS_hung_slowness_issue_when_dealing_with_long_path_names_with_FPolicy_enabled) related to FPolicy notification processing. If you experience this, verify your ONTAP version and consider limiting FPolicy scope to specific volumes.

### User Identity

In the current implementation, the `user` field may be empty for some operations depending on ONTAP's FPolicy notification content. The FPolicy binary frame includes user identity in extended attributes that require additional parsing logic. Future versions will extract this from the NOTI_REQ body.

### Event Durability During Restarts

In my validation, events generated while the Fargate server was disconnected were not observed downstream in Datadog after reconnection. Treat FPolicy delivery during server outages as something you must validate in your own environment.

ONTAP [documentation](https://docs.netapp.com/us-en/ontap/nas-audit/synchronous-asynchronous-notifications-concept.html) describes buffering behavior for asynchronous notifications — notifications generated during a network outage are stored on the storage node and can be fetched when the server comes back online. Beginning with ONTAP 9.14.1, [FPolicy persistent store](https://docs.netapp.com/us-en/ontap/nas-audit/persistent-stores.html) support is available for asynchronous non-mandatory policies. If you cannot tolerate event loss during FPolicy server restarts, evaluate persistent store and validate the behavior on your FSx for ONTAP version.

## Try It Yourself

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;/p&gt;

&lt;h1&gt;
  
  
  Clone the repository
&lt;/h1&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations.git" rel="noopener noreferrer"&gt;https://github.com/Yoshiki0705/fsxn-observability-integrations.git&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploy prerequisites (if not already done)
&lt;/h1&gt;

&lt;p&gt;aws cloudformation deploy \&lt;br&gt;
  --template-file shared/templates/fpolicy-server-fargate.yaml \&lt;br&gt;
  --stack-name fsxn-fpolicy-server \&lt;br&gt;
  --parameter-overrides \&lt;br&gt;
    VpcId= \&lt;br&gt;
    SubnetIds= \&lt;br&gt;
    FsxnSvmSecurityGroupId= \&lt;br&gt;
    ContainerImage= \&lt;br&gt;
  --capabilities CAPABILITY_NAMED_IAM&lt;/p&gt;

&lt;h1&gt;
  
  
  Configure ONTAP FPolicy (see ONTAP section above)
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Create a file on the SMB share
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Check Datadog: source:fsxn-fpolicy
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


## Where FPolicy Fits in ONTAP Telemetry

This series covers three ONTAP telemetry sources. Each serves a different purpose:

| Use Case | Best Source | Latency | Coverage |
|----------|-------------|---------|----------|
| Compliance audit trail | Audit logs ([Part 2](https://dev.to/aws-builders/shipping-fsx-for-ontap-logs-to-datadog-the-serverless-way-n9c)) | Minutes (scheduler interval) | Complete historical record |
| Ransomware detection | ARP via EMS ([Part 3](https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda)) | ~30 seconds (webhook) | ML-based pattern detection |
| Event-driven file activity signal | FPolicy (this post) | ~6 seconds (TCP) | Create events validated; other operations depend on mode/version |
| Forensic investigation | Audit logs + FPolicy correlation | Combined | Timeline reconstruction |

**FPolicy is not a replacement for audit logs.** It provides an event-driven signal for detection and alerting. Audit logs provide the authoritative, complete historical record for compliance and forensics. Use them together.

## Key Takeaways

1. **Use Fargate for FPolicy TCP listener** — Lambda cannot maintain persistent TCP connections. Fargate provides the long-running listener without OS management.
2. **Use SQS to decouple ingestion from shipping** — If Datadog is slow or Lambda is throttled, events buffer safely in SQS.
3. **Validate operation coverage in your environment** — Async mode reliably delivered create events in my testing. Rename/delete behavior varies by ONTAP version and mode.
4. **Use audit logs for forensic completeness** — FPolicy provides event-driven signal for detection; audit logs (Part 2) provide the complete historical record.
5. **Treat FPolicy as event-driven alerting, not full audit replacement** — The two are complementary, not interchangeable.

## Production Considerations Beyond This Validation

This post validates the end-to-end path. For production deployments, the following topics warrant additional design work:

| Topic | Key Questions |
|-------|--------------|
| **HA / Multi-AZ** | ONTAP external engine supports `primary-servers` and `secondary-servers`. How to run multiple Fargate tasks across AZs? |
| **Scope Design** | Which volumes, operations, and protocols to monitor? How to avoid noisy workloads? |
| **Security Hardening** | TLS/mTLS for FPolicy, ECR image scanning, VPC Flow Logs, task role least-privilege |
| **Cost Model** | FPolicy generates events per file operation — Datadog ingest can become the dominant cost at scale |
| **Operations Runbook** | Task restart, engine disconnected, SQS backlog, Datadog missing events, NFS hang |
| **Stable Endpoint** | Auto-update Lambda for engine IP, or primary/secondary server design for zero-downtime restarts |

These topics are documented in the repository:

- **[Production Architecture Patterns](https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/fpolicy-production-architecture-patterns.md)** — Single task, primary/secondary, auto-update, multi-AZ patterns with failure mode matrix
- **[Operational Guide](https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/fpolicy-operational-guide.md)** — 4-layer health model, runbooks, IP reconciliation, synthetic health check
- **[PoC Checklist](https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/fpolicy-poc-checklist.md)** — Preconditions, scope, validation steps, success criteria, go/no-go

Contributions and questions are welcome.

## Series Navigation

- **Part 1**: [Why Your FSx for ONTAP Logs Deserve Better](https://dev.to/aws-builders/why-your-fsx-for-ontap-audit-logs-deserve-better-than-ec2-kod)
- **Part 2**: [Shipping FSx for ONTAP Logs to Datadog, The Serverless Way](https://dev.to/aws-builders/shipping-fsx-for-ontap-logs-to-datadog-the-serverless-way-n9c)
- **Part 3**: [Event-Driven Ransomware Detection with ONTAP ARP + Datadog](https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda)
- **Part 4**: FPolicy File Activity Pipeline (this post)

Coming next:
- **Splunk**: Replacing EC2 + Universal Forwarder with Lambda + HEC
- **OpenTelemetry**: The vendor-neutral escape hatch

---

*Questions about FPolicy or the Fargate architecture? Drop a comment below.*

*Previous: [Part 3 — Event-Driven Ransomware Detection with ONTAP ARP + Datadog](https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda)*

**GitHub**: [github.com/Yoshiki0705/fsxn-observability-integrations](https://github.com/Yoshiki0705/fsxn-observability-integrations)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>datadog</category>
      <category>amazonfsxfornetappontap</category>
    </item>
  </channel>
</rss>
