<?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>DEV Community: Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</title>
    <description>The latest articles on DEV Community by Yoshiki Fujiwara(藤原 善基)@AWS Community Builder (@yoshikifujiwara).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1143688%2F2e0886ff-292c-4e8f-a588-bc7629c2321b.jpeg</url>
      <title>DEV Community: Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</title>
      <link>https://dev.to/yoshikifujiwara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yoshikifujiwara"/>
    <language>en</language>
    <item>
      <title>What to build a file portal on — Nx Plugin for AWS 1.0, AWS Blocks and Amplify Gen 2, same four features (File Portal Part 7)</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Wed, 16 Sep 2026 14:34:33 +0000</pubDate>
      <link>https://dev.to/aws-builders/what-a-stack-deletion-leaves-behind-nx-plugin-for-aws-10-aws-blocks-and-amplify-gen-2-compared-10fg</link>
      <guid>https://dev.to/aws-builders/what-a-stack-deletion-leaves-behind-nx-plugin-for-aws-10-aws-blocks-and-amplify-gen-2-compared-10fg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Nx Plugin for AWS reached 1.0 and AWS Blocks appeared in preview, so starting an application on AWS now has three front doors. I took all three to the same scope, counted the resources the same way, deployed them for real, implemented four features on each, and deleted everything at the end.&lt;/p&gt;

&lt;p&gt;This article stands on its own. It continues a series, but the only thing it assumes is that a file portal is running on an FSx for ONTAP S3 Access Point.&lt;/p&gt;

&lt;p&gt;What prompted it was reading &lt;a href="https://zenn.dev/aws_japan/articles/nx-plugin-for-aws-nx-explained" rel="noopener noreferrer"&gt;a Zenn article&lt;/a&gt; by an AWS SA that compared all three. Someone else's comparison is useful, but as a basis for a decision in my own repository it is thin: you have to deploy and delete before you can say you compared anything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887"&gt;Part 1&lt;/a&gt; was about the application layer — what to build a file portal &lt;em&gt;with&lt;/em&gt;. This one is about &lt;strong&gt;the tooling underneath it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What the verification covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The resource count of each generated output, and the fixed cost its defaults imply&lt;/li&gt;
&lt;li&gt;Deploying each one for real and exercising it&lt;/li&gt;
&lt;li&gt;The differences that appear once the same four features are implemented on all three&lt;/li&gt;
&lt;li&gt;What survives a stack deletion, and the order in which to clean it up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of scope: which tool is better, Nx or monorepos as concepts, and keeping generated code current over time.&lt;/p&gt;

&lt;p&gt;The conclusions first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choosing a tool is a decision you can take back.&lt;/strong&gt; The data path is common to all three, and switching tools does not mean rebuilding the FSx for ONTAP side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The difference is not "can it be built" but what gets fixed in place once you have chosen.&lt;/strong&gt; All three deployed without trouble.&lt;/li&gt;
&lt;li&gt;The generated defaults mean &lt;strong&gt;fixed monthly cost at zero requests.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;What a stack deletion leaves behind &lt;strong&gt;comes in two kinds&lt;/strong&gt;. &lt;code&gt;Retain&lt;/code&gt; and deletion protection are the generator's defaults and can be released on the generating side. Log groups that never appear in the template belong to CDK's internals, and there is no route to release those. The KMS waiting period is the only thing that cannot be avoided.&lt;/li&gt;
&lt;li&gt;For some resources, notably KMS keys, &lt;code&gt;DELETE_COMPLETE&lt;/code&gt; means "scheduled for deletion after a 30-day wait" rather than "gone".&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Switching tools does not touch the data foundation underneath
&lt;/h2&gt;

&lt;p&gt;The components side by side, matched to one scope: web, auth, API and a table. The data path below the S3 Access Point is this portal's real configuration and is identical in all three.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb6ye3ovtqvfi8ndmjbeo.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb6ye3ovtqvfi8ndmjbeo.png" alt="Components of the file portal composed on Amplify Gen 2. From a web browser through AWS Amplify, AWS AppSync and AWS Lambda to an Amazon S3 Access Point, Amazon FSx for NetApp ONTAP and NFS / SMB clients. Amazon Cognito beside AWS Amplify, Amazon DynamoDB beside AWS Lambda. Zero AWS WAF web ACLs and zero customer managed KMS keys are created by default" width="800" height="1261"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1: components of the file portal composed on Amplify Gen 2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Amplify Gen 2 is what this portal actually runs on. No perimeter resources — AWS WAF, customer managed KMS keys — are created by default. Authentication is Amazon Cognito, on the default &lt;code&gt;ESSENTIALS&lt;/code&gt; tier.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8ur6vib97ibttu831ez.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8ur6vib97ibttu831ez.png" alt="Components of the file portal composed on the AWS Blocks production preset. From a web browser through Amazon CloudFront, Amazon API Gateway and 13 AWS Lambda functions to an Amazon S3 Access Point, Amazon FSx for NetApp ONTAP and NFS / SMB clients. There is no Amazon Cognito; two of the four Amazon DynamoDB tables carry authentication. One customer managed KMS key" width="800" height="1261"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: components of the file portal composed on the AWS Blocks production preset&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What to read in the AWS Blocks figure is the absence of Amazon Cognito. Authentication is &lt;code&gt;AuthBasic&lt;/code&gt; (DynamoDB plus JWTs), so it moves out of a managed service and into the tables. Two of the four tables are for auth, and all four carry deletion protection.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgu31p67jnm49x0een3uq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgu31p67jnm49x0een3uq.png" alt="Components of the file portal composed on Nx Plugin for AWS. From a web browser through Amazon CloudFront, Amazon API Gateway (tRPC), AWS Lambda and Amazon DynamoDB to an Amazon S3 Access Point, Amazon FSx for NetApp ONTAP and NFS / SMB clients. Two AWS WAF web ACLs sit on the path (the CLOUDFRONT-scoped one in us-east-1, plus a REGIONAL one), Amazon Cognito is on PLUS with MFA required, and there are four AWS Key Management Service keys" width="800" height="1357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3: components of the file portal composed on Nx Plugin for AWS&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For Nx Plugin for AWS the difference is more perimeter in the figure: two AWS WAF web ACLs on the path (a third sits in a separate us-east-1 stack), Amazon Cognito on &lt;code&gt;PLUS&lt;/code&gt; with MFA required, and four KMS keys. Those three are the fixed-cost difference described later.&lt;/p&gt;

&lt;p&gt;That the three figures match below the access point matters: &lt;strong&gt;switching tools does not mean rebuilding the FSx for ONTAP side.&lt;/strong&gt; Nothing was created or deleted on the FSx for ONTAP side during this verification.&lt;/p&gt;

&lt;p&gt;Reaching it was also identical on all three. An Internet-origin access point is reached by passing the alias as a bucket name to the standard S3 SDK — no endpoint override, no VPC attachment. A file on the NAS can be found, opened and written from a browser with no VPN and no SMB mount, and through phase 3 the functions do not even need to sit in a VPC.&lt;/p&gt;


&lt;h2&gt;
  
  
  The same four features on all three tools (2026-09-14)
&lt;/h2&gt;

&lt;p&gt;Everything above compares the outputs as generated. What someone choosing a tool actually wants to know is the difference once their own application sits on it. So a few days later I implemented the same four features — sign in, list, read, upload (&lt;code&gt;listFiles&lt;/code&gt; / &lt;code&gt;readFile&lt;/code&gt; / &lt;code&gt;uploadFile&lt;/code&gt;) — on each and ran them against the same FSx for ONTAP S3 Access Point.&lt;/p&gt;

&lt;p&gt;The target was fixed too: the same prefix (13 objects), the same file (1,615 bytes, &lt;code&gt;text/markdown; charset=utf-8&lt;/code&gt;), the same 24-byte write. All three returned the same values.&lt;/p&gt;
&lt;h3&gt;
  
  
  The listing
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw16wvsiz7vadjhhc8ro7.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw16wvsiz7vadjhhc8ro7.png" alt="The file listing on the AWS Blocks build. A prefix box holds reports/2026/05/10/, the count reads 13 objects, and thirteen compliance-report-&lt;uuid&gt;.md rows are listed with sizes and modification times" width="800" height="476"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The listing on the AWS Blocks build: thirteen files.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvyfdb6mt7d6swq98iei0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvyfdb6mt7d6swq98iei0.png" alt="The file listing on the Nx Plugin for AWS build. The generated shadcn sidebar is on the left, and the body shows the same prefix, the same 13 objects, and the same thirteen rows" width="800" height="476"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The listing on the Nx Plugin for AWS build, with the generated shadcn sidebar.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqmfnktqewqigz68s73u.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqmfnktqewqigz68s73u.png" alt="The file listing on the Amplify Gen 2 portal. A breadcrumb reads / reports / 2026 / 05 / 10, and the same thirteen files appear alongside a .. entry for the parent, each row carrying a star and a document icon" width="800" height="476"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The listing on the Amplify Gen 2 portal. Being a portal in actual use, it carries extra features such as stars and download icons.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Reading
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0eg312g7aw8tv9d9dcqh.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0eg312g7aw8tv9d9dcqh.png" alt="The read screen on the AWS Blocks build. Below a line reading 1615 bytes, text/markdown; charset=utf-8, the Japanese compliance report is rendered in the page" width="800" height="476"&gt;&lt;/a&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz87n8ve5ugcevuzystq0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz87n8ve5ugcevuzystq0.png" alt="The read screen on the Nx build. The header reads portal-web; under compliance-report-&lt;uuid&gt;.md sit 1615 bytes and text/markdown; charset=utf-8, the same report is rendered in the page, and an Upload section follows below it" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two above render the report in the page. The Amplify Gen 2 portal below does not: it hands &lt;code&gt;.md&lt;/code&gt; to an AI Assistant panel instead, because that is how I wrote it.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy23gsibxsp4jjs2v7wu5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy23gsibxsp4jjs2v7wu5.png" alt="The same file opened on the Amplify Gen 2 portal. The contents are not rendered; an AI Assistant panel has opened on the right, naming the selected file and offering Analyze document" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An aside: a by-product of the capture pass was one accessibility defect in my own portal. The document icon on a file row announces itself as &lt;code&gt;aria-label="Download &amp;lt;filename&amp;gt;"&lt;/code&gt;, yet what it actually does is select the file for AI processing. The &lt;code&gt;title&lt;/code&gt; attribute mentions both, but a screen reader reads out only &lt;code&gt;Download&lt;/code&gt;, so &lt;strong&gt;someone using assistive technology is told the button downloads a file when it starts AI processing instead. It is unfixed as of writing, recorded as something for the portal side to correct.&lt;/strong&gt; Without lining up three builds in the same order, I probably would not have noticed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Uploading
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1wvp0f2c9lnhnw00f4x.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1wvp0f2c9lnhnw00f4x.png" alt="The upload screen on the AWS Blocks build. The heading reads File Portal — AWS Blocks; the prefix is portal-parity/blocks/ with 1 object, and the note-2026-09-14.txt that was just written is listed at 35 B. Below it are a filename field, a body field and an Upload button, with a note that writes cannot leave portal-parity/blocks/" width="800" height="476"&gt;&lt;/a&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7cesuhml5dax436t67vz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7cesuhml5dax436t67vz.png" alt="The upload screen on the Nx build. The heading reads File Portal on Nx Plugin for AWS, subtitled Amazon FSx for NetApp ONTAP S3 Access Point, via tRPC procedures. The prefix is portal-parity/nx/ with 1 object, and the note-2026-09-14.txt that was just written is listed at 24 bytes" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two above are listings right after a successful write. The Amplify screen below is a refusal: the same file name was sent twice and the server side declined to overwrite. That is a difference in what I implemented, not in the tools.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F43vn0l0mtpmj65c6z6l9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F43vn0l0mtpmj65c6z6l9.png" alt="The upload screen on the Amplify Gen 2 portal. The left sidebar lists All Files, Favorites, Recent, Upload, AI Processing, Job History, Snapshots, Lock, ARP/AI and Version Diff. In the upload queue, note-2026-09-14.txt is 24 B with a status of Overwrite prevented and 0% progress, and a warning below reads Overwrite prevented for all files." width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  How the IAM permission differs by tool
&lt;/h3&gt;

&lt;p&gt;Reaching the S3 Access Point is the same on all three, but &lt;strong&gt;the IAM needs care&lt;/strong&gt;: all three require the access-point ARN form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;arn&lt;/span&gt;:&lt;span class="n"&gt;aws&lt;/span&gt;:&lt;span class="n"&gt;s3&lt;/span&gt;:&amp;lt;&lt;span class="n"&gt;region&lt;/span&gt;&amp;gt;:&amp;lt;&lt;span class="n"&gt;account&lt;/span&gt;-&lt;span class="n"&gt;id&lt;/span&gt;&amp;gt;:&lt;span class="n"&gt;accesspoint&lt;/span&gt;/&amp;lt;&lt;span class="n"&gt;name&lt;/span&gt;&amp;gt;          &lt;span class="c"&gt;# ListBucket
&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;:&lt;span class="n"&gt;aws&lt;/span&gt;:&lt;span class="n"&gt;s3&lt;/span&gt;:&amp;lt;&lt;span class="n"&gt;region&lt;/span&gt;&amp;gt;:&amp;lt;&lt;span class="n"&gt;account&lt;/span&gt;-&lt;span class="n"&gt;id&lt;/span&gt;&amp;gt;:&lt;span class="n"&gt;accesspoint&lt;/span&gt;/&amp;lt;&lt;span class="n"&gt;name&lt;/span&gt;&amp;gt;/&lt;span class="n"&gt;object&lt;/span&gt;/* &lt;span class="c"&gt;# GetObject, PutObject
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Written in the bucket form (&lt;code&gt;arn:aws:s3:::&amp;lt;alias&amp;gt;&lt;/code&gt;), the deploy succeeds and list, get and put all return &lt;code&gt;AccessDenied&lt;/code&gt; at run time — a failure that is hard to read.&lt;/p&gt;

&lt;p&gt;AWS Blocks in particular has &lt;code&gt;FileBucket.fromExisting(alias)&lt;/code&gt;, which internally calls &lt;code&gt;s3.Bucket.fromBucketName(alias).grantReadWrite()&lt;/code&gt; and therefore generates the bucket form. Trust the block's grant and add nothing, and it passes locally and fails once deployed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which layer refuses a malformed input
&lt;/h3&gt;

&lt;p&gt;Trying an input that walks out of the prefix (&lt;code&gt;../escape.txt&lt;/code&gt;) showed that the refusing layer differs by tool.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Nx Plugin for AWS&lt;/th&gt;
&lt;th&gt;AWS Blocks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;../escape.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;HTTP 403 &lt;code&gt;{"message":"Forbidden"}&lt;/code&gt;&lt;/strong&gt; (blocked by WAF)&lt;/td&gt;
&lt;td&gt;The application's own domain error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An empty file name&lt;/td&gt;
&lt;td&gt;HTTP 400 plus the application's error&lt;/td&gt;
&lt;td&gt;The application's own domain error&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the Nx build, AWS WAF stopped the request before the Lambda. Defence in depth on by default is a good thing, but the refusal arrives as a generic HTTP 403, so &lt;strong&gt;the starting point of an investigation moves from your own code to the WAF metrics&lt;/strong&gt; — worth knowing before you are debugging at speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this round measured (deploy time and resource count)
&lt;/h3&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;Deploy time&lt;/th&gt;
&lt;th&gt;Resources&lt;/th&gt;
&lt;th&gt;Application Lambdas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Blocks (production preset)&lt;/td&gt;
&lt;td&gt;318 s&lt;/td&gt;
&lt;td&gt;82&lt;/td&gt;
&lt;td&gt;1 (the whole API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nx Plugin for AWS (sandbox)&lt;/td&gt;
&lt;td&gt;307 s&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;5 (one per procedure)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Amplify Gen 2 is excluded from this round's figures (deploy time, resource count) because the comparison used the portal already in production rather than a fresh build.&lt;/p&gt;

&lt;p&gt;Worth noting: a green local e2e run on AWS Blocks says nothing about whether the access point is reachable, because the mocked storage closes the loop inside the mock.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deploying the starters as generated (2026-09-13)
&lt;/h2&gt;

&lt;p&gt;Stopping at &lt;code&gt;synth&lt;/code&gt; leaves out everything you learn while deleting resources. So all three were deployed to a real AWS account in their generated state, before any features were added.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Resources&lt;/th&gt;
&lt;th&gt;Deploy&lt;/th&gt;
&lt;th&gt;Exercised&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Blocks (sandbox preset)&lt;/td&gt;
&lt;td&gt;83&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;signUp and friends returned 200 over JSON-RPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Blocks (production preset)&lt;/td&gt;
&lt;td&gt;117&lt;/td&gt;
&lt;td&gt;1,228 s&lt;/td&gt;
&lt;td&gt;all five operations 200, CloudFront delivery included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nx Plugin for AWS&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;td&gt;317 s&lt;/td&gt;
&lt;td&gt;TOTP enrolled, then 200 from the API via the identity pool&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All three deployed without trouble. The Blocks production preset takes longer because DynamoDB builds its GSIs one at a time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fixed cost that accrues at zero requests
&lt;/h2&gt;

&lt;p&gt;A generator's defaults reach the monthly bill even when the template says nothing about them.&lt;br&gt;
&lt;strong&gt;The figure is settled at the moment the tooling is chosen&lt;/strong&gt;, which makes it a number to read&lt;br&gt;
before starting rather than after.&lt;/p&gt;

&lt;p&gt;With not a single request served, the floor for each tool looks like this (FSx for ONTAP's own cost is not included).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;WAF&lt;/th&gt;
&lt;th&gt;KMS CMK&lt;/th&gt;
&lt;th&gt;Monthly floor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nx Plugin for AWS&lt;/td&gt;
&lt;td&gt;3 ACLs + 6 rules = $21&lt;/td&gt;
&lt;td&gt;4 keys, $4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;from $25&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Blocks (production preset)&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;1 key, $1&lt;/td&gt;
&lt;td&gt;$1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amplify Gen 2 (starter)&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Nx starts higher because what production needs is present from the start. It does bite when something is stood up for a test and left running.&lt;/p&gt;


&lt;h2&gt;
  
  
  What a production portal still needs
&lt;/h2&gt;

&lt;p&gt;The feature set beyond these four, arranged in phases. Which phase your requirements stop at also changes how you choose a tool.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;VPC attachment&lt;/th&gt;
&lt;th&gt;Newly required&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;Paging, search, sorting&lt;/td&gt;
&lt;td&gt;not needed&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Downloads and share links&lt;/td&gt;
&lt;td&gt;not needed&lt;/td&gt;
&lt;td&gt;presigned URLs and an expiry policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Large file uploads&lt;/td&gt;
&lt;td&gt;not needed&lt;/td&gt;
&lt;td&gt;multipart, or similar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Showing ONTAP information&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;reaching the management LIF, splitting the functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Operating ONTAP&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;audit logs, approval for irreversible operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;AI processing&lt;/td&gt;
&lt;td&gt;depends&lt;/td&gt;
&lt;td&gt;confidence thresholds, data classification&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From phase 4 the ONTAP management LIF has to be reachable, which is where &lt;strong&gt;the decision to put a Lambda in a VPC&lt;/strong&gt; arrives, and with it fixed cost such as a NAT gateway. If phase 5 or later is a possibility, splitting the functions into "touches the S3 Access Point" and "touches ONTAP" from the start is the design that pays off.&lt;/p&gt;


&lt;h2&gt;
  
  
  Tearing down the evaluation — what a stack deletion leaves
&lt;/h2&gt;

&lt;p&gt;This is about putting the evaluation environment back, not about running the portal. A portal that&lt;br&gt;
gets built runs for years; the deletion happens while you are still trying things out. Even so, how&lt;br&gt;
much is left behind is worth knowing before you start.&lt;/p&gt;

&lt;p&gt;Listing what remained after each stack deletion turned up resources that static analysis of the template (&lt;code&gt;synth&lt;/code&gt;) could not show.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Left after the stack deletion&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blocks (sandbox preset)&lt;/td&gt;
&lt;td&gt;5 log groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocks (production preset)&lt;/td&gt;
&lt;td&gt;4 DynamoDB tables (deletion protection), 8 log groups, 1 KMS key (waiting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nx Plugin for AWS&lt;/td&gt;
&lt;td&gt;1 Cognito user pool, 1 DynamoDB table, 4 KMS keys, 2 IAM roles, 7 log groups&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Those eight and nine are the generator's defaults, not an AWS constraint.&lt;/strong&gt; The generated code&lt;br&gt;
is what applies &lt;code&gt;Retain&lt;/code&gt; and deletion protection, so anything that may go can be released on the&lt;br&gt;
generating side (&lt;code&gt;RemovalPolicy.DESTROY&lt;/code&gt; in CDK; the deletion-protection properties on DynamoDB&lt;br&gt;
and Cognito set to false). Defaults leaning towards keeping data is a production-shaped choice;&lt;br&gt;
it conflicts with intent only when the deployment is a test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Log groups are the awkward ones.&lt;/strong&gt; The Lambda functions behind CDK and tooling custom resources create them on first execution with the default retention (never expire), which puts them outside CloudFormation's control and leaves them behind. One sweep at the end of the teardown catches them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws logs describe-log-groups &lt;span class="nt"&gt;--log-group-name-prefix&lt;/span&gt; &lt;span class="s2"&gt;"/aws/lambda/&amp;lt;stack-name&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"logGroups[].[logGroupName,retentionInDays]"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The removal order matters too. Nx's user pool cannot be deleted until deletion protection is off, and the SMS configuration needed to switch it off references one of the two IAM roles listed above as survivors. &lt;strong&gt;Delete those IAM roles first, while clearing up what the stack left behind, and the route to switching off deletion protection is gone.&lt;/strong&gt; Delete the user pool first, then the roles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your own functions are a different case from CDK's.&lt;/strong&gt; A Lambda you write can declare its log&lt;br&gt;
group in the template, so it does not survive — measured, the declared ones went away on all&lt;br&gt;
three. What survives belongs to CDK's internal custom resources, and there is no route to declare&lt;br&gt;
those from your own code: the &lt;code&gt;CustomResourceProvider&lt;/code&gt; base class declares an IAM role and a&lt;br&gt;
Lambda function and no log group (&lt;a href="https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/core/lib/custom-resource-provider/custom-resource-provider-base.ts" rel="noopener noreferrer"&gt;custom-resource-provider-base.ts&lt;/a&gt;,&lt;br&gt;
read in full on &lt;code&gt;main&lt;/code&gt;, 2026-09-16).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No released version fixes this&lt;/strong&gt; (checked 2026-09-16). Two feature requests are open upstream&lt;br&gt;
(&lt;a href="https://github.com/aws/aws-cdk/issues/26553" rel="noopener noreferrer"&gt;aws-cdk #26553&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/aws/aws-cdk/issues/23909" rel="noopener noreferrer"&gt;aws-cdk #23909&lt;/a&gt;). A third,&lt;br&gt;
&lt;a href="https://github.com/aws/aws-cdk/issues/24815" rel="noopener noreferrer"&gt;aws-cdk #24815&lt;/a&gt;, is closed as completed — but&lt;br&gt;
&lt;strong&gt;its closing comment, from an AWS maintainer, says the fixing PR was reverted, that the problem&lt;br&gt;
is general to all custom resources, and that tracking moved to #23909. Closed is not shipped.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS Support has not been asked. Lambda documents the implicit creation and the never-expire&lt;br&gt;
default, the CDK source shows what the provider declares, and &lt;strong&gt;CDK's primary channel is its&lt;br&gt;
GitHub repository rather than AWS Support.&lt;/strong&gt; What would be worth filing is the per-output residue&lt;br&gt;
count measured here, which appears in neither issue.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two behaviours that are easy to misread in operation
&lt;/h2&gt;

&lt;p&gt;Both are documented, and both are still easy to read the wrong way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap 1: a deployment mode cannot be dropped later
&lt;/h3&gt;

&lt;p&gt;Nx generates &lt;code&gt;deploy-sandbox&lt;/code&gt; as &lt;code&gt;cdk deploy ... --express&lt;/code&gt; under the hood. If a deploy breaks, "drop &lt;code&gt;--express&lt;/code&gt; and run it again" does not work: &lt;strong&gt;a stack last updated in express mode requires its later operations to be express too.&lt;/strong&gt; The remedy is to pair &lt;code&gt;--express --rollback&lt;/code&gt; from the start.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap 2: DELETE_COMPLETE does not always mean gone
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DELETE_COMPLETE&lt;/code&gt; in CloudFormation does not always mean the resource is gone. For a KMS key it means deletion was scheduled — a 30-day wait by default — and that wait cannot be shortened afterwards. Confirm a teardown from the listed state rather than from the status.&lt;/p&gt;




&lt;h2&gt;
  
  
  Choosing between them
&lt;/h2&gt;

&lt;p&gt;The three are not mutually exclusive; they suit different purposes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nx Plugin for AWS&lt;/strong&gt;: production-leaning defaults and dependency management that holds up as projects multiply. Fixed cost is the highest of the three.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Blocks&lt;/strong&gt;: running the whole application locally with no AWS account (it is in preview, so treat a Block ID as immutable after deployment).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amplify Gen 2&lt;/strong&gt;: treating the backend definition, the sandbox and hosting as one thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Part 1's conclusion stands unchanged — Nextcloud when file sharing is the centre, Amplify Gen 2 when AI processing and ONTAP operations are. What this part settled is which tool the "build" side sits on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test environment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Measured&lt;/td&gt;
&lt;td&gt;2026-09-13 (starters, live), 2026-09-14 (live, with the four features implemented)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;ap-northeast-1 (only Nx's CloudFront web ACL in us-east-1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outputs&lt;/td&gt;
&lt;td&gt;Nx Plugin for AWS 1.0 (&lt;code&gt;@aws/nx-plugin@1.0.0&lt;/code&gt;), AWS Blocks (preview), Amplify Gen 2 (&lt;code&gt;npm create amplify&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Node.js v26.4.0, npm 11.17.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing&lt;/td&gt;
&lt;td&gt;AWS Price List API, ap-northeast-1, 2026-09-07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Teardown&lt;/td&gt;
&lt;td&gt;everything deleted; only 5 KMS keys remain in their waiting period (&lt;code&gt;PendingDeletion&lt;/code&gt; is not billed)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The record in the repository
&lt;/h2&gt;

&lt;p&gt;The numbers, the sources and the teardown procedure live in the repository, in more detail than this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/scaffolding-and-backend-toolkit-choices.md" rel="noopener noreferrer"&gt;Choosing an application foundation&lt;/a&gt; — where the three sit, measurements up to synth, fixed cost, reproduction steps&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/scaffolding-deploy-verification.md" rel="noopener noreferrer"&gt;Deploy verification and teardown&lt;/a&gt; — the 15-entry pitfall register with a source on every entry, the teardown order, and the live record&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-parity-four-features.md" rel="noopener noreferrer"&gt;The same four features on all three&lt;/a&gt; — twelve captures across three builds by four screens, how the permission differs, which layer refuses, 9 more pitfalls, reproduction steps&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-parity-next-steps.md" rel="noopener noreferrer"&gt;Beyond the four features&lt;/a&gt; — what each phase assumes, the nature of the cost, which phases can be skipped&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/index.md" rel="noopener noreferrer"&gt;Documentation index&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Everything in this part is about defaults. Choosing a tool fixes your monthly floor and your teardown procedure, and neither is written in the template.&lt;/p&gt;

&lt;p&gt;What worked as a method was &lt;strong&gt;writing the same four features three times&lt;/strong&gt;. The difference shows up not as how much code you write but as where you write it, and which layer refuses a bad request. Choosing a tool is, I think, choosing where you look first when something breaks.&lt;/p&gt;

&lt;p&gt;I hope this record helps anyone weighing the same choice.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Everything in the test environment has been deleted (only KMS keys remain in their waiting period). The figures come from one specific environment and configuration and will differ with workload and setup. Prices are the published ap-northeast-1 rates as of 2026-09-07.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>nx</category>
      <category>amplify</category>
    </item>
    <item>
      <title>How far ONTAP features reach through an S3 Access Point — measuring qtree, quota and FlexClone (File Portal Part 6)</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Fri, 04 Sep 2026 00:59:30 +0000</pubDate>
      <link>https://dev.to/aws-builders/what-i-left-off-the-file-portal-on-aws-how-far-fsx-for-ontap-features-reach-and-the-work-handed-hk8</link>
      <guid>https://dev.to/aws-builders/what-i-left-off-the-file-portal-on-aws-how-far-fsx-for-ontap-features-reach-and-the-work-handed-hk8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is the sixth part of the series, and &lt;a href="https://dev.to/aws-builders/what-a-stack-deletion-leaves-behind-nx-plugin-for-aws-10-aws-blocks-and-amplify-gen-2-compared-10fg"&gt;Part 7&lt;/a&gt; continues it. &lt;a href="https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887"&gt;Part 1&lt;/a&gt; built a file portal on Amazon FSx for NetApp ONTAP (FSx for ONTAP) S3 access points, &lt;a href="https://dev.to/aws-builders/embedding-storage-operations-into-a-file-portal-from-arpai-incident-response-to-regulatory-1oih"&gt;Part 2&lt;/a&gt; added ransomware response and WORM retention, &lt;a href="https://dev.to/aws-builders/embedding-ai-agents-into-a-file-portal-from-agentcore-mcp-to-multi-agent-teams-part-3-19m1"&gt;Part 3&lt;/a&gt; put AI agents on it, Part 4 delegated 182 storage operations, and Part 5 collected what the running cluster refused.&lt;/p&gt;

&lt;p&gt;This part is &lt;strong&gt;what was left off the screen&lt;/strong&gt;. That is the difference from last time: where Parts 4 and 5 were about operations you run from a screen, this one has two subjects. How much of ONTAP stays usable without going through the portal at all, and the operational work that was handed to a schedule instead of a button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this part does not cover&lt;/strong&gt;: performance figures (this measures whether things are possible and under what condition, not throughput), running SnapLock or Object Lock (irreversible, so not enabled without an instruction naming the retention), and actual tiering into a capacity pool (needs a different aggregate configuration, which was not on this path).&lt;/p&gt;




&lt;h2&gt;
  
  
  The reach of ONTAP features on this path
&lt;/h2&gt;

&lt;p&gt;When you read and write files over the S3 API through an FSx for ONTAP S3 access point, how much of ONTAP stays usable alongside it? You want qtrees to carve up directories, quotas to stop a tenant running away with the space, FlexClone to spin a verification copy. Does any of that sound like your environment?&lt;/p&gt;

&lt;p&gt;Start looking into it and NetApp's documentation gives you pause. &lt;a href="https://docs.netapp.com/us-en/ontap/s3-config/ontap-s3-interoperability-concept.html" rel="noopener noreferrer"&gt;ONTAP S3 interoperability&lt;/a&gt; records qtrees, quotas, FlexClone, and a volume clone of a FlexGroup containing ONTAP S3 buckets as &lt;strong&gt;not supported&lt;/strong&gt; — for the &lt;strong&gt;ONTAP S3 server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is where it stalls. The FSx for ONTAP S3 access point is an AWS mechanism, and what it attaches to is a &lt;strong&gt;volume&lt;/strong&gt;, not a bucket. Attaching one does stand up an ONTAP S3 server on the SVM, and I/O through the access point goes through ONTAP's S3 protocol stack. So the two are not unrelated. Does the same restriction appear?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I measured it. Up front: everything I measured worked.&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;ONTAP feature&lt;/th&gt;
&lt;th&gt;Result on the S3 access point path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qtree&lt;/td&gt;
&lt;td&gt;Can be created. Appears as an S3 prefix, and an object PUT into it lands inside the qtree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quota&lt;/td&gt;
&lt;td&gt;A tree quota refuses an S3 PUT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexClone (volume granularity)&lt;/td&gt;
&lt;td&gt;The clone can be made, and &lt;strong&gt;the clone itself can take an S3 access point&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexClone (file granularity)&lt;/td&gt;
&lt;td&gt;A file written over S3 can be cloned, and &lt;strong&gt;the clone is visible as an object through the access point&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexGroup volume&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;An S3 access point can be attached.&lt;/strong&gt; PUT / GET / LIST and multipart all work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clone of a FlexGroup&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;The clone can be created&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The four items that table lists did not appear as restrictions on this path.&lt;/strong&gt; Reading that table as "not supported there, therefore not supported here" is wrong, and so is the reverse: a "supported" row is not evidence about this path either. &lt;strong&gt;The subjects differ, so nothing is known until it is measured.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What actually tripped me up was not in the table at all. Three things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;NTFS volume with a UNIX identity on an SVM with no CIFS server&lt;/strong&gt; attaches, reaches &lt;code&gt;AVAILABLE&lt;/code&gt;, and then refuses every data operation with &lt;code&gt;AccessDenied&lt;/code&gt;. &lt;strong&gt;&lt;code&gt;AVAILABLE&lt;/code&gt; says nothing about the health of the file system layer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A volume created on the ONTAP side takes &lt;strong&gt;ten to twenty minutes&lt;/strong&gt; to appear in the AWS API. The documentation says "several minutes"&lt;/li&gt;
&lt;li&gt;A volume that has &lt;strong&gt;once carried an S3 access point cannot be deleted from the ONTAP side&lt;/strong&gt;, even after every access point is detached. Only &lt;code&gt;aws fsx delete-volume&lt;/code&gt; removes it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is the walkthrough, and how the controls were set up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test environment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Date&lt;/td&gt;
&lt;td&gt;2026-08-26 (UTC)&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;ONTAP version&lt;/td&gt;
&lt;td&gt;9.18.1P3D1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment type / throughput&lt;/td&gt;
&lt;td&gt;SINGLE_AZ_1 / 128 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test volumes&lt;/td&gt;
&lt;td&gt;UNIX security style 1 GiB × 2 (one with an access point, one as the control), NTFS 1 GiB × 1, FlexGroup 400 GiB × 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access point identity&lt;/td&gt;
&lt;td&gt;UNIX &lt;code&gt;root&lt;/code&gt;, internet origin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup&lt;/td&gt;
&lt;td&gt;Throwaway volumes only. All deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on the identity&lt;/strong&gt;: UNIX &lt;code&gt;root&lt;/code&gt; was used to keep the measurement simple. Every request through an access point is authorized as that one file system identity, so specifying &lt;code&gt;root&lt;/code&gt; removes any narrowing by file permissions. &lt;strong&gt;This is not a recommended configuration.&lt;/strong&gt; Use a dedicated user with only the permissions the writes need, and split access points per purpose (&lt;code&gt;FileSystemIdentity&lt;/code&gt; cannot be changed after creation).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The distinction that matters most — different subjects
&lt;/h2&gt;

&lt;p&gt;The results below have to be read with these two kept apart.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Whose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ONTAP S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The S3 server with which ONTAP serves buckets. This is what NetApp's table is about&lt;/td&gt;
&lt;td&gt;NetApp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FSx for ONTAP S3 access point&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An AWS access point attached to an FSx for ONTAP &lt;strong&gt;volume&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They are not unrelated. Attaching one stands up an ONTAP S3 server on the SVM, and the I/O goes through ONTAP's S3 protocol stack (measured). &lt;strong&gt;That is exactly why NetApp's table is the right place to start.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Being the right place to start is not the same as being the conclusion. The target is a volume rather than a bucket, and the namespace and the authorization path both differ. &lt;strong&gt;"Not supported there, therefore not supported here" turns something unchecked into something checked.&lt;/strong&gt; The reverse reading is the same error.&lt;/p&gt;




&lt;h2&gt;
  
  
  The walkthrough
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Qtree — usable
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&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;Create a qtree in the volume with an access point&lt;/td&gt;
&lt;td&gt;Succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control: the same qtree in the volume with no access point&lt;/td&gt;
&lt;td&gt;Succeeded. &lt;strong&gt;No difference&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list-objects-v2 --delimiter /&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The qtree appears in CommonPrefixes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT into the qtree prefix&lt;/td&gt;
&lt;td&gt;Succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect the qtree from ONTAP&lt;/td&gt;
&lt;td&gt;The written file is inside the qtree directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;NetApp's "Qtrees: not supported" &lt;strong&gt;does not manifest as a refusal on this path.&lt;/strong&gt; The qtree is created, it is visible as a prefix in the S3 namespace, and an object written into that prefix lands inside it. Together with the quota result below, it behaves as a qtree.&lt;/p&gt;

&lt;p&gt;Incidentally, a volume with an access point attached carries an internal &lt;code&gt;____NTAP_S3_MAPPING&lt;/code&gt; directory at its root. It is visible over NFS and SMB, so anyone who inspects a collect-layer volume by hand will see it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quota — enforced against an S3 PUT
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&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;Set a tree quota on the qtree (space 1 MiB, files 10) and enable quotas on the volume&lt;/td&gt;
&lt;td&gt;Succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quota report&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The rule is active, and &lt;strong&gt;the file written over S3 is counted in files used&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT 15 small objects&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;8 succeeded, 7 refused.&lt;/strong&gt; files used stopped at exactly 10/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What the S3 client received on refusal&lt;/td&gt;
&lt;td&gt;HTTP 507 &lt;code&gt;InsufficientCapacity&lt;/code&gt; / &lt;code&gt;Maximum storage capacity of file system has been reached.&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control: raise the files limit 10 → 50 and re-PUT the refused key with the same body&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The control carries the conclusion. Raising the limit alone made the identical PUT succeed, so the refusal was the quota, not a capacity shortage and not a permissions artefact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That the response mis-describes its cause matters to the design.&lt;/strong&gt; The file system was not full; a qtree file-count quota was reached. An operator who sees a 507 and "maximum storage capacity of file system" will consider growing the file system. If you use quotas on this path, &lt;strong&gt;write down what that response actually means in the runbook.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  FlexClone — both volume and file granularity
&lt;/h3&gt;

&lt;p&gt;There are two granularities, and they behave differently.&lt;/p&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;File granularity&lt;/th&gt;
&lt;th&gt;Volume granularity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where it lands&lt;/td&gt;
&lt;td&gt;The same volume. It appears as a new key on the same access point&lt;/td&gt;
&lt;td&gt;A separate volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization boundary&lt;/td&gt;
&lt;td&gt;Same as the parent&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Its own access point, its own policy, its own identity&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time before it is usable&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;599 to 1,177 s measured, for a volume created through the ONTAP API to appear on the AWS side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visible over S3&lt;/td&gt;
&lt;td&gt;Yes. &lt;code&gt;StorageClass=FSX_ONTAP&lt;/code&gt;, sha256 matches the source&lt;/td&gt;
&lt;td&gt;Yes. LIST / GET / PUT through the clone's own access point&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At either granularity the clone shares data blocks with its parent. Cloning a 256 MiB file four times:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Point&lt;/th&gt;
&lt;th&gt;Logical&lt;/th&gt;
&lt;th&gt;Physical&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Just after one 256 MiB PUT&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;253,534,208 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After four file-granularity clones&lt;/td&gt;
&lt;td&gt;1,350,942,720 B&lt;/td&gt;
&lt;td&gt;277,200,896 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Logical grew to five copies, physical by about 23 MB. &lt;strong&gt;FSx for ONTAP bills provisioned SSD capacity, so there is no capacity to provision per duplicate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;NetApp states that copies consume no storage except what is required for metadata until changes are written to the copy (&lt;a href="https://docs.netapp.com/us-en/ontap/concepts/flexclone-volumes-files-luns-concept.html" rel="noopener noreferrer"&gt;FlexClone volumes, files, and LUNs&lt;/a&gt;). The operation that ends the sharing is a &lt;strong&gt;split&lt;/strong&gt; (&lt;code&gt;volume clone split start&lt;/code&gt;), which I did not measure. From ONTAP 9.4, on AFF systems where the volume guarantee is &lt;code&gt;none&lt;/code&gt;, the documentation states that the split shares the physical blocks rather than copying data (&lt;a href="https://docs.netapp.com/us-en/ontap/volumes/split-flexclone-from-parent-task.html" rel="noopener noreferrer"&gt;splitting procedure&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A file-granularity clone gives no observable failure, however.&lt;/strong&gt; &lt;code&gt;POST /api/storage/file/clone&lt;/code&gt; returns 202 and a job UUID, but the UUID resolved to &lt;code&gt;404 entry doesn't exist&lt;/code&gt; and appeared in no job listing. The same &lt;code&gt;fsxadmin&lt;/code&gt; retrieves volume-create and volume-clone jobs as &lt;code&gt;state=success&lt;/code&gt;, so this is not a permissions problem. A call naming a destination directory that does not exist also returned 202 and created nothing. &lt;strong&gt;Judge the outcome by inspecting the destination file.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  FlexGroup — access point attachment
&lt;/h3&gt;

&lt;p&gt;Creation has conditions, learned by failing in order.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&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;Create with default parameters&lt;/td&gt;
&lt;td&gt;Failed. &lt;code&gt;Volumes of this type must be at least 50GB&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry at 50 GiB&lt;/td&gt;
&lt;td&gt;Failed. &lt;code&gt;Aggregates not matching FabricPool requirements: aggr1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry with the aggregate named explicitly&lt;/td&gt;
&lt;td&gt;Failed. &lt;code&gt;Minimum size is "400GB"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400 GiB, explicit aggregate, &lt;code&gt;tiering.policy=none&lt;/code&gt;, thin&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;FlexGroup volumes &lt;strong&gt;can&lt;/strong&gt; be created from the AWS Management Console, the AWS CLI and the Amazon FSx API (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/managing-volumes.html" rel="noopener noreferrer"&gt;Managing volumes&lt;/a&gt;). Naming the aggregate explicitly was required because this one was created through the ONTAP REST API; that constraint does not appear on the AWS-side path.&lt;/p&gt;

&lt;p&gt;What follows is the point: whether an access point on the AWS side attaches to a FlexGroup created on the ONTAP side.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&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;Appears on the FSx for ONTAP side as &lt;code&gt;VolumeStyle=FLEXGROUP&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;It does, with an &lt;code&gt;fsvol-&lt;/code&gt; identifier&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attach an S3 access point to the FlexGroup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded. &lt;code&gt;Lifecycle=AVAILABLE&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT / GET / LIST&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded. The GET content matched&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12 MiB multipart upload&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded. &lt;code&gt;StorageClass=FSX_ONTAP&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshot the FlexGroup&lt;/td&gt;
&lt;td&gt;Succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Clone that FlexGroup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded. &lt;code&gt;style=flexgroup&lt;/code&gt;, &lt;code&gt;is_flexclone=true&lt;/code&gt;, online&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row is the item NetApp's table records as "volume clone of the FlexGroup volume containing ONTAP S3 buckets: not supported".&lt;/p&gt;




&lt;h2&gt;
  
  
  What actually failed on this path
&lt;/h2&gt;

&lt;p&gt;The constraints were not on NetApp's side of the table. Within what I measured, these three are the real traps.&lt;/p&gt;

&lt;h3&gt;
  
  
  A security-style and identity mismatch — failure after a successful attachment
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&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;Attach with an NTFS-security-style volume + UNIX identity (&lt;code&gt;root&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded. &lt;code&gt;Lifecycle=AVAILABLE&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT through that access point&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Refused. &lt;code&gt;AccessDenied&lt;/code&gt;, body only &lt;code&gt;Access Denied&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control: same identity, same caller, UNIX volume&lt;/td&gt;
&lt;td&gt;PUT / GET / LIST all succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CIFS server on this SVM&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; The UNIX-to-Windows mapping cannot resolve&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;AVAILABLE&lt;/code&gt; does not mean the file system layer is healthy.&lt;/strong&gt; Attachable and usable are different properties: IAM and the access point policy are both passed, and the file system layer refuses after that. The body is only &lt;code&gt;Access Denied&lt;/code&gt; and does not name the layer. A refusal at the identity layer says &lt;code&gt;no identity-based policy allows ...&lt;/code&gt; instead, so &lt;strong&gt;the difference in the body is the signal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Trying WINDOWS identity on the same SVM does not even complete the attachment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&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;Attach with WINDOWS identity on an SVM with no CIFS server&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Failed. &lt;code&gt;did not stabilize&lt;/code&gt; (&lt;code&gt;NotStabilized&lt;/code&gt;), stack rolled back&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether an attachment survives the rollback&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;It does not.&lt;/strong&gt; Nothing was orphaned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The wait for something created in ONTAP to appear on the AWS side
&lt;/h3&gt;

&lt;p&gt;A volume created through the ONTAP API is not immediately in the AWS-side &lt;code&gt;describe-volumes&lt;/code&gt;. Without an &lt;code&gt;fsvol-&lt;/code&gt; identifier, neither &lt;code&gt;AWS::FSx::S3AccessPointAttachment&lt;/code&gt; nor &lt;code&gt;create-and-attach-s3-access-point&lt;/code&gt; can reference it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measurement&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;FlexGroup, polled every 20 s with no gaps&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Appeared at 599 s (about 10 min)&lt;/strong&gt;, with an &lt;code&gt;fsvol-&lt;/code&gt; identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A FlexClone volume, polled every 20 s with no gaps&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Appeared at 1,177 s (about 19.6 min)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A separate run&lt;/td&gt;
&lt;td&gt;Still absent at 1,258 s (about 21 min)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The three observations disagree, so this is not an upper bound.&lt;/strong&gt; The order of magnitude is tens of minutes, which is not enough to bake a fixed wait into a design. AWS documents the following, and the measurement is longer than its "several minutes".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Amazon FSx periodically syncs with ONTAP to ensure consistency. If you create or modify volumes using NetApp applications, it may take up to several minutes for these changes to be reflected in the AWS Management Console, AWS CLI, API and SDKs.&lt;/p&gt;

&lt;p&gt;— &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/managing-resources-ontap-apps.html" rel="noopener noreferrer"&gt;Managing FSx for ONTAP resources using NetApp applications&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;This delay has to be kept apart from absence.&lt;/strong&gt; In an observation of about 2.5 minutes, a volume created through the ONTAP API shows no &lt;code&gt;fsvol-&lt;/code&gt; identifier, and it would be easy to conclude from that that neither a clone nor a FlexGroup can take an access point. Wait, and it appears, and it attaches. 2.5 minutes sits inside the "a few minutes" AWS documents, so &lt;strong&gt;what is observable in that window is "not yet appeared".&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It lags in the other direction too. Setting a junction path on the ONTAP side and attaching immediately fails with &lt;code&gt;Amazon FSx is unable to attach S3access point because the volume is not mounted.&lt;/code&gt; The AWS-side &lt;code&gt;JunctionPath&lt;/code&gt; was &lt;code&gt;None&lt;/code&gt; at that moment and still was two minutes later. Setting it with &lt;code&gt;aws fsx update-volume&lt;/code&gt; made it visible on the AWS side in about 40 seconds, and the attachment then succeeded. &lt;strong&gt;The error message was telling the truth.&lt;/strong&gt; What was wrong was my assumption that setting it in ONTAP made it visible to AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  A residual NAS bucket refusing volume deletion on the ONTAP side
&lt;/h3&gt;

&lt;p&gt;First, the terms. An S3 access point is an AWS-side mechanism and an object store NAS bucket is an ONTAP-side object, and &lt;strong&gt;the two are different things.&lt;/strong&gt; Attaching an access point creates a corresponding NAS bucket on the ONTAP side.&lt;/p&gt;

&lt;p&gt;What happened here is residue of the latter. &lt;strong&gt;After every access point was detached, the NAS bucket remained on the ONTAP side, and ONTAP refused to delete the volume because of it.&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;Cannot delete volume "..." in SVM "..." because it is associated with the following
object store NAS buckets: "amazon-fsx-&amp;lt;volume-id&amp;gt;"
&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;What was checked&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;Where the bucket name comes from&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;An exact match for the volume ID.&lt;/strong&gt; It is created per volume, not per access point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After deleting every access point&lt;/td&gt;
&lt;td&gt;Still refused, and still refused hours later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bringing the volume back online and mounted&lt;/td&gt;
&lt;td&gt;Still refused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Re-attaching an access point and detaching it in the correct order&lt;/td&gt;
&lt;td&gt;Still refused. &lt;strong&gt;It is independent of the access point lifecycle&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws fsx delete-volume&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Succeeded.&lt;/strong&gt; Both the volume and the bucket went&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;NetApp's documented &lt;a href="https://docs.netapp.com/us-en/ontap/revert/remove-nas-bucket-task.html" rel="noopener noreferrer"&gt;procedure for removing a NAS bucket configuration&lt;/a&gt; uses &lt;code&gt;vserver object-store-server bucket delete&lt;/code&gt;, which &lt;strong&gt;could not be applied here because the target could not be listed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whether this residue is by design or a defect is unconfirmed.&lt;/strong&gt; No AWS documentation I could find describes a NAS bucket outliving its access points and blocking a volume delete; the documented removal path is &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/delete-access-point.html" rel="noopener noreferrer"&gt;&lt;code&gt;detach-and-delete-s3-access-point&lt;/code&gt;&lt;/a&gt;. If you hit the same state, &lt;code&gt;delete-volume&lt;/code&gt; on the AWS side will complete the teardown, but ask support to establish the cause.&lt;/p&gt;

&lt;p&gt;Not being listed is not the same as not existing. Laid out by reader:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reader&lt;/th&gt;
&lt;th&gt;How it shows this bucket / S3 server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP REST &lt;code&gt;/protocols/s3/buckets&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Does not list it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP CLI &lt;code&gt;vserver object-store-server bucket show&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Does not list it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP REST &lt;code&gt;/svm/svms&lt;/code&gt; (the &lt;code&gt;s3&lt;/code&gt; field)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Reports&lt;/strong&gt; the S3 server as &lt;code&gt;enabled&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP REST &lt;code&gt;/protocols/s3/services&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Does not list&lt;/strong&gt; the same SVM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Objects that AWS manages are hidden from the standard ONTAP S3 views.&lt;/strong&gt; I confirmed the bucket's absence across two readers, and both shared the same blind spot.&lt;/p&gt;

&lt;p&gt;If you have made clones, the teardown order needs one more step of care. &lt;code&gt;delete-volume&lt;/code&gt; returned &lt;code&gt;DELETING&lt;/code&gt; and then silently went back to &lt;code&gt;CREATED&lt;/code&gt; (observed twice). The reason appears only in &lt;code&gt;LifecycleTransitionReason&lt;/code&gt; on &lt;code&gt;describe-volumes&lt;/code&gt;: &lt;code&gt;Failed to delete volume because it has one or more clones.&lt;/code&gt; The clones had been deleted, and were sitting in ONTAP's &lt;strong&gt;volume recovery queue&lt;/strong&gt;. The parent kept &lt;code&gt;clone.has_flexclone&lt;/code&gt; at &lt;code&gt;true&lt;/code&gt;, and the AWS-side delete reads that flag and refuses. Purging the recovery queue cleared the flag and the same &lt;code&gt;delete-volume&lt;/code&gt; worked. &lt;strong&gt;That queue appears in neither the console nor the FSx for ONTAP API.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting it in a runbook
&lt;/h2&gt;

&lt;p&gt;Translated into operational terms, the measurements come to five points.&lt;/p&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;What to write in the runbook&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;
&lt;strong&gt;Match the security style to the identity.&lt;/strong&gt; UNIX identity for UNIX volumes, WINDOWS identity for NTFS ones. Avoid a UNIX identity on NTFS even where the SVM does have a CIFS server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Do not use &lt;code&gt;AVAILABLE&lt;/code&gt; as a connectivity check.&lt;/strong&gt; Make the acceptance criterion a PUT and a read-back after attaching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Budget tens of minutes into automation that creates a resource in ONTAP and then handles it through the AWS API.&lt;/strong&gt; Poll for the &lt;code&gt;fsvol-&lt;/code&gt; identifier rather than sleeping a fixed interval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Write through the AWS management plane.&lt;/strong&gt; Where a setting can be made from either side, as with the junction path, the AWS side reflects faster (about 40 seconds against more than two minutes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Tear down through the AWS-side API.&lt;/strong&gt; A volume that has carried an access point can keep a NAS bucket on the ONTAP side, so a procedure built on ONTAP's &lt;code&gt;volume delete&lt;/code&gt; gets stuck there (&lt;code&gt;aws fsx delete-volume&lt;/code&gt; completes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What the schedule took over
&lt;/h2&gt;

&lt;p&gt;Everything so far was about whether an operation is possible. Operations has another kind of work: checking capacity, taking stock of snapshots, revisiting tiering policy, confirming throughput is still right-sized. &lt;strong&gt;All of it is daily or weekly, and a button for it does not get pressed.&lt;/strong&gt; That work suits a schedule better than a screen, so six patterns live in &lt;code&gt;operations/&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;ID&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OPS1&lt;/td&gt;
&lt;td&gt;capacity-rightsizing&lt;/td&gt;
&lt;td&gt;Capacity and throughput monitoring, tier recommendation, what-if cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPS2&lt;/td&gt;
&lt;td&gt;storage-efficiency&lt;/td&gt;
&lt;td&gt;Tracks deduplication and compression efficiency, recommends enabling them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPS3&lt;/td&gt;
&lt;td&gt;tiering-optimizer&lt;/td&gt;
&lt;td&gt;Cold-data analysis, tiering policy recommendation and savings estimate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPS4&lt;/td&gt;
&lt;td&gt;snapshot-lifecycle&lt;/td&gt;
&lt;td&gt;Retention policy compliance check and drift detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPS5&lt;/td&gt;
&lt;td&gt;cost-optimization&lt;/td&gt;
&lt;td&gt;Cost breakdown, unit economics ($/GB), growth projection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPS6&lt;/td&gt;
&lt;td&gt;qos-monitoring&lt;/td&gt;
&lt;td&gt;QoS policy adherence, bandwidth contention risk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The shape is the same in all six. An EventBridge schedule starts a Step Functions workflow, a VPC Lambda collects from the ONTAP REST API and CloudWatch, another analyses, and a third writes the report. Unit tests total 74 across the six (&lt;code&gt;capacity-rightsizing&lt;/code&gt; 27, &lt;code&gt;snapshot-lifecycle&lt;/code&gt; 19, &lt;code&gt;tiering-optimizer&lt;/code&gt; 11, &lt;code&gt;storage-efficiency&lt;/code&gt; 6, &lt;code&gt;cost-optimization&lt;/code&gt; 6, &lt;code&gt;qos-monitoring&lt;/code&gt; 5).&lt;/p&gt;

&lt;p&gt;The report has two destinations. With &lt;code&gt;OutputDestination=FSXN_S3AP&lt;/code&gt; it is written to an FSx for ONTAP volume through an S3 access point, which means &lt;strong&gt;anyone with the NFS or SMB mount reads it in the file explorer they already use.&lt;/strong&gt; On the real cluster the same 4,240-byte JSON appeared on the ONTAP volume as in the S3 bucket, the Step Functions execution took around 15 seconds, and four metrics were published to the &lt;code&gt;FSxOps&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;A recommendation comes out like this.&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;"recommendation_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"downsize"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vol_backup_temp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"current_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10.0% (1024 GB allocated, 102 GB used)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"recommended_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Shrink to 205 GB or enable autosize (grow_shrink)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"monthly_cost_delta_usd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-102.4&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;&lt;code&gt;DemoMode=true&lt;/code&gt; runs the whole workflow on mock data with no file system, so the report layout and the shape of the recommendations can be reviewed first. Running cost itself — Lambda, Step Functions and S3 storage — came to roughly $1-5 a month for a daily run over a handful of file systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Progressive automation — four levels by design, two in the code
&lt;/h3&gt;

&lt;p&gt;Honestly, here. The template's &lt;code&gt;AutomationLevel&lt;/code&gt; accepts 0 through 3.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Intended behaviour&lt;/th&gt;
&lt;th&gt;Implemented&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Report only&lt;/td&gt;
&lt;td&gt;Yes (default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Notify over SNS&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Execute after human review&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Auto-execute within guardrails&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The code branches in exactly one place, &lt;code&gt;automation_level &amp;gt;= 1&lt;/code&gt;, deciding whether to send SNS. &lt;strong&gt;So 2 or 3 deploys successfully and behaves identically to Level 1.&lt;/strong&gt; The values the parameter accepts and the values the code distinguishes do not match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The values listed in &lt;code&gt;AllowedValues&lt;/code&gt; are not a declaration of what is implemented — accepting a value and honouring it are different things.&lt;/strong&gt; It is the same point Part 4 made about not shipping buttons that cannot be pressed, showing up on the parameter side instead. Until the approval flow exists, narrowing &lt;code&gt;AllowedValues&lt;/code&gt; to 0 and 1 would be the accurate move.&lt;/p&gt;

&lt;p&gt;None of this replaces existing tooling. The vendor's CloudWatch dashboards and auto-resize solutions, and the SSD capacity auto-expansion AWS documents, differ in immediacy and in scope. The six here suit a flow where analysis and a recommendation come first and a person decides. Choose on the use case.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On how recommendations are treated&lt;/strong&gt;: OPS4's snapshot deletion recommendations exclude anything below the minimum retention. A recommendation is an assistive signal, not a final decision. Which retention period satisfies your regulatory requirements is a legal and compliance judgement, and neither this article nor the pattern stands in for it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What I did not measure
&lt;/h2&gt;

&lt;p&gt;"Unverified" is not "cannot be done". Every result above turned out that way, so on this path &lt;strong&gt;nothing is known until it is measured.&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;Item&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FlexClone at LUN granularity&lt;/td&gt;
&lt;td&gt;Unverified. Creating a LUN needs an iSCSI configuration, which is not on this path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The FlexClone split&lt;/td&gt;
&lt;td&gt;Unverified. Documentation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An upper bound on the ONTAP-to-AWS reflection time&lt;/td&gt;
&lt;td&gt;Unverified. The three observations disagree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FabricPool tiering&lt;/td&gt;
&lt;td&gt;Unverified. Needs a different aggregate configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QoS / deduplication and compression / SnapMirror / Vscan&lt;/td&gt;
&lt;td&gt;Unverified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapLock / Object Lock&lt;/td&gt;
&lt;td&gt;Unverified. &lt;strong&gt;Irreversible&lt;/strong&gt;, so not enabled without an instruction naming the retention value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attaching on a FlexCache cache volume&lt;/td&gt;
&lt;td&gt;Unverified. Requires a cluster peer and an SVM peer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPS Levels 2 and 3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Not implemented.&lt;/strong&gt; The approval flow and auto-execution are not in the code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whether the OPS recommendations are sound&lt;/td&gt;
&lt;td&gt;Not evaluated. They are produced; none was applied and measured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPS across many file systems&lt;/td&gt;
&lt;td&gt;Unmeasured. Only a handful were run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Two things were left off the screen, and both were written up here.&lt;/p&gt;

&lt;p&gt;On the ONTAP side I took NetApp's table as the starting point and measured on the S3 access point path. &lt;strong&gt;The four items it lists as unsupported did not appear as restrictions here.&lt;/strong&gt; Qtrees work, a quota refuses an S3 PUT, FlexClone works at both volume and file granularity, a FlexGroup takes an access point, and a clone of that FlexGroup can be created. The constraints were elsewhere: &lt;code&gt;AVAILABLE&lt;/code&gt; says nothing about the file system layer, something created in ONTAP takes tens of minutes to appear on the AWS side, and a volume that has carried an access point can only be deleted from the AWS side.&lt;/p&gt;

&lt;p&gt;On the operations side, the daily work went to a schedule rather than a screen. There is one mismatch between the declaration and the code here: &lt;strong&gt;&lt;code&gt;AutomationLevel&lt;/code&gt; accepts 0 through 3 while the code distinguishes only up to 1.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every failure in these six parts looked like a correct conclusion on its own. &lt;strong&gt;A conclusion is not settled until one more control, or a second reader, is added.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading these six parts. It started with showing files in a browser and ended outside the screen entirely. &lt;a href="https://dev.to/aws-builders/what-a-stack-deletion-leaves-behind-nx-plugin-for-aws-10-aws-blocks-and-amplify-gen-2-compared-10fg"&gt;Part 7&lt;/a&gt; picks up the layer underneath the portal: which tooling to build it on, and what a stack deletion leaves behind. I hope the series is useful to anyone operating FSx for ONTAP, or building this kind of admin screen.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All test resources were deleted. The figures come from one specific environment and configuration and will differ with workload and setup. This article does not stand in for governance or regulatory judgement.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>fsxforontap</category>
      <category>ontap</category>
      <category>s3</category>
    </item>
    <item>
      <title>FlexGroup creation and capacity rebalancing failed on the defaults — what the error codes said (File Portal Part 5)</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Fri, 04 Sep 2026 00:59:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/what-i-learned-driving-fsx-for-ontap-from-a-file-portal-on-aws-flexgroup-creation-capacity-3gkd</link>
      <guid>https://dev.to/aws-builders/what-i-learned-driving-fsx-for-ontap-from-a-file-portal-on-aws-flexgroup-creation-capacity-3gkd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/aws-builders/putting-ontap-operations-on-a-file-portal"&gt;Part 4&lt;/a&gt; described putting 182 ONTAP operations on a file portal: the design of delegation and record, and the treatment of buttons that cannot be pressed and buttons that were never built.&lt;/p&gt;

&lt;p&gt;This part is what the running cluster refused while those 182 were being built. &lt;strong&gt;None of it was visible while reading the documentation; each one arrived as an error code.&lt;/strong&gt; That is the difference from last time: Part 4 was about design, this one is about measurement.&lt;/p&gt;

&lt;p&gt;The readers I have in mind are people writing code against the FSx for ONTAP ONTAP REST API. You do not need to be building a portal — if you call the same API, you stop in the same places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this part covers&lt;/strong&gt;: FlexGroup creation, FlexGroup capacity rebalance, how to read the capacity numbers, deletes that refuse, and four pitfalls in the S3-compatible API. Each with the response the system actually returned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this part does not cover&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ONTAP internals&lt;/li&gt;
&lt;li&gt;Absolute throughput figures (the subject here is whether something is possible and under what condition — but the defaults that decide whether a measured figure can be trusted are covered in a section below)&lt;/li&gt;
&lt;li&gt;Running SnapLock compliance (irreversible, and it would make a test file system undeletable for months, so it was deliberately not done)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creating a FlexGroup through the ONTAP REST API requires naming the target aggregate.&lt;/strong&gt; FSx for ONTAP aggregates have tiering enabled, so ONTAP's automatic placement skips them. Creating one from the AWS console or the Amazon FSx API does not need this step&lt;/li&gt;
&lt;li&gt;The same root cause is &lt;strong&gt;documented on the FlexCache side as a flag (&lt;code&gt;use_tiered_aggregate&lt;/code&gt;), and it had not reached the volume-creation side&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The capacity rebalance &lt;code&gt;max_runtime&lt;/code&gt; has a lower and an upper bound that are not in the API reference.&lt;/strong&gt; With ONTAP's defaults it never starts&lt;/li&gt;
&lt;li&gt;Starting a rebalance &lt;strong&gt;enables an irreversible setting.&lt;/strong&gt; Stopping it does not undo it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;space.used&lt;/code&gt; means a different quantity depending on the volume.&lt;/strong&gt; Snapshots inside the reserve are not counted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A deleted FlexClone child volume stays invisible to the API while still blocking the parent volume's delete.&lt;/strong&gt; There are two ways out, and both were measured&lt;/li&gt;
&lt;li&gt;Two places the documentation and the running system disagreed, and both changed the screen's wording&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eight combinations of defaults return a plausible figure while measuring something other than what you intended.&lt;/strong&gt; Three of them come from reads being served out of cache, and two from when you measured&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Test environment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Dates&lt;/td&gt;
&lt;td&gt;mid-August 2026 to 2026-09-02 (JST)&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;ONTAP version&lt;/td&gt;
&lt;td&gt;9.18.1P3D1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment type&lt;/td&gt;
&lt;td&gt;SINGLE_AZ_1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caller&lt;/td&gt;
&lt;td&gt;ONTAP REST API from a VPC Lambda (Python 3.13)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privilege&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fsxadmin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup&lt;/td&gt;
&lt;td&gt;All test volumes deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The distinction that matters most — ONTAP's words and AWS's words
&lt;/h2&gt;

&lt;p&gt;Mix these up and searching for the error text will not find the answer.&lt;/p&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;Where it appears&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;FabricPool&lt;/strong&gt;, &lt;code&gt;use_tiered_aggregate&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;ONTAP error text and REST field names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;tiering&lt;/strong&gt;, capacity pool tier&lt;/td&gt;
&lt;td&gt;AWS documentation and console&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They refer to the same mechanism. But &lt;strong&gt;the error arrives in ONTAP's vocabulary while the documentation explains it in AWS's&lt;/strong&gt;, so searching the AWS documentation for the error string returns nothing. The portal's wording follows the AWS side; only API strings are quoted as-is.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to watch when creating a FlexGroup
&lt;/h2&gt;

&lt;p&gt;A FlexGroup presents several "constituents" — internally split real volumes — as one namespace.&lt;/p&gt;

&lt;p&gt;To set the scope: FlexGroup volumes can be created from the AWS console, the AWS CLI and the Amazon FSx API (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/managing-volumes.html" rel="noopener noreferrer"&gt;Managing volumes&lt;/a&gt;). What follows is the &lt;strong&gt;ONTAP REST API&lt;/strong&gt; path, which is the one the portal uses.&lt;/p&gt;

&lt;p&gt;ONTAP normally picks the aggregates if you do not name them. On FSx for ONTAP that automatic selection does not resolve.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Aggregates not matching FabricPool requirements: aggr1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An FSx for ONTAP file system has a primary storage tier (SSD) and a capacity pool storage tier, and infrequently accessed data is tiered to the latter (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/volume-storage-capacity.html" rel="noopener noreferrer"&gt;AWS documentation&lt;/a&gt;). Aggregates in that configuration fall outside ONTAP's automatic placement.&lt;/p&gt;

&lt;p&gt;The conditions came out one failure at a time.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&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;Create with default parameters&lt;/td&gt;
&lt;td&gt;Failed. &lt;code&gt;Volumes of this type must be at least 50GB&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry at 50 GiB&lt;/td&gt;
&lt;td&gt;Failed. &lt;code&gt;Aggregates not matching FabricPool requirements: aggr1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry naming the aggregate&lt;/td&gt;
&lt;td&gt;Failed. &lt;code&gt;Minimum size is "400GB"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400 GiB, aggregate named, &lt;code&gt;tiering.policy=none&lt;/code&gt;, thin&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Naming the aggregate works. But &lt;strong&gt;that aggregate name appears in neither the AWS console nor the Amazon FSx API.&lt;/strong&gt; It has to be fetched with &lt;code&gt;GET /storage/aggregates&lt;/code&gt;. Nobody hands the tenant an aggregate name, so that extra call is mandatory.&lt;/p&gt;

&lt;p&gt;The 400 GB floor is configuration-dependent: the documented minimum is 100 GB per constituent, so the floor moves with the constituent count (the default is 8 per HA pair). And this extra step is only needed on the ONTAP REST API path — if you do not need to choose the aggregate yourself, creating the volume from the Amazon FSx API is shorter.&lt;/p&gt;

&lt;p&gt;The same root cause was already known on the FlexCache side. There is a flag, &lt;code&gt;use_tiered_aggregate&lt;/code&gt;, defaulting to &lt;code&gt;false&lt;/code&gt;. It had not reached the volume-creation side. &lt;strong&gt;The same cause surfacing elsewhere with a different face is a familiar shape in a managed service that wraps upstream software.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The screen carries this note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When creating a FlexGroup from this screen, the aggregate is named explicitly rather than placed automatically. FSx for ONTAP aggregates have tiering enabled and are excluded from automatic placement (measured: without it, creation fails with "Aggregates not matching FabricPool requirements"). With the default four constituents the minimum is 400 GB.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The capacity rebalance that never starts with the defaults
&lt;/h2&gt;

&lt;p&gt;A FlexGroup places files across constituents by hash, so they drift apart over time. &lt;strong&gt;When one fills, the whole volume returns "no space" even though the others have room.&lt;/strong&gt; A capacity rebalance is what corrects that.&lt;/p&gt;

&lt;p&gt;Running it against the real cluster produced two constraints on &lt;code&gt;max_runtime&lt;/code&gt; that are not in the API reference.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th&gt;Error code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lower bound&lt;/td&gt;
&lt;td&gt;Under 30 minutes is refused&lt;/td&gt;
&lt;td&gt;&lt;code&gt;144182221&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upper bound&lt;/td&gt;
&lt;td&gt;Must be shorter than the time remaining until the next snapshot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;13107433&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Together, the window to start is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30 min &amp;lt;= max_runtime &amp;lt; (time from start until the next snapshot)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ONTAP's default &lt;code&gt;max_runtime&lt;/code&gt; is 6 hours. On a volume with the default snapshot policy (hourly at :05), 6 hours always violates the upper bound. &lt;strong&gt;With the value the portal offered as its default, it never started once.&lt;/strong&gt; With the default policy the only window is the 30 minutes from :05 to :35 each hour. On a frequent schedule such as &lt;code&gt;5min&lt;/code&gt;, nothing starts until the policy is detached.&lt;/p&gt;

&lt;p&gt;The boundary was pinned with a one-second A/B.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;&lt;code&gt;maxRuntime&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Until next snapshot&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;16:06:12&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PT1H&lt;/code&gt; (60 min)&lt;/td&gt;
&lt;td&gt;58m47s&lt;/td&gt;
&lt;td&gt;Refused (&lt;code&gt;13107433&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16:06:13&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PT30M&lt;/code&gt; (30 min)&lt;/td&gt;
&lt;td&gt;58m46s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;60 minutes &amp;gt; 58m47s refused; 30 minutes &amp;lt; 58m46s succeeded. &lt;strong&gt;Because the two calls are one second apart, the deciding factor is the &lt;code&gt;max_runtime&lt;/code&gt; value and not elapsed time.&lt;/strong&gt; ONTAP's own message names both remedies — "reduce the &lt;code&gt;-max-runtime&lt;/code&gt; or disable the snapshot policy" — because of this structure.&lt;/p&gt;

&lt;p&gt;The full observation log is in &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/solutions/amplify-portal/docs/flexgroup-rebalance-verification.md" rel="noopener noreferrer"&gt;the FlexGroup capacity rebalance record&lt;/a&gt;. State values absent from the reference's volume-side list turned up there too: &lt;code&gt;idle&lt;/code&gt; means running with nothing to move, &lt;code&gt;scheduled&lt;/code&gt; means a reservation exists. The portal had been displaying "running" as "unknown".&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrl8lsv25wlms53mf3ud.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrl8lsv25wlms53mf3ud.png" alt="The rebalance panel: state, volume-wide skew and the most skewed constituent, per-constituent usage, and the maximum runtime rounded to 30 minutes" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on irreversibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Starting a rebalance enables &lt;code&gt;granular data&lt;/code&gt; on that volume. It is required for the rebalance, and it creates two multi-part inodes per moved file (&lt;a href="https://docs.netapp.com/us-en/ontap/flexgroup/manage-flexgroup-rebalance-task.html" rel="noopener noreferrer"&gt;ONTAP documentation&lt;/a&gt;). &lt;strong&gt;Stopping does not undo it.&lt;/strong&gt; The vendor's documentation lists no way back other than deleting the volume or restoring from a snapshot taken before it was enabled.&lt;/p&gt;

&lt;p&gt;Stopping is not a rollback either: files already moved stay where they are. Both were measured (&lt;code&gt;granular_data&lt;/code&gt; remains &lt;code&gt;true&lt;/code&gt; after a stop). The screen's confirmation says both.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The space that does not come back after a delete
&lt;/h2&gt;

&lt;p&gt;Read the usage figure in a volume list naively and you will get it wrong, because &lt;code&gt;space.used&lt;/code&gt; means a different quantity depending on the volume.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snapshots that fit inside the snapshot reserve (5% by default) are not counted in &lt;code&gt;space.used&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Anything past the reserve consumes the same space as live data and is counted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The measurements. A 100 GiB volume reported &lt;code&gt;used&lt;/code&gt; of 18.1 MiB while holding 77.3 MiB of snapshots. That sits inside the 5% reserve (5 GiB), so the usage bar does not move. On a different volume with a 0% reserve, &lt;code&gt;used&lt;/code&gt; was 83,677 MiB: 81,934 MiB of live data and 1,743 MiB of snapshots. &lt;strong&gt;On 8 of 11 volumes, snapshots exceeded live data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So the list was split from one usage figure into three: live data, snapshots, and reserve spill. Whether the space is not coming back because of snapshots or because live data fills it can now be read off the screen.&lt;/p&gt;

&lt;p&gt;Sometimes the cause is on another volume and its own row does not show it. That is FlexClone. A clone is based on one of the parent's snapshots, and that snapshot stays locked while the clone exists. Deleting files on the parent frees nothing while the locked snapshot still references the blocks. &lt;strong&gt;Reading the parent's row alone cannot tell you that&lt;/strong&gt;, so it is written on the FlexClone panel instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to check when a FlexClone volume will not delete
&lt;/h2&gt;

&lt;p&gt;After deleting a child volume created with FlexClone, deleting the parent volume is refused with &lt;code&gt;has one or more clones&lt;/code&gt;. I reproduced that on real hardware. The awkward part is that &lt;strong&gt;the API cannot see that child volume.&lt;/strong&gt; It is not in the volume listing, fetching it by name returns &lt;code&gt;entry doesn't exist&lt;/code&gt;, and yet only the parent's delete refuses, saying a clone exists.&lt;/p&gt;

&lt;p&gt;The cause is ONTAP's volume recovery queue, which retains deleted volumes for 12 hours by default. An A/B in the same environment, varying only whether the clone was split first:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Steps&lt;/th&gt;
&lt;th&gt;Parent delete&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delete an unsplit clone, then delete the parent&lt;/td&gt;
&lt;td&gt;Failed (still failing after 7 and 15 minutes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split the clone, delete it, then delete the parent&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Succeeded within seconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The exit was measured too. &lt;code&gt;GET /api/private/cli/volume/recovery-queue&lt;/code&gt; reads the queue. &lt;code&gt;POST .../purge&lt;/code&gt; goes through with &lt;code&gt;fsxadmin&lt;/code&gt; privilege, the entry left the queue in about 20 seconds, and the parent delete succeeded immediately afterwards.&lt;/p&gt;

&lt;p&gt;This is a place where &lt;strong&gt;I published something wrong and have corrected it.&lt;/strong&gt; The earlier text said purge needs diag privilege, is unreachable as &lt;code&gt;fsxadmin&lt;/code&gt;, and therefore the only option is to wait 12 hours. That was wrong. &lt;strong&gt;I blamed privilege without trying it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The screen's error carries the causation and both exits (split first, or purge). Purge cannot be undone, so it also says to confirm the volume was yours before running it.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fymiqhbd72zwp0y2wan6p.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fymiqhbd72zwp0y2wan6p.png" alt="The FlexClone panel: the clone list and creation form, when to split, and the effect on the parent's capacity" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Four pitfalls in the S3-compatible API
&lt;/h2&gt;

&lt;p&gt;Behaviours worth knowing before you call an FSx for ONTAP S3 access point from a browser or an SDK. Knowing them changes how long the investigation takes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 501 failure of a "write only if absent" request
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PutObject&lt;/code&gt; with &lt;code&gt;if-none-match: *&lt;/code&gt; returns &lt;code&gt;501 NotImplemented&lt;/code&gt;, with the body &lt;code&gt;A header you provided implies functionality that is not implemented&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;Request&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;PUT&lt;/code&gt; + &lt;code&gt;if-none-match: *&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;501 NotImplemented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PUT&lt;/code&gt; + &lt;code&gt;x-amz-checksum-crc32&lt;/code&gt; (no &lt;code&gt;if-none-match&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;200 OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;GET&lt;/code&gt; / &lt;code&gt;ListObjectsV2&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;200 (neither header is sent)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CRC32 flexible checksums pass. Only &lt;code&gt;if-none-match&lt;/code&gt; fails. Reads are unaffected, so the symptom presents as &lt;strong&gt;"the listing works but every write fails."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is not somebody else's problem. Amplify's Storage Browser translates its "do not overwrite" option into that header, so dropping it in unchanged breaks every upload and folder creation. If you need overwrite protection, look the key up before writing instead. That does not give you &lt;code&gt;if-none-match&lt;/code&gt;'s atomicity, and two concurrent writes to the same key can both decide it is absent.&lt;/p&gt;

&lt;h3&gt;
  
  
  GOVERNANCE and COMPLIANCE, indistinguishable from the error text
&lt;/h3&gt;

&lt;p&gt;S3 Object Lock has two retention modes. &lt;strong&gt;GOVERNANCE lets a user holding a specific permission (&lt;code&gt;s3:BypassGovernanceRetention&lt;/code&gt;) delete inside the retention period; COMPLIANCE lets nobody delete until the retention expires.&lt;/strong&gt; Deleting an object written with one day of retention, compared across both modes:&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;GOVERNANCE&lt;/th&gt;
&lt;th&gt;COMPLIANCE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delete without bypass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AccessDenied ... object protected by object lock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identical text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete with &lt;code&gt;BypassGovernanceRetention&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Succeeds&lt;/td&gt;
&lt;td&gt;Refused with identical text&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because the text is identical, the response does not tell you why the bypass did not work. The first things to read are &lt;code&gt;get-object-lock-configuration&lt;/code&gt; and &lt;code&gt;head-object&lt;/code&gt;'s &lt;code&gt;ObjectLockMode&lt;/code&gt; rather than the IAM policy, because the same text covers both a missing permission and a mode that has no bypass.&lt;/p&gt;

&lt;h3&gt;
  
  
  The lock indicator that does not change under automatic locking
&lt;/h3&gt;

&lt;p&gt;Assigning a policy whose schedules carry a &lt;code&gt;retentionPeriod&lt;/code&gt; leaves the locking feature flag at &lt;code&gt;false&lt;/code&gt;; only the assigned policy name changes. Reading the state alone suggests nothing will be locked, while every snapshot that policy takes is in fact locked. The judgement needs the assigned policy's &lt;code&gt;retentionPeriod&lt;/code&gt; as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  The usable endpoints, derived from the API rather than a config file
&lt;/h3&gt;

&lt;p&gt;An alias copied into a config file looks equally correct for an access point that has been deleted and for one that is &lt;code&gt;MISCONFIGURED&lt;/code&gt;. &lt;code&gt;fsx describe-s3-access-point-attachments&lt;/code&gt; returns &lt;code&gt;Lifecycle&lt;/code&gt; and the Internet or VPC origin, so derive the inventory from there. Always follow the pagination: read only the first page and &lt;strong&gt;an access point that exists goes missing in a way indistinguishable from one that does not.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Two places the documentation and the running system disagreed
&lt;/h2&gt;

&lt;p&gt;Two things behaved differently from what I read before implementing them. Both changed the screen's wording.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subject&lt;/th&gt;
&lt;th&gt;Expected from the documentation&lt;/th&gt;
&lt;th&gt;Measured (ONTAP 9.18.1P3D1)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deleting a QoS policy while assigned&lt;/td&gt;
&lt;td&gt;The CLI reference says it is refused without &lt;code&gt;-force&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;REST accepts it, and the volume's assignment is silently removed (every ceiling becomes 0, meaning unlimited)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enforcement after deleting a quota rule&lt;/td&gt;
&lt;td&gt;The REST reference says it stays in effect until enforcement is cycled off and on&lt;/td&gt;
&lt;td&gt;The deleted rule's ceiling disappeared from the usage report immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The QoS confirmation text was rewritten. The correct warning is not "it cannot be deleted" but "&lt;strong&gt;deleting it removes the ceilings from every volume using that policy&lt;/strong&gt;". A note was added that releasing a single volume means assigning &lt;code&gt;none&lt;/code&gt;, not deleting the policy.&lt;/p&gt;




&lt;h2&gt;
  
  
  The eight defaults that can invalidate a measurement
&lt;/h2&gt;

&lt;p&gt;Up to here the defaults decided whether an operation goes through. &lt;strong&gt;Measurement has defaults of the same shape.&lt;/strong&gt; None of them returns an error: each returns a plausible figure while measuring something other than what you intended.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where these figures come from&lt;/strong&gt;: a different measurement from the environment above. 2026-09-01 to 02, ap-northeast-1, ONTAP 9.18.1P3D1, SINGLE_AZ_1, throughput tiers 128 MBps and 2048 MBps, client c5n.9xlarge. Repeatability across repeat runs of the same measurement was 0.02–0.14%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sixth is from a different measurement.&lt;/strong&gt; 2026-09-06, ap-northeast-1, ONTAP 9.18.1P3D1,&lt;br&gt;
SINGLE_AZ_2, throughput 6,144 MBps, an SMB SVM joined to AWS Managed AD, clients c5n.2xlarge and&lt;br&gt;
c5n.9xlarge. A different generation and a different tier from the five above, so do not put the&lt;br&gt;
figures side by side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sixth does not record the NVMe read cache state.&lt;/strong&gt; The second generation at 6,144 MBps has&lt;br&gt;
1,900 GB of NVMe read cache and &lt;strong&gt;it ships enabled&lt;/strong&gt; (confirmed on a live file system on&lt;br&gt;
2026-09-10). The difference Multichannel makes is a channel count and is unaffected by this gap,&lt;br&gt;
but &lt;strong&gt;do not cite 942 and 1,824 as disk-path figures.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The seventh and eighth are from a third measurement.&lt;/strong&gt; 2026-09-10, ap-northeast-1,&lt;br&gt;
ONTAP 9.18.1P6, SINGLE_AZ_2, 1,536 MBps and 6,144 MBps, 8,192 GiB of SSD, client c5n.9xlarge.&lt;br&gt;
&lt;strong&gt;The NVMe read cache state is part of the recorded conditions.&lt;/strong&gt; It ships enabled at 6,144, so it&lt;br&gt;
was disabled on both nodes and read back before measuring.&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;Default&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;th&gt;How to notice&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linux NFS uses one TCP connection&lt;/td&gt;
&lt;td&gt;Stops around 590 MB/s and &lt;strong&gt;does not respond to stream count&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;1 / 4 / 8 give the same figure&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nconnect=16&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dd if=/dev/zero&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zero blocks skip disk and &lt;strong&gt;return at 4x the published ceiling&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Exceeds the published ceiling&lt;/td&gt;
&lt;td&gt;Use incompressible data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volume inline efficiency is on&lt;/td&gt;
&lt;td&gt;Compressible or identical payloads collapse (4x on reads, under 5% on writes)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;space_savings.dedupe_percent&lt;/code&gt; is high&lt;/td&gt;
&lt;td&gt;Turn it off &lt;strong&gt;before writing&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DiskIopsConfiguration: AUTOMATIC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3 IOPS/GiB, so IOPS becomes the limit&lt;/td&gt;
&lt;td&gt;MB/s divided by IOPS is implausibly small&lt;/td&gt;
&lt;td&gt;&lt;code&gt;USER_PROVISIONED&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A read only slightly over the cache size&lt;/td&gt;
&lt;td&gt;The cache-served share dominates and &lt;strong&gt;the disk path is not measured&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DiskReadBytes&lt;/code&gt; divided by &lt;code&gt;DataReadBytes&lt;/code&gt; is a few per cent or less&lt;/td&gt;
&lt;td&gt;Read &lt;strong&gt;at least twice&lt;/strong&gt; the cache in one pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB Multichannel ships disabled&lt;/td&gt;
&lt;td&gt;One channel, and it holds &lt;strong&gt;while the session reports &lt;code&gt;dialect=3.1.1&lt;/code&gt;&lt;/strong&gt; (1 MiB sequential read 942 against 1,824 MB/s)&lt;/td&gt;
&lt;td&gt;One established TCP connection to the SVM; &lt;code&gt;Get-SmbMultichannelConnection&lt;/code&gt; returns nothing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vserver cifs options modify -is-multichannel-enabled true&lt;/code&gt;. &lt;strong&gt;It does not reach existing connections — restart the client's SMB service and remap&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No window length decided&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Measures while the burst allowance is still there. &lt;strong&gt;2.0x on the same environment and the same shape&lt;/strong&gt; (2,882 MB/s for 27 minutes, then 1,439 MB/s)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;FileServerDiskThroughputUtilization&lt;/code&gt; is above 100%. It is a ratio &lt;strong&gt;against baseline&lt;/strong&gt;, so it can be (median 203.5% measured)&lt;/td&gt;
&lt;td&gt;Run until the allowance is gone. Read &lt;code&gt;FileServerDiskThroughputBalance&lt;/code&gt; over the same window and record the balance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reading straight after a fill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Competes with the fill's write-back and &lt;strong&gt;runs about 20% low for 90 seconds&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Disagrees with the same shape measured a second time (39% measured)&lt;/td&gt;
&lt;td&gt;Leave two minutes after the write finishes. Put the wait in the parameter file, not in a note in the runbook&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;How the fifth one surfaced. I assumed 280 GiB would not fit in cache. Read through Amazon CloudWatch's &lt;code&gt;AWS/FSx&lt;/code&gt; namespace, &lt;code&gt;DiskReadBytes&lt;/code&gt; divided by &lt;code&gt;DataReadBytes&lt;/code&gt; came to 1.4 / 0.9 / 1.5%. &lt;strong&gt;98.5-99.9% of the bytes read never touched disk.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 2048 MBps tier's in-memory cache is 256 GB, that is 238 GiB (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/performance.html" rel="noopener noreferrer"&gt;FSx for ONTAP performance&lt;/a&gt;; ap-northeast-1 falls under the first-generation Single-AZ "all other Regions" table). &lt;strong&gt;280 GiB exceeds it by 18%, not by a factor of two or three. "Past the cache" and "not served from the cache" are different things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And clearing the cache by a wide margin does not help if the window is short.&lt;/strong&gt; In the seventh&lt;br&gt;
measurement the working set was 30 times the in-memory cache, and the figure still came back 2.0x&lt;br&gt;
high for the 27 minutes the burst allowance lasted. &lt;strong&gt;"Read at least twice the cache" answers the&lt;br&gt;
fifth, not the seventh.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inline efficiency can be restored, provided you wait.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Rejected unless efficiency.op_state is idle
PATCH /api/storage/volumes/{uuid}  {"efficiency": {"compression": "inline"}}
PATCH /api/storage/volumes/{uuid}  {"efficiency": {"dedupe": "both"}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But only turn it off on a volume you can discard.&lt;/strong&gt; Immediately after writing, a background efficiency operation is running and the wait is not predictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting it in a runbook
&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;What to write down&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;
&lt;strong&gt;To create a FlexGroup through the ONTAP REST API, fetch the aggregate with &lt;code&gt;GET /storage/aggregates&lt;/code&gt; and name it.&lt;/strong&gt; If you do not need to choose the aggregate, create the volume from the Amazon FSx API instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Set the rebalance &lt;code&gt;max_runtime&lt;/code&gt; to at least 30 minutes and below the time to the next snapshot.&lt;/strong&gt; Do not pass the default through&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Do not treat starting a rebalance as an operation you can press at any time.&lt;/strong&gt; &lt;code&gt;granular data&lt;/code&gt; is enabled irreversibly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Do not show usage as a single number.&lt;/strong&gt; Separate live data from snapshots, or nobody can reach the cause of "deleting files did not free space"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;A teardown procedure for an environment with clones must include the split, or the recovery queue purge.&lt;/strong&gt; "The clone was deleted, so the parent can go" does not hold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Errors arrive in ONTAP's vocabulary.&lt;/strong&gt; Keep the AWS-to-ONTAP term mapping at hand before searching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;A runbook for measuring performance states how to drop the defaults first.&lt;/strong&gt; Leave any one of zero-filled data, inline efficiency, an unspecified &lt;code&gt;nconnect&lt;/code&gt; or &lt;code&gt;AUTOMATIC&lt;/code&gt; IOPS in place, and the figure produced is measuring something else&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What I did not measure
&lt;/h2&gt;

&lt;p&gt;"Unverified" is not "impossible". As last time, the boundary stated as a boundary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The actual effect of a rebalance (how far the skew is evened out)&lt;/td&gt;
&lt;td&gt;Unmeasured. Only the conditions to start were measured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance impact after &lt;code&gt;granular data&lt;/code&gt; is enabled&lt;/td&gt;
&lt;td&gt;Unmeasured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A recovery queue with a changed retention&lt;/td&gt;
&lt;td&gt;Unverified. Measured at the 12-hour default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actual tiering into the capacity pool&lt;/td&gt;
&lt;td&gt;Unverified. Needs a different aggregate configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running SnapLock compliance&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Deliberately not done.&lt;/strong&gt; Irreversible, up to the file system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An alternative path for copies over 5 GiB (NFS or SMB)&lt;/td&gt;
&lt;td&gt;Unmeasured. The portal stops at refusing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Part 6 covers the other side: what was left off the screen. How much of ONTAP stays usable alongside the S3 access point path, and the operational work that was handed to a schedule instead of a button.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Nothing in this part was visible while reading the documentation. Creating a FlexGroup through the ONTAP REST API needs the aggregate named, a capacity rebalance never starts with the defaults, &lt;code&gt;space.used&lt;/code&gt; means a different quantity per volume, and a deleted FlexClone volume blocks the parent's delete while invisible through the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What they share is that the error text does not name the cause.&lt;/strong&gt; That is why the causation and the exits live on the screen. As with the one-second A/B on the rebalance, another explanation looked just as correct until one control was added.&lt;/p&gt;

&lt;p&gt;There is also one correction. I published "purge needs diag privilege, so wait 12 hours" and later found it goes through. &lt;strong&gt;I blamed privilege without trying it.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The figures come from one specific environment and configuration and will differ with workload and setup. The error codes were observed on ONTAP 9.18.1P3D1.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>fsxforontap</category>
      <category>ontap</category>
      <category>storage</category>
    </item>
    <item>
      <title>Putting FSx for ONTAP operations in a browser — the buttons I disabled, and the ones I never built (File Portal Part 4)</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Fri, 04 Sep 2026 00:57:42 +0000</pubDate>
      <link>https://dev.to/aws-builders/putting-fsx-for-ontap-operations-on-a-file-portal-on-aws-182-actions-and-the-design-of-425g</link>
      <guid>https://dev.to/aws-builders/putting-fsx-for-ontap-operations-on-a-file-portal-on-aws-182-actions-and-the-design-of-425g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887"&gt;Part 1&lt;/a&gt; built the foundation of a file portal on top of Amazon FSx for NetApp ONTAP (FSx for ONTAP) S3 access points. &lt;a href="https://dev.to/aws-builders/embedding-storage-operations-into-a-file-portal-from-arpai-incident-response-to-regulatory-1oih"&gt;Part 2&lt;/a&gt; added ransomware response and WORM retention. &lt;a href="https://dev.to/aws-builders/embedding-ai-agents-into-a-file-portal-from-agentcore-mcp-to-multi-agent-teams-part-3-19m1"&gt;Part 3&lt;/a&gt; put AI agents on it.&lt;/p&gt;

&lt;p&gt;Those parts were screens for people who handle files. From here the subject is the people who operate the storage itself. Volume create and resize, FlexCache, FlexClone, SnapMirror, snapshot policies, QoS, quotas, SMB shares. The portal now reaches 182 operations.&lt;/p&gt;

&lt;p&gt;The readers I have in mind are infrastructure owners running FSx for ONTAP, and developers building this kind of admin screen with Amplify Gen2. ONTAP terms get a one-line gloss on first use, so ONTAP operations experience is not assumed. You can start here without Parts 1-3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this part covers&lt;/strong&gt; is what had to be decided to put operations on a screen: the design of delegation and record, and the treatment of buttons that cannot be pressed and buttons that were never built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this part does not cover&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ONTAP features themselves (ONTAP's own documentation is more accurate)&lt;/li&gt;
&lt;li&gt;The failures the real cluster produced (that is an article's worth on its own, so it became Part 5)&lt;/li&gt;
&lt;li&gt;Which retention period satisfies your regulatory requirements (a legal and compliance judgement that neither I nor the portal can stand in for)&lt;/li&gt;
&lt;li&gt;Performance and scale limits of the screen (unmeasured; no concurrent-user test was run)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Three management interfaces reach the file system, and two of them are for administrators inside the VPC. &lt;strong&gt;What the portal adds is not a path but delegation and record&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The portal does not remove the path into the VPC. &lt;strong&gt;The path did not disappear; what holds it moved from a person to a Lambda&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The number 182 is not maintained by hand. &lt;strong&gt;A script counts it and CI fails when it disagrees&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A button that cannot be pressed says what is missing. &lt;strong&gt;A button that does nothing is indistinguishable from a broken one&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The confirmation screen for an irreversible operation states &lt;strong&gt;what becomes undeletable, for how long, and what it costs meanwhile&lt;/strong&gt;, because the effect does not stop at the resource named in the call&lt;/li&gt;
&lt;li&gt;After writing this article I ran the screen and found &lt;strong&gt;the lock indicator was broken.&lt;/strong&gt; While only one panel was being read, it looked correct&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The repository is &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/amplify-portal" rel="noopener noreferrer"&gt;solutions/amplify-portal/&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test environment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Date&lt;/td&gt;
&lt;td&gt;2026-09-02 (JST)&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;ONTAP version&lt;/td&gt;
&lt;td&gt;9.18.1P3D1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portal&lt;/td&gt;
&lt;td&gt;Amplify Gen2 sandbox: AppSync + Cognito + VPC Lambdas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda runtime&lt;/td&gt;
&lt;td&gt;Python 3.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How the action count was taken&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;scripts/portal_action_types.py --check&lt;/code&gt; and &lt;code&gt;scripts/check_portal_action_params.py&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup&lt;/td&gt;
&lt;td&gt;Test sandboxes deleted. One working stack retained&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The distinction that matters most — a path that persists, a holder that moved
&lt;/h2&gt;

&lt;p&gt;First, the premise. Without routing through an additional third-party SaaS, three management interfaces reach FSx for ONTAP.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Interface&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Reach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Management Console / FSx API&lt;/td&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;td&gt;The AWS-side operations on file systems, SVMs and volumes; backups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP CLI (SSH)&lt;/td&gt;
&lt;td&gt;SSH to the management endpoint&lt;/td&gt;
&lt;td&gt;Roughly an ONTAP cluster administrator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP REST API&lt;/td&gt;
&lt;td&gt;HTTPS to the management endpoint&lt;/td&gt;
&lt;td&gt;The CLI's operations, programmatically&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The sources, and how anything outside this list is treated, are in &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/fsx-ontap-management-interfaces.md" rel="noopener noreferrer"&gt;Management interfaces&lt;/a&gt;. The CLI and REST management endpoints are reachable &lt;strong&gt;only from inside the VPC or through a Transit Gateway peered network&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One consequence follows directly. Consider an everyday request: add a volume, or raise one quota. Answering it needs &lt;code&gt;fsxadmin&lt;/code&gt;-equivalent credentials and a path into the VPC. So every request lands on the infrastructure owner, and none of it can be handed to anyone else.&lt;/p&gt;

&lt;p&gt;That separation is what the portal does.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delegation.&lt;/strong&gt; A Cognito group (&lt;code&gt;storage-admin&lt;/code&gt;) decides who may run what, per operation. No SSH key and no ONTAP credential is handed out. The credential lives in Secrets Manager and only the Lambda reads it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A record.&lt;/strong&gt; Who ran which operation is retained with the Cognito principal. Unlike an SSH session on a shared account, it attaches to a person&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The portal does not remove the path into the VPC: the Lambda that calls the ONTAP REST API sits inside it. &lt;strong&gt;The path did not disappear; what holds it moved from a person to a Lambda.&lt;/strong&gt; That distinction affects operational design, so the repository documentation words it the same way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The reach of the screen
&lt;/h2&gt;

&lt;p&gt;The portal reaches 182 actions across 12 endpoints. That number is not maintained by hand. A script reconciles the generated types against the handlers and prints it.&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;python3 scripts/portal_action_types.py &lt;span class="nt"&gt;--check&lt;/span&gt;
DISPATCH ACTION TYPES: PASS &lt;span class="o"&gt;(&lt;/span&gt;182 actions declared, matching the handlers&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;python3 scripts/check_portal_action_params.py
PORTAL ACTION PARAMS: PASS &lt;span class="o"&gt;(&lt;/span&gt;187 literal call sites, 182 actions
across 12 endpoints, 1 call&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; not statically readable&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason it is not counted by hand is not that the number grew. &lt;strong&gt;Types do not cross the boundary between the screen and the Lambda.&lt;/strong&gt; The portal's dispatch takes an untyped &lt;code&gt;params&lt;/code&gt;, so code that sends &lt;code&gt;{snapshotName, retentionDays}&lt;/code&gt; to an action reading &lt;code&gt;snapshotId&lt;/code&gt; and &lt;code&gt;expiryTime&lt;/code&gt; compiles, lints, renders a button, and fails on every click. CI now fails until the reconciliation agrees.&lt;/p&gt;

&lt;p&gt;By domain.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Operations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Volume lifecycle&lt;/td&gt;
&lt;td&gt;Create / resize / delete / bring online, for both FlexVol and FlexGroup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexGroup capacity rebalance&lt;/td&gt;
&lt;td&gt;Read state / start / stop / schedule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexCache&lt;/td&gt;
&lt;td&gt;Create / list / resize / write-back toggle / delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexClone&lt;/td&gt;
&lt;td&gt;Create / list / split&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapMirror&lt;/td&gt;
&lt;td&gt;Create / list / transfer history / update now / quiesce / resume / break / resync / abort / delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshots&lt;/td&gt;
&lt;td&gt;List / create / delete / lock / policy create / assign / enable locking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapLock and WORM&lt;/td&gt;
&lt;td&gt;Read configuration / change retention, S3 Object Lock retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARP/AI (ransomware protection)&lt;/td&gt;
&lt;td&gt;State change / bulk enable / suspect list and clear / surge parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Containment&lt;/td&gt;
&lt;td&gt;Block NFS IP / block SMB user / disconnect sessions / list active blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QoS&lt;/td&gt;
&lt;td&gt;Policy create / update / delete / assign to and release from a volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quotas&lt;/td&gt;
&lt;td&gt;Rule create / update / delete / usage report / per-volume enforcement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB&lt;/td&gt;
&lt;td&gt;Share create, update, delete; local users and groups; members; name mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qtrees&lt;/td&gt;
&lt;td&gt;Create / update / rename / delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export policies&lt;/td&gt;
&lt;td&gt;Policy and rule create / delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FPolicy and Vscan&lt;/td&gt;
&lt;td&gt;Policy and event create / enable / delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peering&lt;/td&gt;
&lt;td&gt;Cluster peer and SVM peer create / accept / delete, intercluster LIFs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster inventory&lt;/td&gt;
&lt;td&gt;Nodes / licenses / LIFs / protocols / DNS / jobs, EMS events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;Bucket list, Object Lock status and retention&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;182 is not every ONTAP operation. Operations that need advanced privilege are absent from the REST API, so they cannot appear on this screen.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnmtablhvkzuzfah8kk0d.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnmtablhvkzuzfah8kk0d.png" alt="The volume list, showing live data, snapshot usage and reserve spill separately, and the FlexVol or FlexGroup style" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The plain statement of what cannot be done
&lt;/h2&gt;

&lt;p&gt;The copy limit is the clearest case. It is &lt;strong&gt;easy to confuse with the upload limit&lt;/strong&gt;, so the measured values are split out.&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;Limit&lt;/th&gt;
&lt;th&gt;Measured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Upload (single &lt;code&gt;PutObject&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;5 GiB&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MaxSizeAllowed&lt;/code&gt; = 5,368,709,120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upload (multipart, whole object)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50 GiB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;53,687,091,200. Checked only at &lt;code&gt;CompleteMultipartUpload&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Download (&lt;code&gt;GetObject&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No limit&lt;/td&gt;
&lt;td&gt;Objects over 50 GiB were retrieved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy (single &lt;code&gt;CopyObject&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;5 GiB&lt;/td&gt;
&lt;td&gt;As above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy (&lt;code&gt;UploadPartCopy&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Unusable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Documented as Supported; every form returned &lt;code&gt;NoSuchKey&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Uploads do reach 50 GiB (measured with multipart). Copies, though, have no path past 5 GiB. &lt;code&gt;UploadPartCopy&lt;/code&gt; is listed as Supported in &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 compatibility&lt;/a&gt; and returned &lt;code&gt;NoSuchKey&lt;/code&gt; in every form measured. Rename, move, copy, trash and restore in the portal are all &lt;code&gt;CopyObject&lt;/code&gt;, so that 5 GiB becomes the screen's limit.&lt;/p&gt;

&lt;p&gt;The units caught me out as well. The AWS documentation's "5 GB" and "50 GB" both turned out to be binary when measured. &lt;code&gt;MaxSizeAllowed&lt;/code&gt; from &lt;code&gt;PutObject&lt;/code&gt; is 5,368,709,120 = 5 × 1024³, not the decimal 5,000,000,000. The detail is in &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/s3ap-object-size-limits-verification.md" rel="noopener noreferrer"&gt;the object size limit verification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Rather than surfacing an S3 error halfway through, the portal checks the size before the copy and refuses with a reason.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;{key}&lt;/code&gt; is 6.2 GiB. A single copy is limited to 5 GiB, and the multipart copy that would lift the limit is not usable on this Access Point, so this operation cannot be completed from the portal. Move the file over NFS or SMB instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The guard's 5 GiB threshold is the same value as the measured &lt;code&gt;MaxSizeAllowed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the same reasoning, no button was built for converting a FlexVol to a FlexGroup. The conversion is ONTAP CLI advanced-privilege only, with no REST equivalent. Instead the screen carries the prerequisites, the irreversibility, the treatment of existing snapshots, and what AWS recommends — a copy into a new FlexGroup with AWS DataSync rather than an in-place conversion. &lt;strong&gt;Writing down that something is absent, and why, is more useful than a button that cannot be pressed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The free-space estimate for a clone split (&lt;code&gt;volume clone split estimate&lt;/code&gt;) is the same. It exists only in the CLI, so it cannot appear here. Rather than omitting it silently, the screen says why it is missing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the confirmation screen states for an irreversible operation
&lt;/h2&gt;

&lt;p&gt;SnapLock and snapshot locking cannot be undone. "Are you sure?" on its own does not tell the person pressing it what becomes undeletable, so the screen states this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The SnapLock type (compliance) can only be set at creation and cannot be changed or removed afterwards&lt;/li&gt;
&lt;li&gt;Under compliance, nobody can delete until retention expires. Not the account administrator, and not AWS&lt;/li&gt;
&lt;li&gt;While one unexpired WORM file remains, this volume, its SVM, and the &lt;strong&gt;file system&lt;/strong&gt; cannot be deleted&lt;/li&gt;
&lt;li&gt;While the file system cannot be deleted, it keeps billing&lt;/li&gt;
&lt;li&gt;A newly created empty volume can still be deleted. It stops being deletable when the first file is committed to WORM&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The last two lines are the point. The effect of an irreversible operation does not stop at the resource; it propagates to the parent and to the bill. &lt;strong&gt;A test environment is the worst place to put a file system you cannot delete for six months.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Snapshot locking is written up with its asymmetry intact. A lock can only be extended, never shortened or released. The policy side is reversible: removing the retention, switching policies, or detaching stops new locks. But &lt;strong&gt;snapshots already locked stay until they expire.&lt;/strong&gt; Compressing that asymmetry into one sentence makes one half of it wrong.&lt;/p&gt;

&lt;p&gt;A retention count does not act as a ceiling either. Locked snapshots cannot be rotated out, so they accumulate past the count until expiry and consume the space. That is on the screen too.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sm6phi7lmobhdzbnv9g.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3sm6phi7lmobhdzbnv9g.png" alt="The snapshot lock confirmation, with the retention period and the date until which deletion is impossible" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Irreversible operations carry a type-to-confirm (the keyword typed in capitals). The API also requires &lt;code&gt;acknowledgeIrreversible&lt;/code&gt;, so a call that bypasses the UI passes the same check.&lt;/p&gt;

&lt;p&gt;What is written here is the technical blast radius of an operation. Which retention period satisfies your regulatory requirements is a legal and compliance judgement, and neither this article nor the portal stands in for it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I did not measure
&lt;/h2&gt;

&lt;p&gt;"Unverified" is not "impossible". The boundary of this article, stated as a boundary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Behaviour under concurrent users&lt;/td&gt;
&lt;td&gt;Unmeasured. Only single-operation latency was observed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operations needing advanced privilege&lt;/td&gt;
&lt;td&gt;Out of scope. Absent from the REST API, so they cannot be on the screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enabling SnapLock compliance from the screen&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Deliberately not done.&lt;/strong&gt; Irreversible, and it would make a test file system undeletable for months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-tier delegation with more Cognito groups&lt;/td&gt;
&lt;td&gt;Unverified. There is one tier, &lt;code&gt;storage-admin&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-term retention and search of the audit record&lt;/td&gt;
&lt;td&gt;Unverified. The record is kept; no retention design was done&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Part 5 covers what the real cluster refused while these 182 actions were being built: why creating a FlexGroup through the ONTAP REST API needs the target aggregate named, why a capacity rebalance never starts with ONTAP's defaults, and the two places the documentation and the running system disagreed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Putting operations on a screen was not a matter of listing features. &lt;strong&gt;Three paths already reach the file system and none of them went away, so what a screen adds is delegation and record, not reach.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Half the screen's job turned out to be saying what cannot be done rather than showing what can. Name the missing input on a button that cannot be pressed, write down absent features with the reason, and give irreversible operations their blast radius. Where a limit is measurable — the 5 GiB copy — using the measured value directly in the guard keeps the screen and the storage saying the same thing.&lt;/p&gt;

&lt;p&gt;And one display defect surfaced after the article was written. &lt;strong&gt;Until two panels were reconciled, the broken indicator looked correct.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The figures come from one specific environment and configuration and will differ with workload and setup. This article does not stand in for governance or regulatory judgement.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>fsxforontap</category>
      <category>amplify</category>
      <category>storage</category>
    </item>
    <item>
      <title>AWS Backup now copies FSx for ONTAP backups across Regions and accounts — a new option, measured Tokyo to Osaka</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Sat, 29 Aug 2026 05:02:38 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-backup-now-copies-fsx-for-ontap-backups-across-regions-and-accounts-a-new-option-measured-3f60</link>
      <guid>https://dev.to/aws-builders/aws-backup-now-copies-fsx-for-ontap-backups-across-regions-and-accounts-a-new-option-measured-3f60</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;How do you approach disaster recovery and backup for your file systems on AWS? Take FSx for ONTAP and a pair of Japanese Regions as the example: if Tokyo is primary, standing up a file system in Osaka and replicating into it continuously with SnapMirror is probably the first shape that comes to mind for anyone who has run ONTAP, or is about to evaluate it. Plenty of AWS users run exactly that in production, and as a way to hold availability it is still the best answer. RPO comes down from the 60 minutes AWS Backup offers at its shortest to SnapMirror's 5-minute minimum interval, switching between active and standby is breaking the relationship and promoting the destination, and cutting data back the other way is a &lt;code&gt;snapmirror resync&lt;/code&gt; in the reverse direction.&lt;/p&gt;

&lt;p&gt;The trouble was that "I just want the data sitting on the DR side, without operating a file system there until I actually restore" required the same shape. Amazon FSx for NetApp ONTAP volume backups were created and restored inside the file system's own Region and account, and could not cross a Region boundary. AWS Backup could already copy backups across Regions and accounts for FSx for Windows File Server, FSx for OpenZFS, and FSx for Lustre (&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/04/backup-extends-fsx-support/" rel="noopener noreferrer"&gt;AWS Backup extends Amazon FSx support to 5 additional AWS Regions and expands cross-Region and cross-account copy to 14 AWS Regions&lt;/a&gt;, April 2026), but FSx for ONTAP was not in that set at the time.&lt;/p&gt;

&lt;p&gt;Then on 27 August 2026 that gap closed along the two paths below. &lt;strong&gt;One more shape became available to choose from in a backup strategy.&lt;/strong&gt; The two were announced separately.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/08/fsx-ontap-cross-region-backup-copy/" rel="noopener noreferrer"&gt;Amazon FSx for NetApp ONTAP now supports copying backups across AWS Regions and accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/08/aws-backup-amazon-fsx-netapp-cross-account-region/" rel="noopener noreferrer"&gt;AWS Backup adds cross-Region and cross-account backup support for Amazon FSx for NetApp ONTAP&lt;/a&gt;&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;Path&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Scheduling&lt;/th&gt;
&lt;th&gt;Cross-account&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;FSx for ONTAP backup copy&lt;/strong&gt; (&lt;code&gt;CopyBackup&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Copies a volume backup to another Region&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; Run it each time&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;No.&lt;/strong&gt; Same account only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;AWS Backup&lt;/strong&gt; (policy-based backup management service)&lt;/td&gt;
&lt;td&gt;Copies FSx for ONTAP backups to another Region and another account&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes.&lt;/strong&gt; Copy rules in a backup plan&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes.&lt;/strong&gt; Requires AWS Organizations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So I copied a backup from Tokyo to Osaka, restored it at the destination, and confirmed the content matched. This post covers that procedure, along with the design and operational considerations that came with it.&lt;/p&gt;

&lt;p&gt;Up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Backup now copies FSx for ONTAP backups to another Region and another account&lt;/strong&gt; (27 August 2026). The FSx for ONTAP &lt;code&gt;CopyBackup&lt;/code&gt; also reaches another Region, but it is &lt;strong&gt;same-account and manual&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moving them to another account needs a CMK.&lt;/strong&gt; AWS managed keys cannot be used for a cross-account copy, and a file system's KMS key is set at creation — so it is &lt;strong&gt;a decision made before the file system exists&lt;/strong&gt; (below).&lt;/li&gt;
&lt;li&gt;What matters is that &lt;strong&gt;you no longer need a file system at the destination until you recover&lt;/strong&gt;. The restore target is still the Region the backup lives in, so &lt;strong&gt;creating that file system at recovery time lands on your RTO&lt;/strong&gt; — 20 minutes, measured (below).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CopyBackup&lt;/code&gt; has no scheduler. Recurring copies mean &lt;strong&gt;an AWS Backup backup plan&lt;/strong&gt; or your own automation. &lt;strong&gt;The AWS Backup copy and restore were measured too&lt;/strong&gt; — 6 m 31 s to 8 m 35 s to copy, 16 m 16 s to restore, at 9 MB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This does not replace SnapMirror.&lt;/strong&gt; If a minutes-level RPO and a failback procedure are requirements, SnapMirror still is the answer. &lt;strong&gt;Replication and copy are different operations&lt;/strong&gt; and leave different things at the destination (below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restore time is proportional to capacity.&lt;/strong&gt; My 13 minutes was a figure for 9.4 MiB. Restoring 10 TB on a minimum configuration is 22 hours at the floor, calculated from AWS published rates. I could not find a measured figure at 10 TB or above in public sources (below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost inverts with scale.&lt;/strong&gt; A Single-AZ capacity pool is 47.6% of backup storage ($0.0238 against $0.050, retrieved from the Price List API), so past roughly 4.8 TB a standing destination file system is &lt;em&gt;lower&lt;/em&gt; monthly. Choosing to keep one running is not choosing the more expensive option (below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;: within one account, copying a volume backup from &lt;code&gt;ap-northeast-1&lt;/code&gt; to &lt;code&gt;ap-northeast-3&lt;/code&gt;, restoring it into a file system there, and verifying the content. &lt;strong&gt;Both &lt;code&gt;CopyBackup&lt;/code&gt; and AWS Backup were measured.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out of scope&lt;/strong&gt;: cross-account copies (they require AWS Backup with AWS Organizations — I show the shape but &lt;strong&gt;did not measure it&lt;/strong&gt;), building or failing back a SnapMirror relationship, durations at production data volumes, and reconciling inter-Region transfer charges against a bill.&lt;/p&gt;




&lt;h2&gt;
  
  
  Shape of the setup
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp821j3l4dd2vmzwph7wb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp821j3l4dd2vmzwph7wb.png" alt="Copying a backup from Tokyo to Osaka. The FSx for ONTAP CopyBackup and an AWS Backup copy rule are separate paths, both restores need an existing file system and SVM in Osaka, and the Osaka side holds no file system until a recovery starts" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Dark theme: &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-Adoption-Playbook/00d596de51233a9238ed89d80d9604da242b3f3b/docs/_assets/images/png/backup-copy-cross-region-en-dark@2x.png" rel="noopener noreferrer"&gt;backup-copy-cross-region-en-dark@2x.png&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The right-hand side being empty in normal operation is the whole point.&lt;/strong&gt; With SnapMirror, that right-hand side holds an equivalent file system at all times.&lt;/p&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;Day to day&lt;/th&gt;
&lt;th&gt;When recovering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tokyo (production)&lt;/td&gt;
&lt;td&gt;file system + RW volume + backups&lt;/td&gt;
&lt;td&gt;unchanged, or lost if the Region is affected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Osaka (destination)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;the backup copy only.&lt;/strong&gt; No file system&lt;/td&gt;
&lt;td&gt;file system → SVM → restore into a new volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Billing&lt;/td&gt;
&lt;td&gt;Osaka pays backup storage only&lt;/td&gt;
&lt;td&gt;Osaka's file system starts billing once it exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running the copy&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CopyBackup&lt;/code&gt; each time, or an AWS Backup copy rule&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;On vendor neutrality&lt;/strong&gt;: this post evaluates FSx for ONTAP backup copies on technical grounds only. The section on existing backup products touches other options, but not in a framing where one is better than another. Each suits a different context, and the goal here is to record what actually works and what does not.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Test environment
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Date&lt;/td&gt;
&lt;td&gt;2026-08-28 (&lt;code&gt;CopyBackup&lt;/code&gt; path), 2026-08-29 (AWS Backup path)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runs&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;One per step (n=1).&lt;/strong&gt; Variance was not measured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;AWS CLI v2 and &lt;strong&gt;the AWS Management Console&lt;/strong&gt;. Japanese locale for the &lt;code&gt;CopyBackup&lt;/code&gt; path, &lt;strong&gt;English locale for the AWS Backup path&lt;/strong&gt; (the setting at capture time). &lt;strong&gt;CloudFormation untested&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ap-northeast-1&lt;/code&gt;, first-generation &lt;code&gt;SINGLE_AZ_1&lt;/code&gt;, 1,024 GiB SSD, 128 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Destination&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ap-northeast-3&lt;/code&gt;, same shape, created for this test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9.17.1P7D1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source volume&lt;/td&gt;
&lt;td&gt;FlexVol RW, 1 GiB, tiering &lt;code&gt;NONE&lt;/code&gt;, storage efficiency off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dataset&lt;/td&gt;
&lt;td&gt;5 files / 9.4 MiB (nested directory, symlink, UTF-8 filename, one file at &lt;code&gt;0640&lt;/code&gt;). The AWS Backup run used an equivalent 9,458,747 B set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Backup&lt;/td&gt;
&lt;td&gt;one vault per Region, one backup plan (one rule, one copy rule), selection scoped to the single verification volume ARN, existing &lt;code&gt;AWSBackupDefaultServiceRole&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KMS&lt;/td&gt;
&lt;td&gt;Defaults in each Region: &lt;code&gt;aws/fsx&lt;/code&gt; for the source file system, &lt;code&gt;alias/aws/backup&lt;/code&gt; (&lt;code&gt;KeyManager: AWS&lt;/code&gt;) for the AWS Backup vaults. &lt;strong&gt;CMKs untested, and this configuration cannot do a cross-account copy&lt;/strong&gt; (below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clients&lt;/td&gt;
&lt;td&gt;one &lt;code&gt;t3.micro&lt;/code&gt; (AL2023) per Region, NFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actual spend&lt;/td&gt;
&lt;td&gt;~$0.55 for the &lt;code&gt;CopyBackup&lt;/code&gt; run (destination file system ran 1 h 24 m); under $2 for the AWS Backup run (destination file system ran ~1 h 21 m, two &lt;code&gt;t3.micro&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data used&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Synthetic data from &lt;code&gt;/dev/urandom&lt;/code&gt;.&lt;/strong&gt; No real or personal data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not verified&lt;/td&gt;
&lt;td&gt;Cross-account copies (covered by the &lt;a href="https://community.netapp.com/community/discussion/468353/cross-region-and-cross-account-backup-for-amazon-fsx-for-netapp-ontap" rel="noopener noreferrer"&gt;earlier post&lt;/a&gt;), CMKs, copying &lt;code&gt;AUTOMATIC&lt;/code&gt; backups, incrementality, residue from a failed copy, deploying the CloudFormation template, restoring into an AD-joined SVM, reconciling inter-Region transfer charges, AWS Backup restore testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Do not use these durations as an RTO basis for your environment.&lt;/strong&gt; They are measured against 9.4 MiB, where fixed overhead dominates.&lt;/p&gt;




&lt;h2&gt;
  
  
  When this fits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You want a copy of your data in another Region but &lt;strong&gt;want to avoid running a file system there&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Compliance requires retaining a copy that is logically separated from production&lt;/li&gt;
&lt;li&gt;You want an isolated-account layer against credential compromise or a KMS key compromise&lt;/li&gt;
&lt;li&gt;Recovery measured in tens of minutes to hours is acceptable&lt;/li&gt;
&lt;li&gt;You already take volume backups and only want to add a destination for them&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SnapMirror already covers availability, and you want a separate retention layer detached from production&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to look at something else for now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RPO in minutes is required&lt;/strong&gt; (the guidance figure for backups is 60 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RTO in single-digit minutes is required&lt;/strong&gt; (creating the destination file system took 20 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need a documented path back to production&lt;/strong&gt; (backup copies have none — see below)&lt;/li&gt;
&lt;li&gt;FlexGroup volumes are in scope (copy is unsupported, and creation failed in my environment — see below)&lt;/li&gt;
&lt;li&gt;You need copies spanning China Regions, or cross-account copies in the China Regions (unsupported)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of those apply, a SnapMirror-based design fits the requirement better. Running both together is also coherent, covered below.&lt;/p&gt;




&lt;h2&gt;
  
  
  The distinction that matters most — what changed is where a backup can live
&lt;/h2&gt;

&lt;p&gt;Blur this and the runbook comes out wrong.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Claim&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;A backup can be stored in another Region&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;now possible&lt;/strong&gt; (&lt;code&gt;CopyBackup&lt;/code&gt; or AWS Backup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A backup can be stored in another account&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;now possible, but only through AWS Backup.&lt;/strong&gt; The FSx for ONTAP &lt;code&gt;CopyBackup&lt;/code&gt; stays inside one account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A backup can be restored to a file system in a &lt;strong&gt;different&lt;/strong&gt; Region than the backup&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;no&lt;/strong&gt; (unchanged)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A restore produces a new volume&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;unchanged.&lt;/strong&gt; There is no restore-in-place path&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;using-backups.html&lt;/code&gt; still states that a backup can only be restored to a file system in the Region where the backup is stored. That is not a contradiction — copying &lt;strong&gt;increases the set of Regions where a backup is stored&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So recovery in Osaka is three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A copied backup is already in Osaka (this is what you prepare in advance)&lt;/li&gt;
&lt;li&gt;Create a file system and an SVM in Osaka&lt;/li&gt;
&lt;li&gt;Restore as a new volume&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2 lands on your RTO.&lt;/strong&gt; Not paying for a standby file system means paying for its creation when you need it. That is the trade you are choosing.&lt;/p&gt;




&lt;h2&gt;
  
  
  How &lt;code&gt;CopyBackup&lt;/code&gt; and AWS Backup relate
&lt;/h2&gt;

&lt;p&gt;Two different things are called "copy a backup to another Region" here, so it is worth separating them first. &lt;strong&gt;They are separate mechanisms.&lt;/strong&gt; Neither calls the other, and their control planes and storage locations differ.&lt;/p&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;FSx for ONTAP &lt;code&gt;CopyBackup&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;AWS Backup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Whose API&lt;/td&gt;
&lt;td&gt;Amazon FSx (&lt;code&gt;fsx:CopyBackup&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;AWS Backup (backup plans / copy jobs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What it copies&lt;/td&gt;
&lt;td&gt;An existing FSx for ONTAP volume backup, which must be &lt;code&gt;AVAILABLE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A recovery point AWS Backup took itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where the copy lands&lt;/td&gt;
&lt;td&gt;As an FSx for ONTAP backup in the destination Region&lt;/td&gt;
&lt;td&gt;An AWS Backup &lt;strong&gt;backup vault&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BackupType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;USER_INITIATED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AWS_BACKUP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; Called each time&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes.&lt;/strong&gt; Backup rules and copy rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-account&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes&lt;/strong&gt;, with AWS Organizations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Link back to the source&lt;/td&gt;
&lt;td&gt;Keeps &lt;code&gt;SourceBackupId&lt;/code&gt; and &lt;code&gt;SourceBackupRegion&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Followed through the recovery point ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Here is where the two get conflated.&lt;/strong&gt; You cannot point &lt;code&gt;CopyBackup&lt;/code&gt; at an AWS Backup backup vault, and an AWS Backup copy rule does not call &lt;code&gt;fsx:CopyBackup&lt;/code&gt; on your behalf. Answering "I want &lt;code&gt;CopyBackup&lt;/code&gt; on a schedule" with "use AWS Backup" is right &lt;strong&gt;not because the same call can be scheduled, but because the same goal is reachable through a different mechanism.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The restore constraint, on the other hand, is shared.&lt;/strong&gt; Both paths ask you to pick an &lt;strong&gt;existing file system and SVM&lt;/strong&gt; in the destination Region and both produce a &lt;strong&gt;new volume&lt;/strong&gt;. The AWS Backup restore screen asks for a File system and a Storage virtual machine too (&lt;code&gt;documented&lt;/code&gt;). So the benefit — no standby file system while nothing is wrong — holds for AWS Backup as well, and so does the cost: &lt;strong&gt;file system creation lands on the RTO&lt;/strong&gt; either way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On terminology&lt;/strong&gt;: AWS Backup's own word is copy. How that differs from replication is the next section.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Replication and copy are different words for different things
&lt;/h2&gt;

&lt;p&gt;Two words get used for "keep the data in another Region": replication and copy. &lt;strong&gt;They are different operations, and what sits at the destination differs.&lt;/strong&gt; Without agreeing on that, a requirements conversation goes past itself.&lt;/p&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;SnapMirror &lt;strong&gt;replication&lt;/strong&gt;
&lt;/th&gt;
&lt;th&gt;AWS Backup / &lt;code&gt;CopyBackup&lt;/code&gt; &lt;strong&gt;copy&lt;/strong&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What sits at the destination&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;A volume.&lt;/strong&gt; A &lt;code&gt;DP&lt;/code&gt; volume exists on the destination SVM&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;A backup (recovery point).&lt;/strong&gt; No volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When the source changes&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;It follows.&lt;/strong&gt; Deltas are sent each schedule&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;It does not.&lt;/strong&gt; A point-in-time image; later changes wait for the next backup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;To use the destination&lt;/td&gt;
&lt;td&gt;Break the relationship and promote it to become writable&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;A restore is required, and what you get is a new volume&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the relationship persist&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes.&lt;/strong&gt; Having it in place is the normal state&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;No.&lt;/strong&gt; Each copy is an independent artefact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What sets the RPO&lt;/td&gt;
&lt;td&gt;The replication schedule (down to &lt;strong&gt;5 minutes&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;The backup interval (guidance: &lt;strong&gt;60 minutes&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What the destination costs while idle&lt;/td&gt;
&lt;td&gt;File system capacity and throughput&lt;/td&gt;
&lt;td&gt;Backup storage only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path back to production&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;snapmirror resync&lt;/code&gt; in the other direction&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;There is none&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;AWS's own wording splits the same way.&lt;/strong&gt; The AWS Backup console and documentation call this operation copy throughout — the screens read Copy jobs, Copy rule, &lt;code&gt;Copy type: Manual&lt;/code&gt; (screenshots above). The FSx for ONTAP API is named &lt;code&gt;CopyBackup&lt;/code&gt;. &lt;strong&gt;Replication is the word for mechanisms where the destination exists and tracks the source, as in Amazon S3 Cross-Region Replication.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So "replicate cross-Region with AWS Backup" reads as though &lt;strong&gt;a volume at the destination is tracking the source.&lt;/strong&gt; There isn't one. What is at the destination is a recovery point, and using it takes a restore. The difference lands directly on the RTO: replication promotes a volume that is already there, while a copy starts from &lt;strong&gt;creating a file system and an SVM and then restoring.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reverse mistake is calling SnapMirror a backup. The destination tracks the source, so &lt;strong&gt;a file deleted on the source is gone from the destination's current state after the next transfer&lt;/strong&gt; — what remains is whatever the destination's snapshots still hold. Retaining generations is the job of snapshots or SnapVault, not of the replication itself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SnapVault is a third mechanism.&lt;/strong&gt; ONTAP's SnapVault is a relationship that accumulates generations at the destination. It rides the same transport as SnapMirror with a different purpose. &lt;strong&gt;The backup copies in this post do not create an ONTAP SnapVault relationship.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which one suits which requirement is in choosing between this and SnapMirror.&lt;/p&gt;




&lt;h2&gt;
  
  
  A short glossary
&lt;/h2&gt;

&lt;p&gt;Only the terms this post uses. Skip if you already know them.&lt;/p&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;What it is&lt;/th&gt;
&lt;th&gt;Why it matters here&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File system&lt;/td&gt;
&lt;td&gt;The billing and performance unit. SSD capacity and throughput are provisioned here&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;This is what you no longer need at the destination&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVM (Storage Virtual Machine)&lt;/td&gt;
&lt;td&gt;A logical server inside a file system, holding the NFS / SMB endpoints and auth config&lt;/td&gt;
&lt;td&gt;Required to restore. &lt;strong&gt;A file system alone is not enough&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volume&lt;/td&gt;
&lt;td&gt;Where data lives, and the unit of backup and restore&lt;/td&gt;
&lt;td&gt;A restore produces a &lt;strong&gt;new&lt;/strong&gt; volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Junction path&lt;/td&gt;
&lt;td&gt;Where a volume attaches in the SVM namespace (e.g. &lt;code&gt;/dr_restored&lt;/code&gt;); the NFS mount path&lt;/td&gt;
&lt;td&gt;Specified at restore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexVol / FlexGroup&lt;/td&gt;
&lt;td&gt;Volume styles. FlexVol sits on one aggregate; FlexGroup spreads across several for capacity and parallelism&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;FlexGroup backup copies are unsupported&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;RW&lt;/code&gt; / &lt;code&gt;DP&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Volume type. &lt;code&gt;RW&lt;/code&gt; is read-write, &lt;code&gt;DP&lt;/code&gt; is read-only and used for SnapMirror destinations&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DP&lt;/code&gt; cannot be backed up. &lt;strong&gt;A restoring volume transiently reads as &lt;code&gt;DP&lt;/code&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiering&lt;/td&gt;
&lt;td&gt;Moving cold data from SSD to the cheaper capacity pool. &lt;code&gt;NONE&lt;/code&gt; / &lt;code&gt;AUTO&lt;/code&gt; / &lt;code&gt;SNAPSHOT_ONLY&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Affects read latency right after a restore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshot vs backup&lt;/td&gt;
&lt;td&gt;A snapshot lives inside the file system; a backup is an independent copy outside it&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Only the backup can cross a Region boundary&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  If you run ONTAP on-premises
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ONTAP concept&lt;/th&gt;
&lt;th&gt;Relationship to this capability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SnapMirror&lt;/td&gt;
&lt;td&gt;A different mechanism. Needs cluster peering and a standing destination file system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapVault&lt;/td&gt;
&lt;td&gt;A different mechanism. Backup copies do not create a SnapVault relationship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster peering&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Not required for backup copies.&lt;/strong&gt; Operationally this is the largest difference from SnapMirror&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Driving it from ONTAP CLI / REST&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Backup copies are an AWS-side feature with no corresponding ONTAP CLI command.&lt;/strong&gt; Use the AWS CLI, API, or console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trace left on the volume&lt;/td&gt;
&lt;td&gt;A backup leaves a snapshot named &lt;code&gt;backup-&amp;lt;backup-id&amp;gt;&lt;/code&gt; on the volume&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Prerequisites and permissions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source backup&lt;/td&gt;
&lt;td&gt;must be &lt;code&gt;AVAILABLE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source volume type&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RW&lt;/code&gt; (&lt;code&gt;DP&lt;/code&gt;, LSM, and FlexCache destinations cannot be backed up at all)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volume style&lt;/td&gt;
&lt;td&gt;FlexVol. &lt;strong&gt;FlexGroup copies are unsupported&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition&lt;/td&gt;
&lt;td&gt;Between commercial Regions, between the two China Regions, and between the two GovCloud (US) Regions — &lt;strong&gt;but not across those sets&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KMS&lt;/td&gt;
&lt;td&gt;Encrypted with a key in the destination Region. No parameter needed if the default key is fine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A backup whose source volume has already been deleted can still be copied&lt;/strong&gt; (measured). That is useful after an accidental deletion.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM
&lt;/h3&gt;

&lt;p&gt;The requester &lt;strong&gt;must be able to reach the backup in the source Region&lt;/strong&gt;. Following the shape in the AWS documentation:&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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;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;"CopyBackupAcrossRegions"&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;"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;"fsx:CopyBackup"&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:fsx:*:&amp;lt;your-account-id&amp;gt;:backup/*"&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;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;"DescribeAndRestore"&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;"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="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"fsx:DescribeBackups"&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:DescribeFileSystems"&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:DescribeStorageVirtualMachines"&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:DescribeVolumes"&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:CreateVolumeFromBackup"&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:TagResource"&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;"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;"*"&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;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 &lt;code&gt;"Resource": "*"&lt;/code&gt; on the second statement is a simplification for readability. &lt;strong&gt;Narrow it in production.&lt;/strong&gt; Some &lt;code&gt;Describe*&lt;/code&gt; actions do not support resource-level scoping, but &lt;code&gt;fsx:CreateVolumeFromBackup&lt;/code&gt; can be limited to the destination file system and SVM ARNs. To pin the destination Region, use the &lt;code&gt;aws:RequestedRegion&lt;/code&gt; condition key:&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;"RestrictCopyDestination"&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;"Deny"&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;"fsx:CopyBackup"&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;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&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;"StringNotEquals"&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;"aws:RequestedRegion"&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;"ap-northeast-3"&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;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;&lt;strong&gt;If you have a data residency requirement, put this guard in IAM or an SCP.&lt;/strong&gt; "We have a rule that says not to" does not hold.&lt;/p&gt;

&lt;p&gt;With a customer managed key (CMK), you will additionally need permissions such as &lt;code&gt;kms:CreateGrant&lt;/code&gt; and &lt;code&gt;kms:DescribeKey&lt;/code&gt; on the destination-Region key. &lt;strong&gt;I used the default key, so I have not verified this.&lt;/strong&gt; If you are building on a CMK, prove it with a small volume first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Values to replace with your own
&lt;/h2&gt;

&lt;p&gt;Every command below works once you substitute these.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Placeholder&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;How to get it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;SRC_REGION&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Source Region&lt;/td&gt;
&lt;td&gt;e.g. &lt;code&gt;ap-northeast-1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;DST_REGION&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Destination Region&lt;/td&gt;
&lt;td&gt;e.g. &lt;code&gt;ap-northeast-3&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;SRC_VOLUME_ID&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The RW volume to back up&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;aws fsx describe-volumes --region &amp;lt;SRC_REGION&amp;gt; --query 'Volumes[?OntapConfiguration.OntapVolumeType==&lt;/code&gt;RW&lt;code&gt;].[VolumeId,Name]' --output table&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;SRC_BACKUP_ID&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The backup to copy&lt;/td&gt;
&lt;td&gt;output of step 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;DST_SUBNET_ID&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Subnet for the destination file system&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-subnets --region &amp;lt;DST_REGION&amp;gt; --query 'Subnets[].[SubnetId,AvailabilityZone,CidrBlock]' --output table&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;DST_SG_ID&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Security group for the destination file system&lt;/td&gt;
&lt;td&gt;Must allow NFS / SMB. Sharing one SG with the client plus a self-referencing rule is enough&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;DST_FS_ID&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Destination file system&lt;/td&gt;
&lt;td&gt;output of step 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;DST_SVM_ID&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Destination SVM&lt;/td&gt;
&lt;td&gt;output of step 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;DST_VOLUME_SIZE_BYTES&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restored volume size, in bytes&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Must be at least the data in the backup.&lt;/strong&gt; Reusing the source &lt;code&gt;SizeInBytes&lt;/code&gt; is safest. 1 GiB = &lt;code&gt;1073741824&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two notes on sizing. &lt;strong&gt;The destination file system's SSD capacity must exceed the data being restored&lt;/strong&gt; — restored data is written to SSD first. And &lt;strong&gt;a FlexVol cannot span more than one HA pair.&lt;/strong&gt; For a large volume, settle the destination SSD capacity and generation (second-generation Single-AZ can add HA pairs) before you start.&lt;/p&gt;




&lt;h2&gt;
  
  
  Walkthrough — Tokyo to Osaka
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create a backup (skip if you already have one)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws fsx create-backup &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;SRC_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume-id&lt;/span&gt; &amp;lt;SRC_VOLUME_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Name,Value&lt;span class="o"&gt;=&lt;/span&gt;dr-source &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Purpose,Value&lt;span class="o"&gt;=&lt;/span&gt;cross-region-copy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backup.{Id:BackupId,Life:Lifecycle}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for &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-backups &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;SRC_REGION&amp;gt; &lt;span class="nt"&gt;--backup-ids&lt;/span&gt; &amp;lt;SRC_BACKUP_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backups[0].{Life:Lifecycle,Pct:ProgressPercent}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Copy it
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;--region&lt;/code&gt; is the destination; &lt;code&gt;--source-region&lt;/code&gt; is where the backup is now.&lt;/strong&gt; This reads backwards at first — the copy is requested against the destination Region.&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 copy-backup &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-backup-id&lt;/span&gt; &amp;lt;SRC_BACKUP_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-region&lt;/span&gt; &amp;lt;SRC_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--copy-tags&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Name,Value&lt;span class="o"&gt;=&lt;/span&gt;dr-copy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backup.{Id:BackupId,Life:Lifecycle,Src:SourceBackupId,SrcRegion:SourceBackupRegion}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;--kms-key-id &amp;lt;destination-region-key-arn&amp;gt;&lt;/code&gt; for a CMK. &lt;strong&gt;Incrementality is conditional on using the same KMS key, so changing keys mid-stream makes the next copy full.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Watch progress:&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-backups &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="nt"&gt;--backup-ids&lt;/span&gt; &amp;lt;DST_BACKUP_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backups[0].{Life:Lifecycle,Pct:ProgressPercent}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;COPYING&lt;/code&gt; → &lt;code&gt;AVAILABLE&lt;/code&gt; means done. Mine took 7 m 15 s for a 9.4 MiB volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day-to-day operation ends here.&lt;/strong&gt; Steps 3 to 6 run only when you are recovering.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. [Recovery] Check which generation the destination Region offers
&lt;/h3&gt;

&lt;p&gt;Check the available deployment types &lt;strong&gt;before&lt;/strong&gt; you need them. If first generation (&lt;code&gt;SINGLE_AZ_1&lt;/code&gt;) is available, the minimum throughput is 128 MBps; where only second generation exists it is 384 MBps, which makes standby cost roughly 1.9× higher.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SINGLE_AZ_1&lt;/code&gt; was accepted in Osaka (measured 2026-08-28). &lt;strong&gt;This varies by Region — confirm it for your destination before writing the runbook.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. [Recovery] Create the destination file system
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Billing starts here.&lt;/strong&gt; Even at the minimum, Osaka costs roughly $0.37 per hour, and forgetting it runs about $270 a month. If you are doing this as a drill, &lt;strong&gt;open the teardown commands from step 8 first.&lt;/strong&gt; During my own run I mistyped an option on &lt;code&gt;delete-file-system&lt;/code&gt; and polled for 8 minutes while the command had already failed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;This is the 20 minutes that lands on RTO.&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;aws fsx create-file-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--file-system-type&lt;/span&gt; ONTAP &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage-capacity&lt;/span&gt; 1024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage-type&lt;/span&gt; SSD &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-ids&lt;/span&gt; &amp;lt;DST_SUBNET_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--security-group-ids&lt;/span&gt; &amp;lt;DST_SG_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ontap-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{
    "DeploymentType": "SINGLE_AZ_1",
    "ThroughputCapacity": 128,
    "PreferredSubnetId": "&amp;lt;DST_SUBNET_ID&amp;gt;"
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nv"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Name,Value&lt;span class="o"&gt;=&lt;/span&gt;dr-restore-target &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'FileSystem.{Id:FileSystemId,Life:Lifecycle}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1,024 GiB and 128 MBps are the &lt;strong&gt;first-generation minimums&lt;/strong&gt;. Raise them if your restore exceeds that. &lt;strong&gt;A restore pauses when SSD space runs out&lt;/strong&gt; and resumes automatically once space is available.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. [Recovery] Create the SVM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws fsx create-storage-virtual-machine &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--file-system-id&lt;/span&gt; &amp;lt;DST_FS_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; dr_svm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--root-volume-security-style&lt;/span&gt; UNIX &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'StorageVirtualMachine.{Id:StorageVirtualMachineId,Life:Lifecycle}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For SMB you also need the Active Directory join here. &lt;strong&gt;If AD join is part of your recovery, verify separately that the destination Region can reach your domain controllers, and budget the time.&lt;/strong&gt; My verification was NFS only.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. [Recovery] Restore
&lt;/h3&gt;

&lt;p&gt;A restore always produces a &lt;strong&gt;new volume&lt;/strong&gt;. There is no in-place path.&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 create-volume-from-backup &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--backup-id&lt;/span&gt; &amp;lt;DST_BACKUP_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; dr_restored &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ontap-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{
    "StorageVirtualMachineId": "&amp;lt;DST_SVM_ID&amp;gt;",
    "JunctionPath": "/dr_restored",
    "SizeInBytes": &amp;lt;DST_VOLUME_SIZE_BYTES&amp;gt;,
    "OntapVolumeType": "RW",
    "TieringPolicy": { "Name": "NONE" }
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Volume.{Id:VolumeId,Life:Lifecycle}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;TieringPolicy: NONE&lt;/code&gt; follows the AWS recommendation for consistent read latency after a restore — it assumes you wait for the data to land on SSD before accessing it. Change it if you want the capacity pool.&lt;/p&gt;

&lt;p&gt;Mine reached &lt;code&gt;CREATED&lt;/code&gt; in 13 m 21 s for 9.4 MiB.&lt;/p&gt;

&lt;p&gt;Three conditions drive restore and post-restore performance. &lt;strong&gt;Check these when it feels slow:&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;Condition&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSD capacity&lt;/td&gt;
&lt;td&gt;Restored data is &lt;strong&gt;written to SSD first&lt;/strong&gt;. The restore pauses when space runs out and resumes when it frees up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Second generation is readable during a restore&lt;/strong&gt; (after metadata loads). &lt;strong&gt;First generation makes you wait&lt;/strong&gt; for completion — mine was first generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background priority&lt;/td&gt;
&lt;td&gt;Backup and restore are &lt;strong&gt;lower priority than client I/O&lt;/strong&gt; and consume unused throughput capacity. They progress slowly while production I/O is saturated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The third one has an operational consequence: &lt;strong&gt;overlapping backups with a nightly batch window slows both.&lt;/strong&gt; Separate the backup window from your peak.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Verify the content
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Skip this and you cannot claim you restored anything.&lt;/strong&gt; Order matters: &lt;strong&gt;take the hashes before you create the backup.&lt;/strong&gt; Taking them afterwards mixes in every change since, and you can no longer separate that from a copy problem.&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;# On the source, BEFORE creating the backup&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /mnt/src &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; + | &lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k2&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/before.txt

&lt;span class="c"&gt;# On the restored volume&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /mnt/restored &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sha256sum&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; + | &lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k2&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/after.txt
diff /tmp/before.txt /tmp/after.txt &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;"MATCH"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check permissions and symlinks too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-mindepth&lt;/span&gt; 1 &lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s2"&gt;"%y %m %s %p&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All five sha256 digests matched in my run, and mode &lt;code&gt;0640&lt;/code&gt;, the symlink target, and a UTF-8 (Japanese) filename all survived. I also used the ordering deliberately: &lt;strong&gt;a 2 MiB file added after the backup was absent from the restored volume&lt;/strong&gt;, which is what makes it a point-in-time image rather than a mirror.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Delete what the test created
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Forgetting the destination file system costs roughly $270 a month.&lt;/strong&gt; The order matters.&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;# 1. volume (do not let it create a final backup)&lt;/span&gt;
aws fsx delete-volume &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="nt"&gt;--volume-id&lt;/span&gt; &amp;lt;DST_VOLUME_ID&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ontap-configuration&lt;/span&gt; &lt;span class="s1"&gt;'{"SkipFinalBackup":true}'&lt;/span&gt;

&lt;span class="c"&gt;# 2. SVM (after the volumes are gone)&lt;/span&gt;
aws fsx delete-storage-virtual-machine &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage-virtual-machine-id&lt;/span&gt; &amp;lt;DST_SVM_ID&amp;gt;

&lt;span class="c"&gt;# 3. file system (after the SVM is gone)&lt;/span&gt;
aws fsx delete-file-system &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="nt"&gt;--file-system-id&lt;/span&gt; &amp;lt;DST_FS_ID&amp;gt;

&lt;span class="c"&gt;# 4. the copied backup, if you no longer need it&lt;/span&gt;
aws fsx delete-backup &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;DST_REGION&amp;gt; &lt;span class="nt"&gt;--backup-id&lt;/span&gt; &amp;lt;DST_BACKUP_ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;SkipFinalBackup&lt;/code&gt; a final backup is created, and &lt;strong&gt;that backup then blocks the next deletion.&lt;/strong&gt; I have been stuck on this before.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;confirm the deletion through the API.&lt;/strong&gt; During this run I passed an unsupported option to &lt;code&gt;delete-file-system&lt;/code&gt; and polled for 8 minutes while the command had already failed. Waiting without reading the response only advances the bill.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measured through AWS Backup as well
&lt;/h2&gt;

&lt;p&gt;Everything above is the &lt;code&gt;CopyBackup&lt;/code&gt; path. &lt;strong&gt;The same route, Tokyo to Osaka, was also measured through AWS Backup.&lt;/strong&gt; That is the path that carries scheduling and cross-account copies, so its behaviour is what matters for production.&lt;/p&gt;

&lt;p&gt;Two ways round. An on-demand backup job followed by an on-demand copy job, and a backup plan with a copy rule left to fire on its schedule. Both used the same source volume (1 GiB, 9,458,747 bytes, 5 files).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Elapsed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;On-demand backup job&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4 m 02 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-demand copy job, Tokyo to Osaka&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8 m 35 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan-triggered backup job&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;30 m 06 s&lt;/strong&gt;, of which about 24 minutes was the start window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy rule copy job (automatic)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6 m 31 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore from the AWS Backup console&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16 m 16 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The copy rule fired without being touched.&lt;/strong&gt; The schedule came round, a job was created, the backup completed, and a copy job followed on its own to put a recovery point in the Osaka backup vault.&lt;/p&gt;

&lt;h3&gt;
  
  
  A plan-triggered job waits in &lt;code&gt;CREATED&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;This changes an operational assumption.&lt;/strong&gt; The on-demand job went to &lt;code&gt;RUNNING&lt;/code&gt; immediately. The plan-triggered one stayed &lt;strong&gt;&lt;code&gt;CREATED&lt;/code&gt; for about 24 minutes.&lt;/strong&gt; When it starts inside the start window — 60 minutes in this configuration — is AWS Backup's decision.&lt;/p&gt;

&lt;p&gt;So a runbook that reads "the scheduled time has passed, therefore the backup exists" is wrong by the length of that window. &lt;strong&gt;Confirm a generation by job state or by the recovery point, not by the clock.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The recovery point in Osaka points at the source
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyopifun6mf1w8fhkjz86.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyopifun6mf1w8fhkjz86.png" alt="The Osaka backup vault. One recovery point, Completed, and the resource ID refers to the source file system and volume" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Resource ID column names the &lt;strong&gt;Tokyo&lt;/strong&gt; file system and volume. It sits in the Osaka vault and points at where it came from.&lt;/p&gt;

&lt;p&gt;Open the recovery point and the copy is visible as such.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fem6xguxrqfprktl1fxzn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fem6xguxrqfprktl1fxzn.png" alt="Recovery point detail. Copy type Manual, Source region Asia Pacific (Tokyo), Storage tier Warm" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source region reads Asia Pacific (Tokyo).&lt;/strong&gt; That is the evidence this artefact came from a cross-Region copy. &lt;code&gt;Copy type&lt;/code&gt; is &lt;code&gt;Manual&lt;/code&gt; because this one was run on demand; the copy-rule one arrives as a separate recovery point.&lt;/p&gt;

&lt;h3&gt;
  
  
  The restore form makes you pick a destination file system and SVM
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0zd3fp3nqjjy3ay4xnoy.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0zd3fp3nqjjy3ay4xnoy.png" alt="The AWS Backup restore form. File system and Storage virtual machine are unselected, and the File system ID at the top is the source file system" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "File system ID" at the top is the source.&lt;/strong&gt; The destination is chosen separately, below. Read the two as the same thing and the restore target looks already decided. It is not.&lt;/p&gt;

&lt;p&gt;Open the dropdown and &lt;strong&gt;only file systems in the destination Region are listed.&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz0yiunow7eccg2nnbh41.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz0yiunow7eccg2nnbh41.png" alt="The File system dropdown. Only the Osaka file system is offered" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With no file system in Osaka this list is empty. &lt;strong&gt;Exactly the same constraint as the &lt;code&gt;CopyBackup&lt;/code&gt; path: not holding a destination day to day is paid for here.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage efficiency is checked by default
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frxxecy4cvtol2s3eo8j4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frxxecy4cvtol2s3eo8j4.png" alt="The filled restore form. Volume size is 1024 MiB and " width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Volume size is pre-filled from the source (1024 MiB).&lt;/strong&gt; The FSx for ONTAP console defaults the same field to 1 TiB (below), so AWS Backup is the more predictable of the two here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Enable storage efficiency" is checked by default.&lt;/strong&gt; The source volume had it off, and the restored volume came back with &lt;code&gt;StorageEfficiencyEnabled: true&lt;/code&gt;. The FSx for ONTAP console restore form &lt;strong&gt;pre-selects the source value&lt;/strong&gt;, so &lt;strong&gt;the two consoles behave differently.&lt;/strong&gt; Uncheck it to match the source.&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;Source&lt;/th&gt;
&lt;th&gt;After the AWS Backup restore&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OntapVolumeType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RW&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RW&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SizeInBytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 GiB&lt;/td&gt;
&lt;td&gt;1 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TieringPolicy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NONE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NONE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StorageEfficiencyEnabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;true&lt;/code&gt;&lt;/strong&gt; (the form's default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SecurityStyle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UNIX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;empty&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;SecurityStyle&lt;/code&gt; coming back empty had been seen once on the &lt;code&gt;CopyBackup&lt;/code&gt; path. &lt;strong&gt;It reproduced through AWS Backup, so it is no longer a single-path observation.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The content matched
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgxjl2zc7g1jbb0ubjth8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgxjl2zc7g1jbb0ubjth8.png" alt="The restore job. Completed, Restore time 16 minutes" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mounted from an EC2 instance in Osaka and compared. &lt;strong&gt;All five sha256 values matched&lt;/strong&gt;, and the symlink target, the &lt;code&gt;0640&lt;/code&gt; mode, the UTF-8 filename, the nested directories and the mtimes were preserved.&lt;/p&gt;

&lt;p&gt;The restored volume carries &lt;code&gt;.snapshot/backup-&amp;lt;id&amp;gt;/&lt;/code&gt;. That &lt;code&gt;&amp;lt;id&amp;gt;&lt;/code&gt; is the &lt;strong&gt;source&lt;/strong&gt; FSx for ONTAP backup, not the Osaka recovery point ID. The destination volume's default snapshot policy starts running too, so &lt;code&gt;hourly.*&lt;/code&gt; snapshots appear beside it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What cannot be monitored, and what catches you on teardown
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;BackupSizeInBytes&lt;/code&gt; was 0&lt;/strong&gt; on both backup jobs. Not a size to alert on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restore progress read &lt;code&gt;0.00%&lt;/code&gt; for the whole 16 minutes, then &lt;code&gt;100%&lt;/code&gt;.&lt;/strong&gt; Anything that estimates remaining time from the percentage will not work.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbl7x59l833d1g0ubrmj9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbl7x59l833d1g0ubrmj9.png" alt="The copy jobs list. Two jobs, one on-demand and one from the copy rule, both Completed" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing caught me on teardown. &lt;strong&gt;A vault cannot be deleted until an &lt;code&gt;EXPIRED&lt;/code&gt; recovery point is fully gone.&lt;/strong&gt; After &lt;code&gt;delete-recovery-point&lt;/code&gt; the entry stayed listed as &lt;code&gt;EXPIRED&lt;/code&gt; for about six minutes, and &lt;code&gt;delete-backup-vault&lt;/code&gt; refused with "contains recovery points" throughout. The underlying FSx for ONTAP backup was still &lt;code&gt;AVAILABLE&lt;/code&gt; at that point. &lt;strong&gt;Teardown automation has to poll; taking the delete request's success as done leaves you stuck.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On the console language&lt;/strong&gt;: the screens in this section are in English. That was the setting at capture time; the other sections show the FSx for ONTAP console in Japanese. Field names are quoted in English either way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-account copies were not measured.&lt;/strong&gt; They require AWS Organizations, so this verification stayed inside one account.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  For the cross-account half — an earlier post, and what this one adds
&lt;/h2&gt;

&lt;p&gt;There is a post on the NetApp Community that goes further on cross-account copies than this one does.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://community.netapp.com/community/discussion/468353/cross-region-and-cross-account-backup-for-amazon-fsx-for-netapp-ontap" rel="noopener noreferrer"&gt;Cross-Region and Cross-Account Backup for Amazon FSx for NetApp ONTAP&lt;/a&gt; (Jordan Roth, NetApp Community)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It walks the cross-account path I did not measure, having actually run it.&lt;/strong&gt; It sets out the protection layers (Snapshot copies, Multi-AZ HA, SnapMirror, AWS Backup copies), four architecture patterns — fan-in into an isolated vault account, fan-out to several, crossing Region and account in one hop, and a clean room for incident work — and the organization, key and IAM prerequisites behind them. &lt;strong&gt;If cross-account is going to production, read that first.&lt;/strong&gt; What follows here is only where it meets my own measurements.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this post was missing — cross-account needs a customer managed key
&lt;/h3&gt;

&lt;p&gt;This is the one that matters. &lt;strong&gt;A cross-account copy requires a customer managed key (CMK).&lt;/strong&gt; The AWS Backup documentation states that for resource types not fully managed by AWS Backup, &lt;strong&gt;cross-account copy with AWS managed keys is not supported&lt;/strong&gt;, because an AWS managed key's key policy is immutable and cannot be shared across accounts (&lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/encryption.html" rel="noopener noreferrer"&gt;Encryption for backups in AWS Backup&lt;/a&gt;). FSx for ONTAP sits on the "not fully managed" side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My verification did not meet that condition.&lt;/strong&gt; The destination vault was created with defaults, so its key was &lt;code&gt;alias/aws/backup&lt;/code&gt; (&lt;code&gt;KeyManager: AWS&lt;/code&gt;). &lt;strong&gt;The cross-Region copy still succeeded.&lt;/strong&gt; That is looser than the documentation's general statement, and it is the reason for measuring rather than assuming.&lt;/p&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;Key used&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;Cross-Region copy (Tokyo to Osaka, one account)&lt;/td&gt;
&lt;td&gt;Defaults on both the source file system and the vaults (&lt;code&gt;aws/fsx&lt;/code&gt; / &lt;code&gt;aws/backup&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Succeeded&lt;/strong&gt; (measured)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-account copy&lt;/td&gt;
&lt;td&gt;AWS managed keys cannot be used&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Not measured.&lt;/strong&gt; Unsupported per the documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;There is a design consequence.&lt;/strong&gt; A file system's KMS key is chosen at creation, and &lt;code&gt;update-file-system&lt;/code&gt; has no parameter for changing it (checked in the AWS CLI). So &lt;strong&gt;if a cross-account copy might ever be needed, the CMK decision happens before the file system exists.&lt;/strong&gt; It is not the kind of thing to switch on later. The destination vault has its own constraint: the default vault cannot be used, because its key cannot be shared.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Region exceptions are listed individually
&lt;/h3&gt;

&lt;p&gt;Earlier I wrote "commercial Regions where both services are available". &lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/backup-feature-availability.html" rel="noopener noreferrer"&gt;AWS Backup feature availability&lt;/a&gt; lists the exceptions one by one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exception&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-Region copy not supported&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Middle East (Bahrain), Middle East (UAE) — for all four of FSx for ONTAP, Lustre, Windows File Server and OpenZFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-Region and cross-account copy not supported&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Asia Pacific (New Zealand), China (Beijing), China (Ningxia) — FSx for ONTAP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The China Regions need reading carefully.&lt;/strong&gt; The AWS Backup document history carries an entry dated the same 27 August 2026 saying cross-account backup copy and cross-account management are now supported in the China Regions. That is about AWS Backup generally; &lt;strong&gt;FSx for ONTAP is excluded there&lt;/strong&gt;, per the table above. Merge the two same-day announcements and you reach the opposite conclusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  On "what was possible before", that post and I read it differently
&lt;/h3&gt;

&lt;p&gt;Its table of changes records &lt;strong&gt;cross-Region &lt;code&gt;CopyBackup&lt;/code&gt; within one account as already available&lt;/strong&gt; before the launch. This post has been saying a backup could not cross a Region boundary before. Rather than assert, here is what I checked.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What I checked&lt;/th&gt;
&lt;th&gt;What it says&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP User Guide, "Protecting your data with volume backups" (&lt;a href="https://web.archive.org/web/20260705094204/https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/using-backups.html" rel="noopener noreferrer"&gt;archived 2026-07-05&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;The sub-page list has &lt;strong&gt;no "Copying backups"&lt;/strong&gt; — only "Copying tags to backups". The body says a backup restores to a file system in the same Region where it is stored&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;copy-backups.html&lt;/code&gt; in the ONTAP User Guide&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;No snapshot at all&lt;/strong&gt; in the Internet Archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The FSx for ONTAP What's New&lt;/td&gt;
&lt;td&gt;"Previously, you could create and restore backups in the same Region and account as your file system"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What I cannot test now is whether the &lt;code&gt;CopyBackup&lt;/code&gt; API would have accepted an ONTAP backup before the launch.&lt;/strong&gt; That API is not specific to FSx for ONTAP, and has supported cross-Region copies for FSx for Windows File Server and FSx for Lustre for years, so reading the API reference alone and concluding it was already possible is a reasonable reading. &lt;strong&gt;Documentation not describing something is not the same as an API refusing it&lt;/strong&gt;, so I am leaving that unverified.&lt;/p&gt;

&lt;p&gt;For a reader today the practical answer is the same either way. &lt;strong&gt;What is unambiguously new is the AWS Backup half&lt;/strong&gt;: cross-Region copy through AWS Backup, and any cross-account copy, from 27 August 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this post adds
&lt;/h3&gt;

&lt;p&gt;The parts that do not overlap.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Here&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Measured durations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Backup, copy and restore on both paths, to the second — including a plan-triggered job waiting about 24 minutes in the start window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Two consoles behaving differently&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The AWS Backup restore form checks storage efficiency by default; the FSx for ONTAP one pre-selects the source value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A single-path observation reproduced&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SecurityStyle&lt;/code&gt; coming back empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What cannot be monitored&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;BackupSizeInBytes&lt;/code&gt; reads 0; restore progress sits at &lt;code&gt;0.00%&lt;/code&gt; for 16 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Where teardown sticks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An &lt;code&gt;EXPIRED&lt;/code&gt; recovery point blocks vault deletion for minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-Region copy on default keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It works with &lt;code&gt;aws/backup&lt;/code&gt; (above)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Replication and copy as terms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Covered in its own section
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Regional DR inside one country&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tokyo to Osaka, with the cost of keeping data in-country&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The two things that caught me out
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. A restoring volume reads as &lt;code&gt;DP&lt;/code&gt;, not &lt;code&gt;RW&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I read the attributes right after the restore reached &lt;code&gt;CREATED&lt;/code&gt; and got &lt;code&gt;OntapVolumeType: DP&lt;/code&gt;. DP volumes are read-only and cannot be backed up. I started writing "a restore produces a read-only volume".&lt;/p&gt;

&lt;p&gt;Then I tried &lt;code&gt;touch&lt;/code&gt; from the client. &lt;strong&gt;It succeeded.&lt;/strong&gt; A re-read returned &lt;code&gt;RW&lt;/code&gt;. To be sure, I ran a second restore passing &lt;code&gt;OntapVolumeType: RW&lt;/code&gt; explicitly — and it &lt;em&gt;also&lt;/em&gt; reported &lt;code&gt;DP&lt;/code&gt; while &lt;code&gt;CREATING&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;State&lt;/th&gt;
&lt;th&gt;&lt;code&gt;OntapVolumeType&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Restoring (&lt;code&gt;CREATING&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore complete (&lt;code&gt;CREATED&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RW&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is transient. &lt;strong&gt;If monitoring or automation reads this field, it sees &lt;code&gt;DP&lt;/code&gt; during a restore.&lt;/strong&gt; Since DP volumes cannot be backed up, a naive "back up as soon as the restore finishes" step behaves differently depending on when it reads. Gate on &lt;code&gt;Lifecycle&lt;/code&gt; reaching &lt;code&gt;CREATED&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;Without the write probe I would have published the wrong claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. FlexGroup backup creation failed asynchronously
&lt;/h3&gt;

&lt;p&gt;The documented limitation is that &lt;strong&gt;copying&lt;/strong&gt; backups of FlexGroup volumes is unsupported. It attaches to the copy and says nothing about creation. I misread it and built the test on the creation side.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CreateBackup&lt;/code&gt; against a FlexGroup volume &lt;strong&gt;was accepted and returned &lt;code&gt;CREATING&lt;/code&gt;&lt;/strong&gt;, then went &lt;code&gt;FAILED&lt;/code&gt; about 30 seconds later. The message names no cause:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Backup failed. Please delete the backup and try again.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a control, a FlexVol RW backup created in the same session with the same credentials succeeded — so this is not permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But this is one observation on one volume.&lt;/strong&gt; I am not claiming FlexGroup backups cannot be created. AWS documentation describes the &lt;em&gt;restore&lt;/em&gt; behaviour of FlexGroup backups (adding constituents when the HA pair count differs), which does not read as creation being unsupported. Separately it states only that a &lt;strong&gt;SnapLock&lt;/strong&gt; FlexGroup volume cannot be backed up. My target was a non-SnapLock FlexGroup, single constituent, one aggregate, 113 GiB provisioned.&lt;/p&gt;

&lt;p&gt;The consequence: &lt;strong&gt;no &lt;code&gt;AVAILABLE&lt;/code&gt; FlexGroup backup was produced, so the documented copy restriction was never exercised.&lt;/strong&gt; If FlexGroup is in scope, verify it &lt;strong&gt;from the creation side&lt;/strong&gt; in your own environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The attribute I called "not carried over" was a CLI default
&lt;/h3&gt;

&lt;p&gt;Seeing &lt;code&gt;StorageEfficiencyEnabled: true&lt;/code&gt; on the restored volume, I wrote that storage efficiency is not carried over by a restore. &lt;strong&gt;That was wrong too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running the same restore through the console showed the field &lt;strong&gt;pre-selected to the source value&lt;/strong&gt; (disabled), and it restored as &lt;code&gt;false&lt;/code&gt;. The initial &lt;code&gt;true&lt;/code&gt; came from omitting the field in the CLI &lt;code&gt;--ontap-configuration&lt;/code&gt;. It is an &lt;strong&gt;API default when omitted&lt;/strong&gt;, not restore behaviour. State it explicitly if you script the restore.&lt;/p&gt;

&lt;p&gt;That is twice in one exercise that I nearly published a behaviour inferred from a single observation. &lt;strong&gt;Running the same operation through a second interface is what exposed both.&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;Attribute&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Restored&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StorageEfficiencyEnabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;true&lt;/code&gt; if omitted in the CLI&lt;/strong&gt;, &lt;code&gt;false&lt;/code&gt; when stated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SecurityStyle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UNIX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;empty in the API response (both CLI and console; UNIX mode bits behaved correctly over NFS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Check storage efficiency after a restore&lt;/strong&gt; — omitting the field enables it even when the source had it off. The empty &lt;code&gt;SecurityStyle&lt;/code&gt; reproduced across both paths, but against the same UNIX-style source; other security styles were not tested.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the console shows that the CLI does not
&lt;/h2&gt;

&lt;p&gt;Four behaviours only visible in the console.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on the screenshots&lt;/strong&gt;: my console is set to Japanese, so that is what you will see. The field&lt;br&gt;
positions and the behaviour are identical in any locale, and each caption names the field in English.&lt;br&gt;
If you would rather follow along in English, switch your own console language under &lt;strong&gt;Unified&lt;br&gt;
Settings → Language&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The copy form defaults to the same Region
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqilytg95olqhefzadlqa.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqilytg95olqhefzadlqa.png" alt="Backups list with the Actions menu open: restore, delete, and copy sit together" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Backups list. Select a backup, then **Actions → Copy backup&lt;/em&gt;* (バックアップをコピー), alongside restore&lt;br&gt;
(バックアップを復元) and delete (バックアップを削除).*&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzb3d1z0s609cxme8thbq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzb3d1z0s609cxme8thbq.png" alt="Copy form. The destination Region field defaults to Asia Pacific (Tokyo), the current Region" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Copy backup form. *&lt;/em&gt;送信先リージョン (destination Region) defaults to the current Region** — leaving it&lt;br&gt;
alone produces an in-Region copy. タグをコピー is "copy tags".*&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcs12z373usq860hfe84k.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcs12z373usq860hfe84k.png" alt="Destination Region dropdown listing commercial Regions" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The destination Region list. Commercial Regions only; the China and GovCloud sets cannot be crossed.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzpoa0q2ufym0t4uky8v.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzpoa0q2ufym0t4uky8v.png" alt="With Osaka selected, the displayed KMS key ID changes to Osaka's default key" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;With Asia Pacific (Osaka) selected, the **KMS キー ID (KMS key ID) changes to that Region's default&lt;br&gt;
key&lt;/em&gt;&lt;em&gt;. This is what "incrementality requires the same KMS key" means in practice: the key in the&lt;br&gt;
destination Region.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  While the copy runs
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3jwh1ejzezh6f3he7edv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3jwh1ejzezh6f3he7edv.png" alt="Destination Backups list showing the copy in progress, labelled as a copied backup" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Destination Region's Backups list. ステータス reads コピー中 ("copying"), and the entry is labelled&lt;br&gt;
*&lt;/em&gt;(コピーされたバックアップ) — "(copied backup)"*&lt;em&gt;. Tag copying carried the name across.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvxws6wrmnvhetenye5ly.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvxws6wrmnvhetenye5ly.png" alt="Detail page of the completed copy, showing source backup ID and source backup Region" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The completed copy. *&lt;/em&gt;ソースバックアップ ID (source backup ID)** and &lt;strong&gt;ソースバックアップリージョン&lt;br&gt;
(source backup Region) = &lt;code&gt;ap-northeast-1&lt;/code&gt;&lt;/strong&gt; are the two fields that establish this is a copy. タグ shows&lt;br&gt;
the copied tags.*&lt;/p&gt;
&lt;h3&gt;
  
  
  Restore needs more than a file system
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F81jlg03oik0z9fgq47b8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F81jlg03oik0z9fgq47b8.png" alt="Restore dialog titled " width="635" height="275"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The dialog is titled *&lt;/em&gt;バックアップからボリュームを作成 — "Create volume from backup"*&lt;em&gt;. The title states&lt;br&gt;
plainly that a restore produces a new volume. ファイルシステム (file system) starts empty.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foeqiho1ye86gp6idmh43.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foeqiho1ye86gp6idmh43.png" alt="The file system dropdown, listing only file systems that exist in the destination Region" width="635" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The ファイルシステム dropdown lists **only file systems that already exist in the destination Region&lt;/em&gt;&lt;em&gt;. If&lt;br&gt;
you have not created one, there is nothing to select — which is where the "create the file system first"&lt;br&gt;
step announces itself.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgfy6e3t94tjc4t2j35sr.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgfy6e3t94tjc4t2j35sr.png" alt="After choosing the file system, the storage virtual machine field appears marked required" width="635" height="1500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;After picking the file system, *&lt;/em&gt;ストレージ仮想マシン (storage virtual machine) appears with a red 必須&lt;br&gt;
("required")** and blocks submission. This is the concrete form of "the destination needs a file system&lt;br&gt;
&lt;strong&gt;and&lt;/strong&gt; an SVM". Note the SnapLock section at the bottom — irreversible, and not a field to set in&lt;br&gt;
passing during a restore.*&lt;/p&gt;
&lt;h3&gt;
  
  
  The volume size default is 1 TiB
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7bht0iahil6eo4nwqi9x.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7bht0iahil6eo4nwqi9x.png" alt="The filled restore form, with the size corrected from the 1 TiB default down to 1 GiB" width="635" height="1500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The same form filled in. *&lt;/em&gt;ボリュームサイズ (volume size) defaults to 1 TiB** even though the source&lt;br&gt;
volume is 1 GiB — I changed the unit to GiB here. ボリューム名, ジャンクションパス, and ストレージ効率&lt;br&gt;
are all pre-filled from the backup; &lt;strong&gt;size is not.&lt;/strong&gt; On a 1,024 GiB destination the default consumes the&lt;br&gt;
entire file system.*&lt;/p&gt;
&lt;h3&gt;
  
  
  The console shows &lt;code&gt;DP&lt;/code&gt; too, and does not refresh
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7g7oaggwl6r9y8ei1xqn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7g7oaggwl6r9y8ei1xqn.png" alt="Volume detail during the restore: lifecycle " width="800" height="794"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;During the restore. *&lt;/em&gt;ライフサイクルの状態 (lifecycle) = 作成 ("creating")** and &lt;strong&gt;ONTAP ボリュームタイプ&lt;br&gt;
(ONTAP volume type) = &lt;code&gt;DP&lt;/code&gt;&lt;/strong&gt; — with Read-Write (RW) selected in the form. ジャンクションパス and&lt;br&gt;
ストレージ効率 are absent, and セキュリティスタイル (security style) is blank.*&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fasum0pzhr4843ghf2at0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fasum0pzhr4843ghf2at0.png" alt="The same page after a reload: created, RW, with the remaining fields populated" width="800" height="476"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The same page after a reload: *&lt;/em&gt;作成済み ("created")** and &lt;code&gt;RW&lt;/code&gt;, with junction path, storage efficiency,&lt;br&gt;
and snapshot policy now shown. &lt;strong&gt;The page does not auto-refresh&lt;/strong&gt; — it kept showing &lt;code&gt;DP&lt;/code&gt; after the API had&lt;br&gt;
already returned &lt;code&gt;RW&lt;/code&gt;, so judging completion by eye alone misses it. セキュリティスタイル stayed blank.*&lt;/p&gt;


&lt;h2&gt;
  
  
  Designing this for production
&lt;/h2&gt;

&lt;p&gt;Everything above was one manual copy. &lt;strong&gt;Six things still need deciding.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Scheduling — &lt;code&gt;CopyBackup&lt;/code&gt; has none
&lt;/h3&gt;

&lt;p&gt;The AWS documentation describes the FSx for ONTAP path as "&lt;strong&gt;manually&lt;/strong&gt; copy volume backups". &lt;strong&gt;It runs from the console, CLI, or API on demand and carries no scheduler.&lt;/strong&gt; Two ways to make it periodic:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Fits when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Backup backup plan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adding a &lt;strong&gt;copy rule&lt;/strong&gt; to a plan's rule writes each backup to a backup vault in the chosen Region, and optionally the chosen account, as it is taken. On-demand copy jobs are available too&lt;/td&gt;
&lt;td&gt;You want policy control, or the same protection applied across an organization. &lt;strong&gt;The only path if the copy has to land in another account&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Your own automation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EventBridge Scheduler → Lambda → &lt;code&gt;create-backup&lt;/code&gt; → wait → &lt;code&gt;copy-backup&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;You want to keep using FSx for ONTAP backups directly and manage generations yourself&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AWS Backup is a managed service that centralizes taking, copying, and retaining backups under policy. FSx for ONTAP joined its cross-Region and cross-account copy targets in August 2026. Applied across an organization it pairs with AWS Organizations, and the account boundary is defined by Organizations policies; the documentation describes fan-in (many production accounts into one isolated account) and fan-out (one production account into several isolated accounts).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cross-Region copy and the restore were measured&lt;/strong&gt; (Measured through AWS Backup as well). The copy rule fired without being touched and the restore went through. A plan-triggered job does wait inside the start window, though — about 24 minutes here — so the scheduled time is not when the generation exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Backup's Region coverage here is "all commercial Regions where both services are available"&lt;/strong&gt;, but the &lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/backup-feature-availability.html" rel="noopener noreferrer"&gt;feature availability page&lt;/a&gt; lists individual exceptions (below). That page also states that when a resource type has no checkmark in the copy columns, copy operations are unsupported in every scenario, including a same-Region same-account copy to a different vault.&lt;/p&gt;

&lt;p&gt;If you build it yourself, &lt;strong&gt;calling &lt;code&gt;copy-backup&lt;/code&gt; before &lt;code&gt;create-backup&lt;/code&gt; completes fails&lt;/strong&gt; — the source must be &lt;code&gt;AVAILABLE&lt;/code&gt;. Poll, or use a Step Functions wait.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whether &lt;code&gt;AUTOMATIC&lt;/code&gt; backups can be copied is unverified.&lt;/strong&gt; The What's New post says "new and existing backups" without qualifying the type. If automatic backups are your evacuation source, prove it with one generation first.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Make recovery declarative — CloudFormation can express the restore
&lt;/h3&gt;

&lt;p&gt;Typing six steps during an incident is not realistic at any team size. This can be declarative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;AWS::FSx::Volume&lt;/code&gt; has a &lt;code&gt;BackupId&lt;/code&gt; property&lt;/strong&gt; ("the ID of the volume backup to use to create a new volume"). So the file system, the SVM, and the restore all fit in one template:&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;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CopiedBackupId&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;String&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ID of the backup in the destination Region&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DrFileSystem&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::FSx::FileSystem&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;FileSystemType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ONTAP&lt;/span&gt;
      &lt;span class="na"&gt;StorageCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;
      &lt;span class="na"&gt;StorageType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SSD&lt;/span&gt;
      &lt;span class="na"&gt;SubnetIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;DrSubnetId&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;SecurityGroupIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="nv"&gt;DrSecurityGroupId&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;OntapConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DeploymentType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SINGLE_AZ_1&lt;/span&gt;
        &lt;span class="na"&gt;ThroughputCapacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;128&lt;/span&gt;
        &lt;span class="na"&gt;PreferredSubnetId&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;DrSubnetId&lt;/span&gt;

  &lt;span class="na"&gt;DrSvm&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::FSx::StorageVirtualMachine&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;FileSystemId&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;DrFileSystem&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dr_svm&lt;/span&gt;
      &lt;span class="na"&gt;RootVolumeSecurityStyle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UNIX&lt;/span&gt;

  &lt;span class="na"&gt;DrRestoredVolume&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::FSx::Volume&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;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dr_restored&lt;/span&gt;
      &lt;span class="na"&gt;VolumeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ONTAP&lt;/span&gt;
      &lt;span class="na"&gt;BackupId&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;CopiedBackupId&lt;/span&gt;
      &lt;span class="na"&gt;OntapConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;StorageVirtualMachineId&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;DrSvm&lt;/span&gt;
        &lt;span class="na"&gt;JunctionPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dr_restored&lt;/span&gt;
        &lt;span class="na"&gt;SizeInBytes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1073741824&lt;/span&gt;
        &lt;span class="na"&gt;OntapVolumeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RW&lt;/span&gt;
        &lt;span class="na"&gt;TieringPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NONE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Keeping this template undeployed in the destination Region is the cold-standby-IaC shape.&lt;/strong&gt; Zero cost while idle, one &lt;code&gt;deploy&lt;/code&gt; at recovery, and no step to forget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I have not deployed this template&lt;/strong&gt; — I ran the same sequence through the CLI. Deploy it once and time it before it becomes your runbook. &lt;strong&gt;An undeployed recovery template is a draft, not a procedure.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. RTO breakdown — "20 minutes" is one line of it
&lt;/h3&gt;

&lt;p&gt;I measured file system creation (20 min) and the restore (13 min). &lt;strong&gt;Your RTO is not the sum of those two.&lt;/strong&gt; Fill this in for your environment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;My measurement&lt;/th&gt;
&lt;th&gt;Yours&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Decision to recover (detection to action)&lt;/td&gt;
&lt;td&gt;not measured&lt;/td&gt;
&lt;td&gt;　&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Destination file system creation&lt;/td&gt;
&lt;td&gt;~20 min&lt;/td&gt;
&lt;td&gt;　&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVM creation&lt;/td&gt;
&lt;td&gt;a few minutes&lt;/td&gt;
&lt;td&gt;　&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AD join (if using SMB)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;not performed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;　&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore to &lt;code&gt;CREATED&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;13 m 21 s (9.4 MiB)&lt;/td&gt;
&lt;td&gt;　&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export policy / share reconfiguration&lt;/td&gt;
&lt;td&gt;not measured&lt;/td&gt;
&lt;td&gt;　&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client remount, DNS cutover&lt;/td&gt;
&lt;td&gt;not measured&lt;/td&gt;
&lt;td&gt;　&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business-side validation&lt;/td&gt;
&lt;td&gt;not measured&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;If you use SMB, the AD join is in there.&lt;/strong&gt; Confirm destination-Region reachability to your domain controllers while nothing is wrong. Discovering that mid-incident is a dead end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restore time gets longer with capacity
&lt;/h3&gt;

&lt;p&gt;The 13 m 21 s and 16 m 16 s I measured were on a 9.4 MiB volume. &lt;strong&gt;Treat those as reference points only.&lt;/strong&gt; Restore time for FSx for ONTAP backup, through either path, scales with capacity.&lt;/p&gt;

&lt;p&gt;AWS publishes the rates (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/using-backups.html" rel="noopener noreferrer"&gt;Backup and restore performance&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;Operation&lt;/th&gt;
&lt;th&gt;Mostly large files&lt;/th&gt;
&lt;th&gt;Mostly small files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backup creation&lt;/td&gt;
&lt;td&gt;750 MBps&lt;/td&gt;
&lt;td&gt;100 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore&lt;/td&gt;
&lt;td&gt;250 MBps&lt;/td&gt;
&lt;td&gt;100 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two conditions come attached to those numbers, and &lt;strong&gt;both push an estimate to the longer side&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They are values "across several concurrent restores" — that is AWS's wording. Nothing says a single restore runs at that rate.&lt;/li&gt;
&lt;li&gt;Backup and restore are background work and use only unused throughput capacity. &lt;strong&gt;Throughput capacity is therefore the ceiling.&lt;/strong&gt; A 128 MBps file system will not reach 250 MBps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Applying both, the calculation is &lt;code&gt;min(published rate, throughput capacity)&lt;/code&gt;. These are floors, on the optimistic assumption that client I/O is zero.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Logical data&lt;/th&gt;
&lt;th&gt;128 MBps / large files&lt;/th&gt;
&lt;th&gt;256 MBps+ / large files&lt;/th&gt;
&lt;th&gt;Mostly small files (any throughput)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 TB&lt;/td&gt;
&lt;td&gt;2 h 10 m&lt;/td&gt;
&lt;td&gt;1 h 6 m&lt;/td&gt;
&lt;td&gt;2 h 46 m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 TB&lt;/td&gt;
&lt;td&gt;10 h 51 m&lt;/td&gt;
&lt;td&gt;5 h 33 m&lt;/td&gt;
&lt;td&gt;13 h 53 m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 TB&lt;/td&gt;
&lt;td&gt;21 h 42 m&lt;/td&gt;
&lt;td&gt;11 h 6 m&lt;/td&gt;
&lt;td&gt;27 h 46 m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 TB&lt;/td&gt;
&lt;td&gt;43 h 24 m&lt;/td&gt;
&lt;td&gt;22 h 13 m&lt;/td&gt;
&lt;td&gt;55 h 33 m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 TB&lt;/td&gt;
&lt;td&gt;108 h 30 m&lt;/td&gt;
&lt;td&gt;55 h 33 m&lt;/td&gt;
&lt;td&gt;138 h 53 m&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Restoring 10 TB on the minimum configuration (128 MBps) is 22 hours at the floor.&lt;/strong&gt; Mostly small files is 28 hours, and that one does not shrink by adding throughput, because the published 100 MBps binds first. At 50 TB it passes four days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I could not find a measured figure at 10 TB or above in any public source.&lt;/strong&gt; I searched AWS blogs, re:Post, the Knowledge Center, and the documentation, and found no article that publishes a measured restore duration with the capacity stated. The table above is calculated from AWS published rates — &lt;strong&gt;it is not something anyone measured&lt;/strong&gt;. If you need a meaningful number for your own environment, the only way is to measure once at production-equivalent capacity and file mix.&lt;/p&gt;

&lt;h3&gt;
  
  
  And this can overturn "generation 1 is enough"
&lt;/h3&gt;

&lt;p&gt;The section below says gen 1 is enough for a DR standby. At scale that reverses.&lt;/p&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;Gen 1&lt;/th&gt;
&lt;th&gt;Gen 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly at minimum throughput&lt;/td&gt;
&lt;td&gt;$115.97 (128 MBps)&lt;/td&gt;
&lt;td&gt;$772.99 (384 MBps)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reads while restoring&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;No.&lt;/strong&gt; You wait for completion&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes&lt;/strong&gt;, once metadata has loaded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to service at 10 TB&lt;/td&gt;
&lt;td&gt;21 h 42 m (floor)&lt;/td&gt;
&lt;td&gt;6–46 min (after 1–7% of metadata loads; the rest loads in the background)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Metadata is documented as 1–7% of the backup data, depending on file size — the smaller the files, the larger the share. At 10 TB, loading just that 1–7% first lets clients mount and start reading. A first access to a block that has not loaded yet costs tens of milliseconds, and it is cached on SSD after that.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;$657 a month of difference can turn a 21-hour outage into something closer to 30 minutes.&lt;/strong&gt; If you hold 10 TB or more and your RTO is constrained in hours, choosing gen 2 for the standby too is a defensible call. Gen 1 is enough when either the capacity is small or the RTO can absorb waiting for the restore to finish.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A few TB or less, or an RTO in days → gen 1&lt;/li&gt;
&lt;li&gt;10 TB or more with an RTO in hours → gen 2's reads-while-restoring earn their keep&lt;/li&gt;
&lt;li&gt;Both are a longer RTO than SnapMirror (neither reaches a shape where the destination volume already exists)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Application consistency — this does not replace a database backup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A backup is a point-in-time copy of a volume.&lt;/strong&gt; It does not quiesce an application.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;What you need&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File servers, shared storage&lt;/td&gt;
&lt;td&gt;A volume point-in-time is usually enough&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databases (Oracle, SQL Server, …)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;A database-level backup, or quiescing before the backup&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anything with constantly open writes&lt;/td&gt;
&lt;td&gt;Application-level consistency checks after restore&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Reading "we copy volume backups to another Region, so DR is covered" for a database workload can end with a volume that restores and a database that will not start.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Concurrency limits at scale
&lt;/h3&gt;

&lt;p&gt;The limits are on &lt;strong&gt;operations in flight&lt;/strong&gt;, not on how much you can keep.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Consequence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Copies in flight (per volume, per destination Region, per KMS key)&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Parallelizing one volume's generations stops at 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copies in flight (per account)&lt;/td&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Firing off hundreds or thousands of volumes at once exceeds it.&lt;/strong&gt; Excess requests are rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backups per volume&lt;/td&gt;
&lt;td&gt;4,091&lt;/td&gt;
&lt;td&gt;Work your generation plan backwards from here&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;In a multi-tenant design with one volume per tenant, tenant count becomes your concurrency.&lt;/strong&gt; Batch the submissions and retry rejections with exponential backoff.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Monitoring and control
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Copies are asynchronous.&lt;/strong&gt; When &lt;code&gt;copy-backup&lt;/code&gt; returns, nothing has finished yet.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decide&lt;/th&gt;
&lt;th&gt;Options&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where failures are detected&lt;/td&gt;
&lt;td&gt;With AWS Backup you can follow job state. For &lt;code&gt;CopyBackup&lt;/code&gt;, polling &lt;code&gt;describe-backups&lt;/code&gt; is the dependable route. &lt;strong&gt;Whether CloudWatch or EventBridge can carry this is unverified&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How success is evidenced&lt;/td&gt;
&lt;td&gt;Store the &lt;code&gt;describe-backups&lt;/code&gt; response (&lt;code&gt;BackupId&lt;/code&gt;, &lt;code&gt;SourceBackupId&lt;/code&gt;, &lt;code&gt;SourceBackupRegion&lt;/code&gt;, &lt;code&gt;CreationTime&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who approves a restore&lt;/td&gt;
&lt;td&gt;A restore creates a new volume and destroys nothing, but it &lt;strong&gt;consumes destination SSD and starts billing&lt;/strong&gt;. Put it in an approval flow where that is expected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which destinations are allowed&lt;/td&gt;
&lt;td&gt;Enforce it in IAM or an SCP, not in a written rule&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One note on tags. &lt;strong&gt;&lt;code&gt;--copy-tags&lt;/code&gt; carries source tags to the destination.&lt;/strong&gt; If your tags name systems or departments, that naming appears in the isolated account too. Depending on why you are isolating, re-tagging may be the better choice.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;The $0.55 I spent was a test bill.&lt;/strong&gt; What is useful in your environment is a monthly figure, so this builds one up from unit rates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit rates — queried from the AWS Price List API
&lt;/h3&gt;

&lt;p&gt;The rates below were queried from the AWS Price List API on 2026-08-29 (effective 2026-07-01). Tokyo and Osaka were identical on all four of the items that appear in both.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Charged item&lt;/th&gt;
&lt;th&gt;Tokyo / Osaka rate&lt;/th&gt;
&lt;th&gt;Nature of the charge&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSD storage (Single-AZ)&lt;/td&gt;
&lt;td&gt;$0.150 / GB-month&lt;/td&gt;
&lt;td&gt;provisioned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSD storage (Multi-AZ)&lt;/td&gt;
&lt;td&gt;$0.300 / GB-month&lt;/td&gt;
&lt;td&gt;provisioned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity pool Standard (Single-AZ)&lt;/td&gt;
&lt;td&gt;$0.0238 / GB-month&lt;/td&gt;
&lt;td&gt;consumed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity pool Standard (Multi-AZ)&lt;/td&gt;
&lt;td&gt;$0.0476 / GB-month&lt;/td&gt;
&lt;td&gt;consumed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup storage&lt;/td&gt;
&lt;td&gt;$0.050 / GB-month&lt;/td&gt;
&lt;td&gt;consumed (incremental)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput capacity (Single-AZ, gen 1)&lt;/td&gt;
&lt;td&gt;$0.906 / MBps-month&lt;/td&gt;
&lt;td&gt;provisioned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput capacity (Single-AZ, gen 2)&lt;/td&gt;
&lt;td&gt;$2.013 / MBps-month&lt;/td&gt;
&lt;td&gt;provisioned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity pool read requests&lt;/td&gt;
&lt;td&gt;$0.00037 / 1,000 requests&lt;/td&gt;
&lt;td&gt;consumed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity pool write requests&lt;/td&gt;
&lt;td&gt;$0.0047 / 1,000 requests&lt;/td&gt;
&lt;td&gt;consumed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Going through AWS Backup does not change the backup storage rate.&lt;/strong&gt; FSx for ONTAP is not a resource type that AWS Backup &lt;em&gt;fully&lt;/em&gt; manages, so the storage charge appears on the FSx for ONTAP side rather than on AWS Backup (&lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/metering-and-billing.html" rel="noopener noreferrer"&gt;Metering, costs, and billing for AWS Backup&lt;/a&gt;). The rate is therefore $0.050 / GB-month on the native path and on the AWS Backup path alike. Only when you use a logically air-gapped vault (LAG vault) does all of the storage and transfer land on the AWS Backup side.&lt;/p&gt;

&lt;h3&gt;
  
  
  FSx for ONTAP backup is not the cheap option
&lt;/h3&gt;

&lt;p&gt;$0.050 / GB-month is 2.1× the Single-AZ capacity pool ($0.0238). Put the other way round, the capacity pool is 47.6% of backup storage — less than half.&lt;/p&gt;

&lt;p&gt;AWS makes the same comparison. AWS Prescriptive Guidance puts fully managed backup at $0.05 / GB-month, against $0.03221 / GB-month for SnapVault aimed at a capacity pool at an SSD-to-pool ratio of 1:10 (&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/optimize-costs-microsoft-workloads/storage-fsx-smb.html" rel="noopener noreferrer"&gt;Choose the right SMB file storage&lt;/a&gt;, US Region example).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That 47.6% is Single-AZ only.&lt;/strong&gt; A Multi-AZ capacity pool is $0.0476, which is 95.2% of backup storage, and the advantage all but disappears. Carrying "the capacity pool is less than half" into a Multi-AZ estimate will put your numbers wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running one continuously has a floor
&lt;/h3&gt;

&lt;p&gt;Read the rate table straight and it seems to say "if the capacity pool is cheap, then keeping a destination file system running is cheap too". It does not work out that way. The minimums on the provisioned charges set the floor for anything kept running.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Minimum SSD capacity&lt;/td&gt;
&lt;td&gt;1,024 GiB per HA pair&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/limits.html" rel="noopener noreferrer"&gt;Quotas&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum throughput capacity (gen 1)&lt;/td&gt;
&lt;td&gt;128 MBps&lt;/td&gt;
&lt;td&gt;same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum throughput capacity (gen 2, 1 HA pair)&lt;/td&gt;
&lt;td&gt;384 MBps&lt;/td&gt;
&lt;td&gt;same&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Monthly cost of a Single-AZ minimum configuration:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;Components&lt;/th&gt;
&lt;th&gt;Monthly at minimum&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gen 1 (128 MBps)&lt;/td&gt;
&lt;td&gt;1,024 GiB × $0.150 + 128 MBps × $0.906&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$269.57&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gen 2 (384 MBps)&lt;/td&gt;
&lt;td&gt;1,024 GiB × $0.150 + 384 MBps × $2.013&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$926.59&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The same "minimum configuration" differs by 3.4× between generations, because gen 2's minimum throughput is 384 MBps and its per-MBps rate is 2.2× gen 1. For backup and a DR standby, gen 1 is the choice (below).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All-tiering does not remove this floor either.&lt;/strong&gt; Metadata always stays on SSD, so the 1,024 GiB SSD minimum remains provisioned.&lt;/p&gt;

&lt;h3&gt;
  
  
  It inverts with scale
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Assumptions&lt;/strong&gt;: 30 daily backup generations, 2% daily change rate, no dedupe or compression benefit assumed, Single-AZ gen 1 at the 128 MBps minimum, the Osaka rates above. The all-SSD column provisions capacity to the recommended 80% SSD utilisation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Logical data&lt;/th&gt;
&lt;th&gt;Backup copies (no destination FS)&lt;/th&gt;
&lt;th&gt;Standing + all-tiering&lt;/th&gt;
&lt;th&gt;Standing + all SSD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 TB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$80&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$293&lt;/td&gt;
&lt;td&gt;$303&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 TB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$160&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$317&lt;/td&gt;
&lt;td&gt;$491&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 TB&lt;/td&gt;
&lt;td&gt;$400&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$389&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1,053&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 TB&lt;/td&gt;
&lt;td&gt;$800&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$508&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1,991&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 TB&lt;/td&gt;
&lt;td&gt;$1,600&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$746&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$3,866&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 TB&lt;/td&gt;
&lt;td&gt;$4,000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,460&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$9,491&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The crossover is around 4.8 TB.&lt;/strong&gt; That is the $269.57 floor divided by the per-GB difference: 1.6 generations of backup at $0.050 ($0.080) against the capacity pool at $0.0238, a gap of $0.0562.&lt;/p&gt;

&lt;p&gt;This is what backs up "we keep one running for RTO and RPO". Past 4.8 TB a standing destination file system is &lt;em&gt;lower&lt;/em&gt; monthly, and a minutes-level RPO, a single-digit-minute RTO, and a failback path come with it. Improving recoverability is not an increase in monthly cost at that size. At 1 TB it is the other way round, and the gap between $80 and $293 has to be explained as a difference in requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a DR standby takes generation 1
&lt;/h3&gt;

&lt;p&gt;For backup and a DR standby, gen 1 is enough. The reason is the throughput increments.&lt;/p&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;Gen 1&lt;/th&gt;
&lt;th&gt;Gen 2 (1 HA pair)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Selectable throughput&lt;/td&gt;
&lt;td&gt;128 / 256 / 512 / 1,024 / 2,048 MBps (Tokyo and Osaka cap at 2,048)&lt;/td&gt;
&lt;td&gt;384 / 768 / 1,536 / 3,072 / 6,144 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly at minimum throughput&lt;/td&gt;
&lt;td&gt;128 MBps × $0.906 = &lt;strong&gt;$115.97&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;384 MBps × $2.013 = &lt;strong&gt;$772.99&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A standby's throughput is sized by SnapMirror transfer volume and the reads right after a failover, not by production load. Gen 2 still floors at 384 MBps for that use, which is 6.7× on the throughput line alone. With gen 1 you start at 128 MBps and move up to 256, then 512, when you need it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment type cannot be changed after creation&lt;/strong&gt; (documented). Going from gen 1 (&lt;code&gt;SINGLE_AZ_1&lt;/code&gt;) to gen 2 (&lt;code&gt;SINGLE_AZ_2&lt;/code&gt;) means a restore from backup, or a migration over SnapMirror or AWS DataSync. Decide the generation before you build the standby.&lt;/p&gt;

&lt;h3&gt;
  
  
  Total monthly by throughput
&lt;/h3&gt;

&lt;p&gt;The 4.8 TB crossover above assumes the 128 MBps minimum. Raise throughput and the crossover moves up with it. Here are the combinations laid out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assumptions&lt;/strong&gt;: Single-AZ gen 1, all-tiering, SSD at the 1,024 GiB minimum, Tokyo / Osaka rates. The backup-copy column is 30 daily generations at a 2% change rate. Capacity pool request charges are not included.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Logical data&lt;/th&gt;
&lt;th&gt;128 MBps&lt;/th&gt;
&lt;th&gt;256 MBps&lt;/th&gt;
&lt;th&gt;512 MBps&lt;/th&gt;
&lt;th&gt;1,024 MBps&lt;/th&gt;
&lt;th&gt;2,048 MBps&lt;/th&gt;
&lt;th&gt;Backup copies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 TB&lt;/td&gt;
&lt;td&gt;$293&lt;/td&gt;
&lt;td&gt;$409&lt;/td&gt;
&lt;td&gt;$641&lt;/td&gt;
&lt;td&gt;$1,105&lt;/td&gt;
&lt;td&gt;$2,033&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$80&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 TB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$389&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$505&lt;/td&gt;
&lt;td&gt;$736&lt;/td&gt;
&lt;td&gt;$1,200&lt;/td&gt;
&lt;td&gt;$2,128&lt;/td&gt;
&lt;td&gt;$400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 TB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$508&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$624&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$855&lt;/td&gt;
&lt;td&gt;$1,319&lt;/td&gt;
&lt;td&gt;$2,247&lt;/td&gt;
&lt;td&gt;$800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 TB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$746&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$862&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,093&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,557&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$2,485&lt;/td&gt;
&lt;td&gt;$1,600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 TB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,460&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,576&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,807&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$2,271&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$3,199&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bold is cheaper than holding backup copies. Restated as the highest throughput that still comes in under the backup-copy figure:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Logical data&lt;/th&gt;
&lt;th&gt;Backup copies / month&lt;/th&gt;
&lt;th&gt;Highest throughput under that&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1–2 TB&lt;/td&gt;
&lt;td&gt;$80–$160&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; Even 128 MBps standing costs more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 TB&lt;/td&gt;
&lt;td&gt;$400&lt;/td&gt;
&lt;td&gt;128 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 TB&lt;/td&gt;
&lt;td&gt;$800&lt;/td&gt;
&lt;td&gt;256 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 TB&lt;/td&gt;
&lt;td&gt;$1,600&lt;/td&gt;
&lt;td&gt;1,024 MBps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50 TB&lt;/td&gt;
&lt;td&gt;$4,000&lt;/td&gt;
&lt;td&gt;2,048 MBps (the gen 1 ceiling in Tokyo and Osaka)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At 50 TB you can provision the largest throughput gen 1 offers in-Region and still come in cheaper than holding backup copies. At 1–2 TB backup copies are cheaper at every throughput, and there the difference is a requirements conversation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the "zero while idle" of backup copies buys
&lt;/h3&gt;

&lt;p&gt;With no destination file system in normal operation, there is no throughput capacity charge and no SSD capacity charge. That is a clear advantage on the monthly figure. The work it defers, though, reappears at the moment of failover, as urgent work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the unit of a backup is the volume, so a restore always produces a new volume.&lt;/strong&gt; There is no path that overwrites an existing volume in place, on the &lt;code&gt;CopyBackup&lt;/code&gt; route or the AWS Backup route. That is what makes steps 4 to 6 below unavoidable rather than optional: a new volume means a new junction path if the original still exists, and export policies, SMB shares, quotas, and the snapshot policy do not travel with it. The volume size default is a separate trap — it comes up as 1 TiB rather than the source's size, covered further up.&lt;/p&gt;

&lt;p&gt;In an incident that same constraint cuts the other way and becomes an advantage: you can preserve a compromised volume exactly as it stands and restore beside it, so the original is still there to investigate.&lt;/p&gt;

&lt;p&gt;Here is what a cold start to restored service actually needed, in the range I measured.&lt;/p&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;Step&lt;/th&gt;
&lt;th&gt;Measured / state&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;Create the file system in the destination Region&lt;/td&gt;
&lt;td&gt;20–22 min (measured, &lt;code&gt;SINGLE_AZ_1&lt;/code&gt;, 128 MBps)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Create the SVM&lt;/td&gt;
&lt;td&gt;a few minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Restore the volume from backup&lt;/td&gt;
&lt;td&gt;13 m 21 s (&lt;code&gt;CopyBackup&lt;/code&gt; path) / 16 m 16 s (AWS Backup path), both at 9 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Recreate export policies and SMB shares&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Only the volume is restored.&lt;/strong&gt; Share configuration does not come with it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;AD-join the SVM if you use SMB&lt;/td&gt;
&lt;td&gt;It is a new SVM, so it joins again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Repoint clients&lt;/td&gt;
&lt;td&gt;The new file system has a different DNS name and different IPs. Either move a CNAME or remount on the client&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Steps 1 and 3 finish if you wait. Steps 4 to 6 are a person deciding and typing. And they are needed mid-incident, not on a quiet afternoon. What you saved monthly is traded against doing that work under pressure.&lt;/p&gt;

&lt;p&gt;In a SnapMirror shape, 1 to 3 do not exist, and the destination SVM in 5 is already joined. What is left is 6 and the break-and-promote, so the number of steps is not comparable to begin with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps 4 to 6 can be automated ahead of time.&lt;/strong&gt; Declare the restore in CloudFormation, script &lt;code&gt;vserver cifs create&lt;/code&gt; and the share creation, put a CNAME in front of DNS. But "can be" and "confirmed to work during an incident" are different claims. Run it through once while nothing is wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  The price difference is the requirement difference
&lt;/h3&gt;

&lt;p&gt;Here is what sits inside the $80 and the $293 at 1 TB.&lt;/p&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;Backup copies&lt;/th&gt;
&lt;th&gt;Standing (SnapMirror shape)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RPO&lt;/td&gt;
&lt;td&gt;60 min (the automatic backup minimum)&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTO&lt;/td&gt;
&lt;td&gt;file system creation 20–22 min + restore 13–16 min (measured)&lt;/td&gt;
&lt;td&gt;promote the volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failback&lt;/td&gt;
&lt;td&gt;no path&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Destination file system in normal operation&lt;/td&gt;
&lt;td&gt;not needed&lt;/td&gt;
&lt;td&gt;needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;If the requirement is a minute-level RPO, backup copies are not a candidate.&lt;/strong&gt; And at recovery time the backup-copy side creates a destination file system too, so the same floor starts applying from then on. The accurate reading is a design whose cost structure differs between normal operation and recovery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not included above
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Capacity pool request charges&lt;/strong&gt;: not in the tables above. The rates are known, but I have not measured the conversion from SnapMirror transferred bytes to request counts, so I cannot produce a number. With all-tiering every write goes to the capacity pool, so write requests ($0.0047 / 1,000) may not be negligible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inter-Region data transfer&lt;/strong&gt;: treated differently depending on the path. Split out into the next section&lt;/li&gt;
&lt;li&gt;Source-side backup storage (unchanged — you only added somewhere to put a copy)&lt;/li&gt;
&lt;li&gt;KMS request charges if you use a CMK&lt;/li&gt;
&lt;li&gt;SnapMirror inter-Region transfer (SnapMirror is not measured in this post)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Inter-Region data transfer is treated differently per path
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AWS Backup copies have a transfer line item.&lt;/strong&gt; Tokyo to Osaka is $0.09 / GB (Price List API, &lt;code&gt;AWSBackup&lt;/code&gt;, &lt;code&gt;APN1-APN3-CrossRegion-WarmBytes-FSx&lt;/code&gt;, retrieved 2026-08-29). The AWS Backup pricing page gives the same transfer rate for Resource Group 3, which includes Amazon FSx, on a standard vault and on a logically air-gapped vault alike.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For native &lt;code&gt;CopyBackup&lt;/code&gt;, the reasonable thing is to estimate as though it applies.&lt;/strong&gt; I started to write "probably does not apply", checked again, and withdrew it. Here is why.&lt;/p&gt;

&lt;p&gt;The argument I withdrew: backups sit in AWS-managed S3 and never traverse the customer VPC, so they are not subject to inter-Region transfer charges the way EC2 is. &lt;strong&gt;That does not hold.&lt;/strong&gt; EBS snapshots are equally in an AWS-managed area and equally do not traverse a VPC, and a cross-Region snapshot copy does incur AWS Data Transfer charges. The AWS Storage Blog walks through tracking that charge in Cost Explorer, and the usage type that appears there is &lt;code&gt;USW2-USE1-AWS-Out-Byte&lt;/code&gt;, the service is "EC2 - Other", and the usage type group is Data Transfer - Region to Region (Out) (&lt;a href="https://aws.amazon.com/blogs/storage/effectively-track-aws-data-transfer-costs-for-cross-region-amazon-ebs-snapshot-copy/" rel="noopener noreferrer"&gt;Effectively track AWS data transfer costs for cross-region Amazon EBS Snapshot Copy&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That also exposes a hole in how I first checked it. I based it on "there is no transfer item in the FSx for ONTAP price list", but inter-Region transfer is listed under &lt;code&gt;AWSDataTransfer&lt;/code&gt;, not under the originating service. EBS is exactly that case: there is no "snapshot transfer" item in the EBS price list, and the charge happens anyway.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;AWSDataTransfer&lt;/code&gt; has a generic, service-agnostic inter-Region SKU.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Usage type&lt;/th&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tokyo → Osaka (out)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;APN1-APN3-AWS-Out-Bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$0.09 / GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Osaka → Tokyo (in)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;APN1-APN3-AWS-In-Bytes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$0.00 / GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(Price List API, &lt;code&gt;AWSDataTransfer&lt;/code&gt;, effective 2026-06-01, retrieved 2026-08-29)&lt;/p&gt;

&lt;p&gt;The Amazon FSx SKU on the AWS Backup side is also $0.09 / GB. The same transfer rate appearing in both places makes it natural to read them as the same underlying charge.&lt;/p&gt;

&lt;p&gt;To summarise, this is as far as it can be taken right now.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS Backup cross-Region copies incur transfer&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Listed in the price list&lt;/strong&gt; ($0.09 / GB, Tokyo → Osaka)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native &lt;code&gt;CopyBackup&lt;/code&gt; incurs transfer&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Estimate as though it does.&lt;/strong&gt; A generic $0.09 / GB SKU exists, and the same shape (EBS snapshot copy) is charged in practice. But I have not confirmed it on a bill — &lt;strong&gt;unverified&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"It is not in the FSx for ONTAP price list, so it is free"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;This inference is wrong.&lt;/strong&gt; Transfer is listed under &lt;code&gt;AWSDataTransfer&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In an estimate, put in the first full copy's capacity × $0.09 / GB. After that it is incremental, so changed volume × $0.09 / GB. A 10 TB first full is $900 — not a monthly figure, but a one-off of real size. The shortest way to settle it is to copy one small volume and read the &lt;code&gt;UsageType&lt;/code&gt; in Cost Explorer. I have not done that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On which account is billed for transfer, two AWS statements disagree.&lt;/strong&gt; FSx for ONTAP is not a resource type AWS Backup fully manages, so that discrepancy lands directly on this case.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Statement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/metering-and-billing.html" rel="noopener noreferrer"&gt;AWS Backup Developer Guide&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;For resource types that are not fully managed, transfer appears on the destination account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/backup/pricing/" rel="noopener noreferrer"&gt;AWS Backup pricing page&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Transfer is charged to the account sending the data (the copy source)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Which one is correct I have not been able to confirm. If cost attribution has to go into the design of a cross-account setup, copy once first and check which account it lands on in the actual bill.&lt;/p&gt;

&lt;h3&gt;
  
  
  The trade-off of all-tiering
&lt;/h3&gt;

&lt;p&gt;Keeping a standby cheap with all-tiering means reads after a failover start from the capacity pool. All does not pull read blocks back onto SSD, so performance immediately after a cutover is not the same as production sized for SSD. It is a choice between the monthly cost while idle and the performance immediately after cutover.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prioritise cost while idle → all&lt;/li&gt;
&lt;li&gt;Need production-equivalent performance from the moment of cutover → provision SSD as in production (the "all SSD" column above)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When you build your own estimate, replace the change rate and the generation count with measurements from your environment.&lt;/strong&gt; The 2% is an assumption, not something I measured. Rates are revised, too. The figures above were retrieved on 2026-08-29 and are effective 2026-07-01, so check the current values on the &lt;a href="https://aws.amazon.com/fsx/netapp-ontap/pricing/" rel="noopener noreferrer"&gt;FSx for ONTAP pricing page&lt;/a&gt; and the &lt;a href="https://aws.amazon.com/backup/pricing/" rel="noopener noreferrer"&gt;AWS Backup pricing page&lt;/a&gt; before you decide.&lt;/p&gt;




&lt;h2&gt;
  
  
  As a ransomware layer
&lt;/h2&gt;

&lt;p&gt;An isolated copy is a useful layer, but it &lt;strong&gt;lacks two properties people expect of it.&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;Expectation&lt;/th&gt;
&lt;th&gt;Reality&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Put it in an isolated account and it cannot be deleted&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Copies can be deleted.&lt;/strong&gt; The isolation holds only if the destination account's IAM or vault policy prevents deletion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A backup copy satisfies an immutability requirement&lt;/td&gt;
&lt;td&gt;It does not. Preventing deletion during a retention period is the domain of WORM features (Object Lock, SnapLock, Tamperproof Snapshot, Vault Lock)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;WORM features are irreversible.&lt;/strong&gt; Once enabled, nothing is deletable until retention expires, and the blast radius is routinely wider than the resource you named. Get the retention value and the blast radius approved separately before enabling one. I made a file system undeletable for six months this way.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;strong&gt;a restore always producing a new volume is an advantage during an incident.&lt;/strong&gt; You can preserve the compromised volume for investigation and restore into a separate volume to resume operations, without overwriting the original.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common errors and how to isolate them
&lt;/h2&gt;

&lt;p&gt;Decide &lt;strong&gt;which layer stopped&lt;/strong&gt; before you start debugging. Get the layer wrong and you spend the incident suspecting something that works.&lt;/p&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;Look at&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Source&lt;/td&gt;
&lt;td&gt;The backup&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Lifecycle&lt;/code&gt; is &lt;code&gt;AVAILABLE&lt;/code&gt;; volume type is &lt;code&gt;RW&lt;/code&gt;; style is FlexVol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Permissions&lt;/td&gt;
&lt;td&gt;IAM / KMS&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fsx:CopyBackup&lt;/code&gt; present; source-Region backup reachable; destination key permissions for a CMK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Copy&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;describe-backups&lt;/code&gt; at the destination&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPYING&lt;/code&gt;, &lt;code&gt;AVAILABLE&lt;/code&gt;, or &lt;code&gt;FAILED&lt;/code&gt;; concurrency limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Restore&lt;/td&gt;
&lt;td&gt;The destination volume&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Lifecycle&lt;/code&gt;; enough SSD (it pauses when short)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Access&lt;/td&gt;
&lt;td&gt;The client&lt;/td&gt;
&lt;td&gt;Export policy, security group, SVM NFS / SMB endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Messages I actually saw:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Message&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Backup &amp;lt;id&amp;gt; is being copied, can't be deleted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Wait for the copy. &lt;strong&gt;There is a short delay even after it completes&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Backup failed. Please delete the backup and try again.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Names no cause. Suspect FlexGroup and take a FlexVol control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Volume with type DP is not backupable.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;That is a SnapMirror destination. &lt;strong&gt;Back up at the source instead&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rejection from exceeding copies in flight&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5 per volume per destination Region per KMS key, 1,000 per account. Wait and retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Unknown options: --ontap-configuration&lt;/code&gt; (&lt;code&gt;delete-file-system&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;A CLI version difference. &lt;strong&gt;Read the response.&lt;/strong&gt; This cost me 8 minutes of polling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Whether a failed copy leaves a partial backup at the destination is unverified&lt;/strong&gt; — I did not induce a failure deliberately. If you automate this, detect &lt;code&gt;FAILED&lt;/code&gt; and delete it (the backup created by my FlexGroup failure did remain as &lt;code&gt;FAILED&lt;/code&gt;, and it deleted cleanly).&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this sits alongside an existing backup product or SaaS
&lt;/h2&gt;

&lt;p&gt;If you already run a data protection product or backup SaaS, this update is not a migration question. It is a question of &lt;strong&gt;which layer you give to what.&lt;/strong&gt; There are three paths, and combining them is normal.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Managed by&lt;/th&gt;
&lt;th&gt;Another Region&lt;/th&gt;
&lt;th&gt;Another account&lt;/th&gt;
&lt;th&gt;Fits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A. FSx for ONTAP backups&lt;/td&gt;
&lt;td&gt;FSx for ONTAP API / console&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;yes&lt;/strong&gt; (new)&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;Volume-level evacuation with the fewest moving parts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B. AWS Backup&lt;/td&gt;
&lt;td&gt;Backup plans&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;yes&lt;/strong&gt; (needs Organizations)&lt;/td&gt;
&lt;td&gt;Policy control, one view across services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C. Existing data protection product / SaaS&lt;/td&gt;
&lt;td&gt;That product's policy&lt;/td&gt;
&lt;td&gt;Depends on the product&lt;/td&gt;
&lt;td&gt;Depends on the product&lt;/td&gt;
&lt;td&gt;File- or application-level restore, catalog search, fitting existing workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  A concrete way to split them
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;File-level restores from the existing product; whole-volume evacuation from FSx for ONTAP backup copies.&lt;/strong&gt; One split that often works, and the reason is restore granularity. A restore from an FSx for ONTAP backup &lt;strong&gt;produces one new volume&lt;/strong&gt;, which is a lot of machinery to return a single file a user deleted. Conversely, pushing whole volumes to another Region through an existing product tends to grow the transfer volume and the catalog. Where your product already handles cross-Region evacuation efficiently, the opposite split is equally reasonable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The decisive question for AWS Backup is whether you need another account.&lt;/strong&gt; Evacuating to an isolated account is out of reach for the FSx for ONTAP API alone; it needs AWS Backup and AWS Organizations. If you only need another Region, the FSx for ONTAP API is fewer moving parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to ask, regardless of product
&lt;/h3&gt;

&lt;p&gt;Ask along these axes rather than by product name:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Axis&lt;/th&gt;
&lt;th&gt;What to establish&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unit of protection&lt;/td&gt;
&lt;td&gt;Whole volume, file / directory, or application-consistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore granularity&lt;/td&gt;
&lt;td&gt;Can it return one file, or only a whole volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-Region / cross-account path&lt;/td&gt;
&lt;td&gt;The product's own feature, via AWS Backup, or your own copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What the restore target requires&lt;/td&gt;
&lt;td&gt;Does a file system need to exist first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where retention policy lives&lt;/td&gt;
&lt;td&gt;The product or AWS. &lt;strong&gt;Is it managed in two places&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring and alerting&lt;/td&gt;
&lt;td&gt;Where failures surface, and whether that reaches your existing observability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where the cost lands&lt;/td&gt;
&lt;td&gt;Product licensing, AWS usage, or both&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;"Managed in two places" is where the accidents are.&lt;/strong&gt; If FSx for ONTAP automatic backups and the product's policy each hold generations independently, changing retention on one side leaves the other holding an unintended number. The ceiling is 4,091 backups per volume, so plan backwards from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage is also billed twice.&lt;/strong&gt; Holding the same data in both the product's repository and FSx for ONTAP backups charges for both sets of generations. If you combine them, pick a primary and &lt;strong&gt;make the generation counts differ&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;Shape&lt;/th&gt;
&lt;th&gt;Primary&lt;/th&gt;
&lt;th&gt;Secondary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File-level restore matters most&lt;/td&gt;
&lt;td&gt;Existing product, long retention, many generations&lt;/td&gt;
&lt;td&gt;FSx for ONTAP backups short and few (recent whole-volume evacuation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-Region evacuation matters most&lt;/td&gt;
&lt;td&gt;FSx for ONTAP backup copies, many generations&lt;/td&gt;
&lt;td&gt;Existing product limited to in-Region file restores&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither is correct in the abstract. It comes down to &lt;strong&gt;which granularity 90% of your restore requests arrive in.&lt;/strong&gt; Counting your past restore tickets is the most reliable way to find out.&lt;/p&gt;

&lt;h3&gt;
  
  
  About monitoring
&lt;/h3&gt;

&lt;p&gt;My verification polled &lt;code&gt;describe-backups&lt;/code&gt;. &lt;strong&gt;I have not verified a path for detecting copy completion through CloudWatch metrics or EventBridge events.&lt;/strong&gt; If you plan to wire this into an existing observability stack, that is a gap you will need to close yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Choosing between this and SnapMirror
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Neither is better.&lt;/strong&gt; The AWS documentation states RPO and RTO figures for both, and they protect different things.&lt;/p&gt;

&lt;p&gt;For context: cross-Region disaster recovery for FSx for ONTAP has been buildable with SnapMirror for years — a destination file system, cluster peering, asynchronous replication. Plenty of production environments run exactly that, and backup copies do not displace it. What got added is the option of not holding the destination while nothing is wrong.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Backup copies&lt;/th&gt;
&lt;th&gt;SnapMirror&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Suits&lt;/td&gt;
&lt;td&gt;Offline copies for compliance or isolation&lt;/td&gt;
&lt;td&gt;Availability in a second Region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RPO guidance&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;60 minutes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;as low as &lt;strong&gt;5 minutes&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTO guidance&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;minutes to hours&lt;/strong&gt; (size dependent)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;single-digit minutes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needed while idle&lt;/td&gt;
&lt;td&gt;No destination file system&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;A running destination file system&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shape of recovery&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Restore as a new volume.&lt;/strong&gt; The original does not come back&lt;/td&gt;
&lt;td&gt;Break the relationship and promote the destination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path back to production&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;None.&lt;/strong&gt; Continue on the recovery side, or copy back in the other direction&lt;/td&gt;
&lt;td&gt;Delete the relationship and &lt;code&gt;snapmirror resync&lt;/code&gt; from the DR side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trade-off&lt;/td&gt;
&lt;td&gt;Destination file system creation lands on RTO (20 min measured); RPO is as coarse as the backup interval. &lt;strong&gt;The unit of backup and restore is the volume, and with AWS Backup / FSx backups a restore always lands in a newly created volume&lt;/strong&gt; — there is no restore-in-place path, so junction paths, share configuration and where clients point all have to be rebuilt&lt;/td&gt;
&lt;td&gt;You pay for destination capacity and throughput continuously; needs cluster peering and &lt;strong&gt;does not work through NAT&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In operation, the asymmetry in "path back to production" is what bites. SnapMirror can be re-established in reverse to fail back. Backup copies only restore, and what you get is a new volume. If a documented failback procedure is a requirement, backup copies do not substitute for it.&lt;/p&gt;

&lt;p&gt;One caveat on &lt;code&gt;snapmirror resync&lt;/code&gt;: &lt;strong&gt;user-created snapshots are not replicated by a resync.&lt;/strong&gt; The &lt;code&gt;preserve&lt;/code&gt; parameter is available on XDP relationships.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running both
&lt;/h3&gt;

&lt;p&gt;This is a natural combination: &lt;strong&gt;SnapMirror for availability, backup copies for isolated retention.&lt;/strong&gt; One catch — &lt;strong&gt;a SnapMirror destination is a &lt;code&gt;DP&lt;/code&gt; volume and cannot be backed up&lt;/strong&gt;, so "back up the replica and evacuate that" does not work. &lt;strong&gt;Take backups at the source and copy those.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Added (September 2026)&lt;/strong&gt;: read-only does not mean inert. Verified on ONTAP 9.18.1P5 in ap-northeast-1: a SnapMirror destination can be served read-only through an FSx for ONTAP S3 Access Point &lt;strong&gt;while replication is still running&lt;/strong&gt; — no break, no clone. What gates the attachment is the junction path, not the volume type, and setting that junction path is an ONTAP-side operation: the Amazon FSx API refuses it on a &lt;code&gt;DP&lt;/code&gt; volume, and &lt;code&gt;UpdateVolume&lt;/code&gt; refuses it silently, returning HTTP 200 with the volume unchanged. After a transfer, new data was readable through the same access point in 15 seconds. Budget minutes to tens of minutes for the FSx control plane to report a newly mounted volume as attachable. &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations/blob/main/docs/en/s3ap-flexcache-snapmirror-considerations.md#32-s3-ap-attachment-at-the-destination" rel="noopener noreferrer"&gt;Design considerations&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Regional DR within one country
&lt;/h2&gt;

&lt;p&gt;Copying to Osaka keeps data inside Japan while placing it in a second Region.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Consideration&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where the data sits&lt;/td&gt;
&lt;td&gt;Both Tokyo and Osaka are in-country&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle cost&lt;/td&gt;
&lt;td&gt;Backup storage only ($0.050/GB-month in Osaka, rate effective 2026-07-01)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needed at recovery&lt;/td&gt;
&lt;td&gt;A file system and SVM in Osaka. &lt;strong&gt;Creation time lands on RTO&lt;/strong&gt; (20 min measured)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generation availability&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SINGLE_AZ_1&lt;/code&gt; was creatable in Osaka (measured 2026-08-28). &lt;strong&gt;This differs by Region&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trade-off&lt;/td&gt;
&lt;td&gt;Coarser RPO and RTO than SnapMirror. A minute-level RPO requirement means keeping the destination running&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;How you satisfy a data residency requirement is a design question, &lt;strong&gt;not a legal or regulatory determination.&lt;/strong&gt; Interpreting regulatory requirements belongs to your compliance function and the relevant authority. This post does not substitute for that.&lt;/p&gt;




&lt;h2&gt;
  
  
  A staged rollout
&lt;/h2&gt;

&lt;p&gt;Do not start on a production volume. The order I would suggest:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the whole path on a small test volume (~1 GiB)&lt;/strong&gt; — copy, destination file system, restore, content check, teardown. This is where the holes in your procedure show up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time it including teardown.&lt;/strong&gt; Destination file system creation lands on RTO, so this is the number you most want&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure once at production data volume.&lt;/strong&gt; My 9.4 MiB figures are dominated by fixed overhead and are not evidence for your environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check whether the second copy is incremental&lt;/strong&gt; by comparing transfer volume and duration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run periodic copies on a non-production volume and build the failure-detection path.&lt;/strong&gt; This is where the real work is&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go to production, keeping the existing backup path running in parallel&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put restore drills into operations.&lt;/strong&gt; Without them your RTO is an estimate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Do not go to production without step 5.&lt;/strong&gt; Copies are asynchronous, and when one fails your command has already returned success.&lt;/p&gt;

&lt;h3&gt;
  
  
  A verification plan template
&lt;/h3&gt;

&lt;p&gt;Useful to have on one page if a team is involved:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;What goes in it&lt;/th&gt;
&lt;th&gt;Mine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Question being answered&lt;/td&gt;
&lt;td&gt;"Does cross-Region evacuation work without a standing destination?"&lt;/td&gt;
&lt;td&gt;same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Success criteria&lt;/td&gt;
&lt;td&gt;Content matches after restore, and every line of the RTO breakdown is filled&lt;/td&gt;
&lt;td&gt;Hash match achieved. RTO breakdown incomplete (AD and remount unmeasured)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time required&lt;/td&gt;
&lt;td&gt;Half a day for the small path, a day at production volume&lt;/td&gt;
&lt;td&gt;~1.5 hours including teardown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost ceiling&lt;/td&gt;
&lt;td&gt;Driven by destination file system runtime. Set it first&lt;/td&gt;
&lt;td&gt;Capped at 3 hours / $2; actual $0.55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stakeholders&lt;/td&gt;
&lt;td&gt;Storage operations, the business (accepting the restored data), audit / control, networking (destination VPC, AD reachability)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go criteria&lt;/td&gt;
&lt;td&gt;Measured RTO under target / incrementality confirmed / failure detection working&lt;/td&gt;
&lt;td&gt;Not met (incrementality and detection unverified)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No-Go criteria&lt;/td&gt;
&lt;td&gt;RPO needed in minutes / failback required / FlexGroup in scope&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;By that standard I cannot call my own run a Go.&lt;/strong&gt; Incrementality and failure detection are unverified, and the RTO breakdown does not include AD. What this post establishes is that the path works.&lt;/p&gt;

&lt;h3&gt;
  
  
  A recovery runbook skeleton
&lt;/h3&gt;

&lt;p&gt;Naming the decision points keeps the day itself calm:&lt;/p&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;Decision or action&lt;/th&gt;
&lt;th&gt;Branch&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;Is recovery needed (Region-level failure, or data loss)?&lt;/td&gt;
&lt;td&gt;Data loss alone is &lt;strong&gt;an in-Region restore&lt;/strong&gt;. You do not need the other Region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Which generation?&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;CreationTime&lt;/code&gt; on the copies. &lt;strong&gt;This is also where you see whether copying kept up&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Create the destination file system&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deploy&lt;/code&gt; the template, or steps 4–5 by hand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;SMB in play?&lt;/td&gt;
&lt;td&gt;Then AD join is required. &lt;strong&gt;Put the reachability check here&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Restore&lt;/td&gt;
&lt;td&gt;Wait for &lt;code&gt;Lifecycle: CREATED&lt;/code&gt;. &lt;strong&gt;&lt;code&gt;OntapVolumeType&lt;/code&gt; reads &lt;code&gt;DP&lt;/code&gt; while restoring&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Business validation&lt;/td&gt;
&lt;td&gt;Data match and application startup. &lt;strong&gt;Databases need a consistency check&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Return to the original Region?&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Backup copies have no failback path.&lt;/strong&gt; Either continue on the recovery side, or copy back in the other direction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Try it now
&lt;/h2&gt;

&lt;p&gt;The same steps with only the values to substitute:&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;# Change these five lines for your environment&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SRC_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-northeast-1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DST_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-northeast-3
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SRC_VOLUME_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fsvol-0123456789abcdef0
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SRC_BACKUP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;backup-0123456789abcdef0
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DST_BACKUP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;   &lt;span class="c"&gt;# fill in after the copy&lt;/span&gt;

&lt;span class="c"&gt;# 1) create a backup&lt;/span&gt;
aws fsx create-backup &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--volume-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_VOLUME_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backup.BackupId'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text

&lt;span class="c"&gt;# 2) wait for AVAILABLE&lt;/span&gt;
aws fsx describe-backups &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--backup-ids&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backups[0].Lifecycle'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text

&lt;span class="c"&gt;# 3) copy to another Region (--region is the destination, --source-region is the origin)&lt;/span&gt;
aws fsx copy-backup &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DST_REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-backup-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--source-region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--copy-tags&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backup.BackupId'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text

&lt;span class="c"&gt;# 4) confirm the copy&lt;/span&gt;
aws fsx describe-backups &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DST_REGION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--backup-ids&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DST_BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Backups[0].{Life:Lifecycle,Src:SourceBackupId,SrcRegion:SourceBackupRegion}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If step 4 shows &lt;code&gt;SourceBackupId&lt;/code&gt; and &lt;code&gt;SourceBackupRegion&lt;/code&gt;, the copy is real.&lt;/strong&gt; That is the whole day-to-day preparation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;What this update removed is &lt;strong&gt;what you keep running day to day.&lt;/strong&gt; Putting data in a second Region no longer requires a file system there.&lt;/p&gt;

&lt;p&gt;You pay for that at recovery time instead — file system creation (20 minutes measured) plus an RPO as coarse as your backup interval. &lt;strong&gt;If minute-level RPO and a failback procedure are requirements, this does not replace SnapMirror.&lt;/strong&gt; If isolated retention is the requirement, you now have an option that needs nothing standing by.&lt;/p&gt;

&lt;p&gt;Three things I plan to do next:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Measure cross-account copies.&lt;/strong&gt; The AWS Backup cross-Region copy and restore are measured now; cross-account needs AWS Organizations and remains documentation only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure incrementality at production data volume.&lt;/strong&gt; At 9.4 MiB it disappears into fixed overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Establish a failure-detection path.&lt;/strong&gt; Whether CloudWatch or EventBridge can carry copy state is unverified&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most useful thing I learned was smaller than the feature: &lt;strong&gt;I believed &lt;code&gt;DP&lt;/code&gt; was a permanent property until I tried writing to the volume&lt;/strong&gt;, and I believed storage efficiency was not carried over until I ran the same restore through the console. Two conclusions drawn from a single observation on a single interface, both wrong.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/08/fsx-ontap-cross-region-backup-copy/" rel="noopener noreferrer"&gt;AWS What's New: FSx for NetApp ONTAP now supports copying backups across AWS Regions and accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/08/aws-backup-amazon-fsx-netapp-cross-account-region/" rel="noopener noreferrer"&gt;AWS What's New: AWS Backup adds cross-Region and cross-account backup support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/copy-backups.html" rel="noopener noreferrer"&gt;AWS: Copying backups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/copying-backups-same-account.html" rel="noopener noreferrer"&gt;AWS: Copying backups within the same AWS account&lt;/a&gt; — the backup-copy vs SnapMirror guidance and the RPO / RTO figures&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/using-backups.html" rel="noopener noreferrer"&gt;AWS: Protecting your data with volume backups&lt;/a&gt; — restore target confined to the backup's Region, SSD-first restores, background priority&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/to-restore-backups.html" rel="noopener noreferrer"&gt;AWS: Restoring a backup to a new volume&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/APIReference/API_CreateVolumeFromBackup.html" rel="noopener noreferrer"&gt;AWS: CreateVolumeFromBackup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-fsx-volume.html" rel="noopener noreferrer"&gt;AWS CloudFormation: AWS::FSx::Volume&lt;/a&gt; — the &lt;code&gt;BackupId&lt;/code&gt; property&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/cross-region-backup.html" rel="noopener noreferrer"&gt;AWS Backup: Creating backup copies across AWS Regions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/create-cross-account-backup.html" rel="noopener noreferrer"&gt;AWS Backup: Creating backup copies across AWS accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/encryption.html" rel="noopener noreferrer"&gt;AWS Backup: Encryption for backups in AWS Backup&lt;/a&gt; (why AWS managed keys cannot do a cross-account copy)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/backup-feature-availability.html" rel="noopener noreferrer"&gt;AWS Backup: Feature availability&lt;/a&gt; (the per-Region exceptions)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://community.netapp.com/community/discussion/468353/cross-region-and-cross-account-backup-for-amazon-fsx-for-netapp-ontap" rel="noopener noreferrer"&gt;Cross-Region and Cross-Account Backup for Amazon FSx for NetApp ONTAP&lt;/a&gt; (NetApp Community: the cross-account walkthrough, four architecture patterns, clean room and restore testing)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/limits.html" rel="noopener noreferrer"&gt;AWS: Quotas (FSx for ONTAP)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Every resource used for this test has been deleted. The figures are measurements from one specific environment and configuration, and will differ with data volume and setup.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>storage</category>
      <category>disasterrecovery</category>
      <category>devops</category>
    </item>
    <item>
      <title>Using Databricks FILE type against data on NAS: what works today with FSx for ONTAP S3 Access Points</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Wed, 12 Aug 2026 15:07:29 +0000</pubDate>
      <link>https://dev.to/aws-builders/using-databricks-file-type-against-data-on-nas-what-works-today-with-fsx-for-ontap-s3-access-points-52m6</link>
      <guid>https://dev.to/aws-builders/using-databricks-file-type-against-data-on-nas-what-works-today-with-fsx-for-ontap-s3-access-points-52m6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A follow-up to &lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-lakehouse-part2-databricks" rel="noopener noreferrer"&gt;Part 2: Databricks and FSx for ONTAP S3 Access Points&lt;/a&gt; in the "FSx for ONTAP S3 Access Points x Lakehouse Deep Dive" series. Some results changed since Part 2 — if you read that one, see "Why reads fail" below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I tested whether images and PDFs already sitting on FSx for ONTAP can be referenced from&lt;br&gt;
Databricks without copying them into the lakehouse. The result is: registration works, reads&lt;br&gt;
do not.&lt;/p&gt;

&lt;p&gt;If you only need the zero-copy verdict, read "Connecting to an FSx for ONTAP S3 Access Point"&lt;br&gt;
and "Design tips" below. If you want the behaviour of the &lt;code&gt;FILE&lt;/code&gt; type itself, start from the&lt;br&gt;
top.&lt;/p&gt;

&lt;p&gt;Databricks shipped &lt;code&gt;FILE&lt;/code&gt; as a beta column type: a reference to an unstructured file held as a&lt;br&gt;
single column in a Delta table. The value is a struct carrying &lt;code&gt;uri&lt;/code&gt; / &lt;code&gt;offset&lt;/code&gt; / &lt;code&gt;size&lt;/code&gt; /&lt;br&gt;
&lt;code&gt;content_type&lt;/code&gt; / &lt;code&gt;checksum&lt;/code&gt;. Because it holds the reference and the metadata as a struct, you&lt;br&gt;
can pass it straight from a table into an AI function such as &lt;code&gt;ai_parse_document&lt;/code&gt;. There are&lt;br&gt;
two variants — &lt;code&gt;FILE EXTERNAL&lt;/code&gt;, which holds only the reference, and &lt;code&gt;FILE MANAGED&lt;/code&gt;, which&lt;br&gt;
copies the bytes into Databricks.&lt;/p&gt;

&lt;p&gt;I tested this against files on Amazon FSx for NetApp ONTAP, reached through an FSx for ONTAP S3&lt;br&gt;
Access Point — the feature that exposes an ONTAP volume over an S3-compatible API.&lt;/p&gt;

&lt;p&gt;Here is what I found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registration against an FSx for ONTAP S3 AP succeeds; reads do not.&lt;/strong&gt; The cause sits in the
session policy attached to the temporary credentials Unity Catalog vends, and there is no way
to work around it from the user side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The FILE type itself works.&lt;/strong&gt; But you have to choose between &lt;code&gt;FILE EXTERNAL&lt;/code&gt; and
&lt;code&gt;FILE MANAGED&lt;/code&gt; before you ingest&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your design puts metadata in object tags, tag values are effectively ASCII.&lt;/strong&gt; Most CJK
strings are rejected while a minority are accepted. AWS Support has escalated that split to
the service team as a potential defect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The operator surface is inconsistent.&lt;/strong&gt; &lt;code&gt;GROUP BY&lt;/code&gt; and &lt;code&gt;DISTINCT&lt;/code&gt; are accepted; &lt;code&gt;=&lt;/code&gt; and
&lt;code&gt;ORDER BY&lt;/code&gt; are rejected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below: what is established for each of those four, and how to turn it into design decisions.&lt;br&gt;
The templates and scripts are in the repository, so you can re-run the same tests in your own&lt;br&gt;
account.&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Measured&lt;/td&gt;
&lt;td&gt;2026-08-12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;One FSx for ONTAP file system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access Point&lt;/td&gt;
&lt;td&gt;One, INTERNET origin, UNIX root identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workspace&lt;/td&gt;
&lt;td&gt;One non-trial workspace, purpose-built, same account and region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute&lt;/td&gt;
&lt;td&gt;Serverless SQL warehouse, and a classic DBR 18.2 cluster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control&lt;/td&gt;
&lt;td&gt;A S3 general bucket in the same account, running the same operations alongside&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;SQL Statement Execution API, and boto3 from my workstation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Running the S3 general bucket control alongside matters. Looking only at the Access Point side, you&lt;br&gt;
cannot separate a defect in your own environment from behaviour specific to the Access Point.&lt;br&gt;
Except where stated otherwise, every conclusion below rests on the same operation succeeding&lt;br&gt;
against S3 general bucket.&lt;/p&gt;

&lt;p&gt;Note that the object tag results depend on the ONTAP tag validation implementation, so a&lt;br&gt;
different ONTAP version may behave differently. The FSx console and &lt;code&gt;describe-file-systems&lt;/code&gt; do&lt;br&gt;
not expose the version; use the ONTAP REST API (&lt;code&gt;GET /api/cluster?fields=version&lt;/code&gt;) to check&lt;br&gt;
yours.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the FILE type can and cannot do
&lt;/h2&gt;

&lt;p&gt;First, the behaviour of the type itself. There is prior work on this feature from Databricks&lt;br&gt;
Japan: &lt;a href="https://qiita.com/taka_yayoi/items/ab110946862e63dca9f6" rel="noopener noreferrer"&gt;パス文字列でもバイナリでもない。DatabricksのFILE型を試す&lt;/a&gt;&lt;br&gt;
by taka_yayoi. Every operational caveat it lists reproduced in my environment, so I have&lt;br&gt;
tabulated those first. &lt;em&gt;(What follows is my paraphrase and my own measurements, not a&lt;br&gt;
translation.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some terms first. &lt;code&gt;FILE MANAGED&lt;/code&gt; copies bytes into a Databricks-side location called a&lt;br&gt;
FileSpace. &lt;code&gt;list_files&lt;/code&gt; enumerates files on a Volume and returns FILE values; &lt;code&gt;create_file&lt;/code&gt;&lt;br&gt;
constructs a FILE value explicitly. &lt;code&gt;_object_metadata&lt;/code&gt; reads an object's tags and user&lt;br&gt;
metadata.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;th&gt;Measured&lt;/th&gt;
&lt;th&gt;Design implication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FileSpace on the same volume as the source&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CREATE TABLE&lt;/code&gt; succeeds; &lt;code&gt;INSERT&lt;/code&gt; fails with &lt;code&gt;Cannot get file metadata under managed storage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Give the FileSpace its own volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;checksum&lt;/code&gt; on FILE values from &lt;code&gt;list_files&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Null on every row&lt;/td&gt;
&lt;td&gt;Not usable for integrity checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;checksum&lt;/code&gt; under &lt;code&gt;FILE MANAGED&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Populated with &lt;code&gt;ETAG:"…"&lt;/code&gt;, matching the &lt;code&gt;etag&lt;/code&gt; from &lt;code&gt;_object_metadata&lt;/code&gt; on S3 general bucket exactly as a string&lt;/td&gt;
&lt;td&gt;Comes from the object store, so it is usable for reconciliation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filenames under &lt;code&gt;FILE MANAGED&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Replaced with an opaque UUID; the extension does not survive either&lt;/td&gt;
&lt;td&gt;Copy the name to a column &lt;strong&gt;before&lt;/strong&gt; ingesting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format of &lt;code&gt;uri&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Prefixed with &lt;code&gt;dbfs:&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Normalise before writing string comparisons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serverless notebooks&lt;/td&gt;
&lt;td&gt;Unsupported&lt;/td&gt;
&lt;td&gt;Use the SQL Statement Execution API against a serverless SQL warehouse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic garbage collection in beta&lt;/td&gt;
&lt;td&gt;Does not run&lt;/td&gt;
&lt;td&gt;The FileSpace grows on every re-run. Plan for manual deletion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The Previews page toggle&lt;/td&gt;
&lt;td&gt;Used &lt;code&gt;FILE EXTERNAL&lt;/code&gt; / &lt;code&gt;FILE MANAGED&lt;/code&gt; without touching it (confirmed on 2 workspaces)&lt;/td&gt;
&lt;td&gt;May not be required. Do not assume either way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;content_type&lt;/code&gt; from &lt;code&gt;create_file&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;binary/octet-stream&lt;/code&gt; for both Japanese and ASCII&lt;/td&gt;
&lt;td&gt;Pass it explicitly if anything downstream branches on it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What this tells us&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The difference between &lt;code&gt;FILE EXTERNAL&lt;/code&gt; and &lt;code&gt;FILE MANAGED&lt;/code&gt; is not only whether bytes are copied.&lt;br&gt;
Whether &lt;code&gt;checksum&lt;/code&gt; is populated and whether the filename survives both change. Reversing the&lt;br&gt;
decision later means re-ingesting, so decide before you ingest.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;content_type&lt;/code&gt;, I got &lt;code&gt;binary/octet-stream&lt;/code&gt; for both Japanese and ASCII input. For the same&lt;br&gt;
files, &lt;code&gt;list_files&lt;/code&gt; reported &lt;code&gt;text/plain&lt;/code&gt;, so the two paths infer differently. If anything&lt;br&gt;
downstream branches on &lt;code&gt;content_type&lt;/code&gt;, pass it explicitly.&lt;/p&gt;
&lt;h2&gt;
  
  
  The state of operator support
&lt;/h2&gt;

&lt;p&gt;Both the documentation and the prior article state that FILE columns cannot be used in grouping&lt;br&gt;
expressions. Testing it, support splits more finely than that.&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;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GROUP BY file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT DISTINCT file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GROUP BY file.uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;accepted (the documented approach)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;d.file = r.attachment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rejected — &lt;code&gt;The `=` does not support ordering on type "FILE"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ORDER BY file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rejected — &lt;code&gt;The `sortorder` does not support ordering on type "FILE EXTERNAL"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What this tells us&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GROUP BY&lt;/code&gt; and &lt;code&gt;DISTINCT&lt;/code&gt; both need equality semantics to decide group membership. Both are&lt;br&gt;
accepted while the equality operator itself is explicitly rejected. Since the error names the&lt;br&gt;
type's operator surface, this does not look like row-count-dependent behaviour.&lt;/p&gt;

&lt;p&gt;That said, this was confirmed on two rows. I cannot rule out that grouping degrades to identity&lt;br&gt;
comparison rather than value equality. In practice, use &lt;code&gt;GROUP BY file.uri&lt;/code&gt; and do not depend on&lt;br&gt;
&lt;code&gt;GROUP BY file&lt;/code&gt; being accepted. This is beta, so the behaviour may change.&lt;/p&gt;
&lt;h2&gt;
  
  
  Connecting to an FSx for ONTAP S3 Access Point
&lt;/h2&gt;

&lt;p&gt;This is the main question. The answer is "registration works, reads do not", and the cause is&lt;br&gt;
not a lack of Access Point support.&lt;/p&gt;
&lt;h3&gt;
  
  
  The four things registration needs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Required form&lt;/th&gt;
&lt;th&gt;Symptom when wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;External ID in the trust policy&lt;/td&gt;
&lt;td&gt;The Databricks &lt;strong&gt;account UUID&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;403 Forbidden&lt;/code&gt; from the storage provider. The metastore ID and the workspace ID do not work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-assume in the trust policy&lt;/td&gt;
&lt;td&gt;Account root as the principal, with an &lt;code&gt;aws:PrincipalArn&lt;/code&gt; condition naming the role ARN&lt;/td&gt;
&lt;td&gt;Naming the role as its own principal fails at creation with &lt;code&gt;Invalid principal&lt;/code&gt;, because IAM validates that the principal exists. Unity Catalog requires the role to be able to assume itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource in the IAM permission policy&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;access point ARN&lt;/strong&gt; (&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; and &lt;code&gt;.../object/*&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AccessDeniedException&lt;/code&gt;. The alias-as-bucket-name form alone does not work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External location URL&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;alias&lt;/strong&gt; form (&lt;code&gt;s3://&amp;lt;alias&amp;gt;/&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;An ARN-style URL is rejected immediately with &lt;code&gt;url does not specify a valid bucket name&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What this tells us&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is an asymmetry worth committing to memory: the same Access Point must be written as an&lt;br&gt;
ARN in the IAM policy and as an alias in the external location URL. The AWS CLI works with the&lt;br&gt;
alias form of the ARN, so verifying connectivity with the CLI first and then writing the IAM&lt;br&gt;
policy will trip you up.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;aws:PrincipalArn&lt;/code&gt; approach works because that key resolves to the role ARN rather than the&lt;br&gt;
assumed-role session ARN. Rather than trust my memory of the condition key, I deployed the&lt;br&gt;
template, assumed the role, and assumed it again with those credentials to confirm.&lt;/p&gt;

&lt;p&gt;With all four satisfied, &lt;code&gt;CREATE EXTERNAL LOCATION&lt;/code&gt; succeeds with &lt;code&gt;skip_validation=False&lt;/code&gt; —&lt;br&gt;
meaning Unity Catalog's own validation passed. The external volume on top can be created too.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why reads fail
&lt;/h3&gt;

&lt;p&gt;After registration, every read path returns 403 or an authorisation error. The same on&lt;br&gt;
serverless SQL and on a classic DBR 18.2 cluster.&lt;/p&gt;

&lt;p&gt;This is the test that isolated the cause. Unity Catalog will vend the temporary credentials it&lt;br&gt;
uses if you ask for them. I requested them for both paths and used them from my workstation,&lt;br&gt;
with no Databricks compute or network involved.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vended for&lt;/th&gt;
&lt;th&gt;&lt;code&gt;ListObjectsV2&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;HeadObject&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3 general bucket control path&lt;/td&gt;
&lt;td&gt;success&lt;/td&gt;
&lt;td&gt;success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP S3 AP path&lt;/td&gt;
&lt;td&gt;AccessDenied&lt;/td&gt;
&lt;td&gt;403&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same role, same session, same network; the only variable is which path the credentials were&lt;br&gt;
scoped to. The error states the cause explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&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;name&amp;gt;"
because no session policy allows the s3:ListBucket action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this tells us&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;because no session policy allows&lt;/code&gt; is the decisive part. What is named is the session policy,&lt;br&gt;
not the role's permissions. By session policy I mean the down-scoped policy passed to&lt;br&gt;
&lt;code&gt;AssumeRole&lt;/code&gt; to constrain the role's permissions on a per-request basis.&lt;/p&gt;

&lt;p&gt;The policy body is generated by Unity Catalog and is not readable by the user. But since adding&lt;br&gt;
an access-point-ARN allowance to the role changes nothing, &lt;strong&gt;the session policy appears not to&lt;br&gt;
mention the access point ARN resource form at all.&lt;/strong&gt; Only the overlap between the session policy&lt;br&gt;
and the role policy is effective, so additions on the role side have no effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no workaround on the user side.&lt;/strong&gt; Unity Catalog generates the policy, so the fix has&lt;br&gt;
to happen there. Emitting the access point ARN form in the session policy should resolve it,&lt;br&gt;
though I do not know what implementation constraints apply.&lt;/p&gt;

&lt;p&gt;Note also that validation at registration time (&lt;code&gt;skip_validation=False&lt;/code&gt;) passes while reads&lt;br&gt;
return 403. What that validation checks is not published, so I cannot account for the&lt;br&gt;
difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  What changed since Part 2
&lt;/h3&gt;

&lt;p&gt;Part 2 of this series (measured 2026-05) reported that listing top-level files and reading an&lt;br&gt;
explicit file did work through a UC external location. In this run (2026-08-12), &lt;code&gt;read_files&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;list_files&lt;/code&gt;, &lt;code&gt;to_file&lt;/code&gt; and &lt;code&gt;dbutils.fs.ls&lt;/code&gt; all returned 403; no read path partially succeeded.&lt;/p&gt;

&lt;p&gt;That environment is gone, so I cannot account for the difference conclusively. The likely&lt;br&gt;
explanation is that the earlier partial successes were observed on an instance-profile path&lt;br&gt;
rather than through UC-vended credentials — Part 2 lists instance-profile direct access as a&lt;br&gt;
separate approach that works but sits outside UC governance, so the paths may not have been&lt;br&gt;
cleanly separated.&lt;/p&gt;

&lt;p&gt;This run had a S3 general bucket control alongside and includes the vended-credential test outside&lt;br&gt;
Databricks entirely, so take these results as the current ones. I will annotate the Part 2&lt;br&gt;
table accordingly.&lt;/p&gt;

&lt;p&gt;This test has prerequisites. Enable &lt;strong&gt;External Data Access&lt;/strong&gt; on the metastore and grant&lt;br&gt;
&lt;code&gt;EXTERNAL USE SCHEMA&lt;/code&gt; and &lt;code&gt;EXTERNAL USE LOCATION&lt;/code&gt;. Both are disabled by default. They are the&lt;br&gt;
controls that allow credentials to be used externally, so they are worth understanding&lt;br&gt;
independently of this issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  A table to work through when you see a 403
&lt;/h3&gt;

&lt;p&gt;Arranged so that anyone hitting the same 403 can check top to bottom. &lt;strong&gt;None of these is the&lt;br&gt;
cause.&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;What to suspect&lt;/th&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unity Catalog volume privileges&lt;/td&gt;
&lt;td&gt;The failing volume had &lt;code&gt;READ VOLUME&lt;/code&gt;; the working control volume had no privileges at all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM role permissions&lt;/td&gt;
&lt;td&gt;Assumed the same role from my workstation and listed successfully. HEAD on a real object returned 200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HEAD on a missing key returning 403 (killing the &lt;code&gt;_delta_log&lt;/code&gt; probe)&lt;/td&gt;
&lt;td&gt;The Access Point returned 404 for every shape I tried — same as S3 general bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;x-amz-expected-bucket-owner&lt;/code&gt; being rejected&lt;/td&gt;
&lt;td&gt;Accepted by both the Access Point and the control bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A different endpoint&lt;/td&gt;
&lt;td&gt;Both Databricks and the AWS CLI use &lt;code&gt;&amp;lt;alias&amp;gt;.s3.&amp;lt;region&amp;gt;.amazonaws.com&lt;/code&gt;, which CNAMEs to &lt;code&gt;s3-r-w.&amp;lt;region&amp;gt;.amazonaws.com&lt;/code&gt;. Same host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insufficient compute role permissions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sts:GetCallerIdentity&lt;/code&gt; raised &lt;code&gt;NoCredentialsError&lt;/code&gt; on the driver. The cluster has no default AWS credentials; the compute role is not in the path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The S3 gateway endpoint&lt;/td&gt;
&lt;td&gt;No change after disassociating the route. The failure reproduces outside the VPC with vended credentials, so this path is not the problem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One note on the last row. A Databricks-managed VPC creates an S3 gateway endpoint at workspace&lt;br&gt;
creation, and the private route tables point the S3 managed prefix list at it. That was not the&lt;br&gt;
cause here, but for designs that reach an S3 AP from inside a VPC, check first whether the&lt;br&gt;
alias resolves into the prefix list's range. I did not reconcile the resolved CIDRs against the&lt;br&gt;
prefix list contents in this environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What object tags can actually hold
&lt;/h2&gt;

&lt;p&gt;If you want metadata to live with the file, object tags are one option: tag at write time, read&lt;br&gt;
from the table side later. That path has constraints.&lt;/p&gt;

&lt;p&gt;The following was measured without Databricks in the picture — boto3 from my workstation&lt;br&gt;
straight against the FSx for ONTAP S3 Access Point, with the same operations running against&lt;br&gt;
the S3 general bucket control bucket.&lt;/p&gt;

&lt;p&gt;Start with what works. &lt;code&gt;PutObjectTagging&lt;/code&gt;, &lt;code&gt;GetObjectTagging&lt;/code&gt; and &lt;code&gt;DeleteObjectTagging&lt;/code&gt; all&lt;br&gt;
function, as do &lt;code&gt;x-amz-meta-*&lt;/code&gt; headers and &lt;code&gt;x-amz-tagging&lt;/code&gt; on the same &lt;code&gt;PutObject&lt;/code&gt; as the data.&lt;br&gt;
The limits (10 tags per object, 128 characters for a key, 256 for a value) are the same values&lt;br&gt;
S3 general bucket documents; I did not measure the boundaries.&lt;/p&gt;

&lt;p&gt;Reading through a second access point on the same volume returned tags written through the&lt;br&gt;
first. &lt;strong&gt;Tag retention is not scoped per access point.&lt;/strong&gt; For existing NAS assets shared across&lt;br&gt;
several paths, that property works in your favour. But I did not observe where the tags are&lt;br&gt;
physically stored, so visibility from NFS or SMB, and retention across SnapMirror, FlexClone&lt;br&gt;
and Snapshot restore, remain unverified.&lt;/p&gt;

&lt;p&gt;Two constraints change how you design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An object overwrite clears tags and user metadata.&lt;/strong&gt; Nothing errors. A pipeline that rewrites&lt;br&gt;
a file has to re-apply them in the same &lt;code&gt;PutObject&lt;/code&gt;. My understanding is that S3 general bucket behaves&lt;br&gt;
the same way, since &lt;code&gt;PutObject&lt;/code&gt; replaces the object, so treat this as a general S3 design point&lt;br&gt;
rather than something specific to FSx for ONTAP. I did not run a side-by-side control for this&lt;br&gt;
particular case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tag values are effectively ASCII.&lt;/strong&gt; Every printable Latin-1 character I tried&lt;br&gt;
(U+00A1–U+00FF) was accepted. Above U+0100, most are rejected with &lt;code&gt;InvalidTag&lt;/code&gt; — but not all.&lt;br&gt;
A minority of CJK strings are accepted, deterministically, 6 runs out of 6.&lt;/p&gt;

&lt;p&gt;To find the unit of validation, I tested single characters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&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;U+5206&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InvalidTag&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+985E&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InvalidTag&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+5206 U+985E&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;accepted&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+6771&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InvalidTag&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+4EAC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InvalidTag&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U+6771 U+4EAC&lt;/td&gt;
&lt;td&gt;&lt;code&gt;InvalidTag&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What this tells us&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two two-character strings, every constituent character rejected on its own, and the two strings&lt;br&gt;
disagree with each other. &lt;strong&gt;Validity is determined by the complete byte sequence, not as a&lt;br&gt;
function of the characters in it&lt;/strong&gt; — so this is not a per-character allowlist.&lt;/p&gt;

&lt;p&gt;For the two-character pairs I tested, results were identical forwards and backwards, and&lt;br&gt;
identical as a tag key and as a tag value. I did not test longer strings or other scripts.&lt;/p&gt;

&lt;p&gt;I raised this with AWS Support. After checking it against the S3 and FSx for ONTAP&lt;br&gt;
documentation, they gave the view that the pattern does not match any intentional validation&lt;br&gt;
they could identify, and escalated it to the service team as a potential defect in the tag&lt;br&gt;
validation layer.&lt;/p&gt;

&lt;p&gt;Their reply also quoted the documented character set: letters, whitespace, and&lt;br&gt;
&lt;code&gt;+ - = . _ : / @&lt;/code&gt;. That does not predict the split under either reading. If "letters" means&lt;br&gt;
Unicode letters, every string I tested qualifies yet half are rejected. If it means ASCII&lt;br&gt;
letters, all of them should be rejected yet half are accepted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The design guidance is simple: keep tags ASCII and keep localised text in a column.&lt;/strong&gt; Today&lt;br&gt;
the situation is "some strings pass and some do not", which is more awkward to handle than a&lt;br&gt;
uniform restriction. I will update the repository and this article when a determination&lt;br&gt;
arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design tips
&lt;/h2&gt;

&lt;p&gt;The same material, ordered the way you actually make the decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FILE type&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decide between &lt;code&gt;FILE EXTERNAL&lt;/code&gt; and &lt;code&gt;FILE MANAGED&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; ingesting. Checksum and filename handling differ, and changing course later means re-ingesting&lt;/li&gt;
&lt;li&gt;Give the FileSpace its own volume. On the same volume, &lt;code&gt;INSERT&lt;/code&gt; fails — and because the error surfaces at ingest rather than at configuration time, diagnosis is slower&lt;/li&gt;
&lt;li&gt;Copy the filename to its own column before ingesting into &lt;code&gt;FILE MANAGED&lt;/code&gt;. It is replaced with a UUID and the extension does not survive&lt;/li&gt;
&lt;li&gt;Do not rely on &lt;code&gt;content_type&lt;/code&gt; inference; pass it explicitly&lt;/li&gt;
&lt;li&gt;Group by &lt;code&gt;file.uri&lt;/code&gt;. Do not depend on &lt;code&gt;GROUP BY file&lt;/code&gt; being accepted&lt;/li&gt;
&lt;li&gt;Automatic garbage collection does not run in beta. Build manual FileSpace deletion into operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Unity Catalog and FSx for ONTAP S3 Access Points&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The external ID in the trust policy is the Databricks &lt;strong&gt;account UUID&lt;/strong&gt;. The metastore ID does not work&lt;/li&gt;
&lt;li&gt;The trust policy needs self-assume: account root as the principal with an &lt;code&gt;aws:PrincipalArn&lt;/code&gt; condition naming the role ARN&lt;/li&gt;
&lt;li&gt;The IAM permission policy needs the &lt;strong&gt;access point ARN&lt;/strong&gt;, including &lt;code&gt;.../object/*&lt;/code&gt;. The alias-form ARN alone does not work&lt;/li&gt;
&lt;li&gt;The external location URL takes the &lt;strong&gt;alias&lt;/strong&gt; form. ARN-style URLs are rejected&lt;/li&gt;
&lt;li&gt;Reads do not work today. If zero-copy is a requirement, evaluate AWS-native engines that authorise in the caller's own IAM context alongside it (this repository has walkthroughs under &lt;code&gt;integrations/athena&lt;/code&gt;, &lt;code&gt;integrations/glue&lt;/code&gt;, &lt;code&gt;integrations/emr-spark&lt;/code&gt;, &lt;code&gt;integrations/redshift-spectrum&lt;/code&gt; and &lt;code&gt;integrations/lake-formation&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Object tags&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep tags ASCII and keep localised text in a column&lt;/li&gt;
&lt;li&gt;Re-apply tags in the same &lt;code&gt;PutObject&lt;/code&gt; when you rewrite a file. An overwrite clears them&lt;/li&gt;
&lt;li&gt;Tag retention is not scoped per access point, so the same tags are readable through several access points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to test&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run a S3 general bucket control alongside. Without it you cannot separate a defect in your environment from Access Point behaviour, and the first 403 looks like the answer&lt;/li&gt;
&lt;li&gt;If the platform exposes credential vending, use those credentials from your own machine and compare. Isolating the problem is much faster without the vendor's compute in the path&lt;/li&gt;
&lt;li&gt;Verify teardown by reconciling against a baseline. NAT Gateways do not disappear when you delete the workspace&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Run it yourself
&lt;/h2&gt;

&lt;p&gt;All of the above is in the repository as templates and scripts: one template, one script, one&lt;br&gt;
runbook.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Artefact&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;integrations/databricks/uc-storage-credential-role.yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The IAM role the Unity Catalog storage credential assumes, plus a S3 general bucket control bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shared/scripts/probe_uc_external_location.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Seeds identical object tags on both sides, registers both external locations with validation on, reads &lt;code&gt;_object_metadata&lt;/code&gt; through each, prints a verdict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docs/en/databricks-verification-runbook.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prerequisites, where each parameter comes from, the verdict branches, dependency-ordered teardown, measured cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Baseline first. This is what makes "teardown looks clean" verifiable&lt;/span&gt;
python3 shared/scripts/audit_databricks_workspace_footprint.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; &lt;span class="nt"&gt;--save&lt;/span&gt; /tmp/baseline.json

&lt;span class="c"&gt;# 2. The IAM role and control bucket&lt;/span&gt;
aws cloudformation deploy &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; fsxn-databricks-uc-credential &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; integrations/databricks/uc-storage-credential-role.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_NAMED_IAM &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameter-overrides&lt;/span&gt; file://cfn-params/databricks-uc-storage-credential.json

&lt;span class="c"&gt;# 3. Compare. --control-bucket is deliberately mandatory&lt;/span&gt;
.venv/bin/python shared/scripts/probe_uc_external_location.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; &amp;lt;your-profile&amp;gt; &lt;span class="nt"&gt;--role-arn&lt;/span&gt; &amp;lt;from stack output&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ap-alias&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;-ext-s3alias&lt;/span&gt; &lt;span class="nt"&gt;--ap-name&lt;/span&gt; &amp;lt;name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--control-bucket&lt;/span&gt; &amp;lt;from stack output&amp;gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; &lt;span class="nt"&gt;--vend-check&lt;/span&gt;

&lt;span class="c"&gt;# 4. Tear down and reconcile against the baseline&lt;/span&gt;
python3 shared/scripts/audit_databricks_workspace_footprint.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; &lt;span class="nt"&gt;--compare&lt;/span&gt; /tmp/baseline.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script prints one of four verdicts. The first is the important one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inconclusive&lt;/strong&gt; — the S3 general bucket control could not be read either, so this run says nothing about the Access Point. Fix the environment first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registration succeeded, reads denied&lt;/strong&gt; — the result in this article&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registration and reads both succeeded&lt;/strong&gt; — the platform changed. Please tell me which release&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registration itself failed&lt;/strong&gt; — almost certainly a missing Access Point ARN in the IAM policy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One note on the parameter file: a &lt;code&gt;_comment&lt;/code&gt; key in a CloudFormation parameter file makes the&lt;br&gt;
CLI fail with &lt;code&gt;Unknown parameter in Parameters[n]&lt;/code&gt;. The description of each parameter lives in&lt;br&gt;
&lt;code&gt;cfn-params/README.md&lt;/code&gt; instead, so fill in values with that open alongside.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you are evaluating a different platform
&lt;/h2&gt;

&lt;p&gt;The specific bug is Databricks'. The shape is not, and the shape is the part worth taking away&lt;br&gt;
if you are looking at a different product.&lt;/p&gt;

&lt;p&gt;Managed lakehouse platforms generally reach into your cloud account by assuming a role you&lt;br&gt;
create, then constrain that role at runtime with a down-scoped session policy so that&lt;br&gt;
credentials handed to a query cannot roam. That is good design.&lt;/p&gt;

&lt;p&gt;But the policy has to name your storage. If the storage is addressed through an access point&lt;br&gt;
while the policy names it in bucket-form ARNs, the two do not match and authorisation fails —&lt;br&gt;
however correct your own role policy is.&lt;/p&gt;

&lt;p&gt;So the question to put to a platform you are evaluating, before you commit to an architecture,&lt;br&gt;
can be narrow and answerable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you vend temporary credentials for an external location, which resource ARN forms does&lt;br&gt;
the session policy contain? Specifically, does it include the S3 access point ARN form in&lt;br&gt;
addition to the bucket form?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And if the platform exposes credential vending, you can verify it without asking anyone:&lt;br&gt;
request the credentials, use them from your own machine, and compare a native bucket path&lt;br&gt;
against an access point path. That is what &lt;code&gt;--vend-check&lt;/code&gt; above does, and it was the only test&lt;br&gt;
in this exercise that produced a conclusion with no room for interpretation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost and cleanup
&lt;/h2&gt;

&lt;p&gt;I created a test workspace and destroyed it the same day. The workspace mode that uses your own&lt;br&gt;
AWS account creates a VPC with a NAT Gateway. A NAT Gateway runs roughly 45 USD a month per&lt;br&gt;
gateway (hourly charge only, excluding data processing, at the August 2026 Tokyo region rate).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;These resources are created directly rather than through a CloudFormation stack, so deleting&lt;br&gt;
the workspace does not remove them.&lt;/strong&gt; This is the easiest place to make a foolish mistake during&lt;br&gt;
cleanup.&lt;/p&gt;

&lt;p&gt;I recorded a baseline before starting and reconciled afterwards. Available NAT Gateways back to&lt;br&gt;
zero; VPC, both IAM roles and the bucket gone, with no unassociated Elastic IPs.&lt;/p&gt;

&lt;p&gt;I turned that check into a script (&lt;code&gt;shared/scripts/audit_databricks_workspace_footprint.py&lt;/code&gt;). In&lt;br&gt;
a shared account a plain listing is not a verdict, because most of what it shows belongs to&lt;br&gt;
someone else. Run it once before creating anything and once after teardown; it exits non-zero if&lt;br&gt;
anything exists that was not in the baseline. Running it confirmed this teardown was clean and&lt;br&gt;
also surfaced leftover roles from an earlier workspace. The cost breakdown is in the runbook with&lt;br&gt;
measured figures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current state and open items
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Established&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CREATE STORAGE CREDENTIAL&lt;/code&gt; / &lt;code&gt;CREATE EXTERNAL LOCATION&lt;/code&gt; / &lt;code&gt;CREATE EXTERNAL VOLUME&lt;/code&gt; succeed against an FSx for ONTAP S3 AP alias, with Unity Catalog validation left on&lt;/li&gt;
&lt;li&gt;Reads through it return 403, on both serverless SQL and a classic DBR 18.2 cluster&lt;/li&gt;
&lt;li&gt;The same failure reproduces outside Databricks, using vended credentials from a workstation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_object_metadata&lt;/code&gt; on S3 general bucket returns object tags and user metadata correctly&lt;/li&gt;
&lt;li&gt;Object tag acceptance is determined by the complete byte sequence, and within the range tested is neither order- nor position-dependent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Not established&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC-origin Access Points, and Access Points with WINDOWS identity&lt;/li&gt;
&lt;li&gt;Whether &lt;code&gt;_object_metadata&lt;/code&gt; would read tags through an Access Point if the session policy were fixed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ai_parse_document&lt;/code&gt; over files served from an Access Point&lt;/li&gt;
&lt;li&gt;Throughput and latency of these paths&lt;/li&gt;
&lt;li&gt;Whether &lt;code&gt;GROUP BY&lt;/code&gt; on a FILE column is correct at scale, or merely accepted&lt;/li&gt;
&lt;li&gt;Where object tags are physically stored; visibility from NFS or SMB; retention across SnapMirror, FlexClone, Snapshot restore and FabricPool tiering&lt;/li&gt;
&lt;li&gt;The boundary values for tag limits (10 tags, 128-character keys, 256-character values)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reporting status&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The object tag behaviour has been escalated through AWS Support to the FSx for ONTAP service&lt;br&gt;
team and is awaiting a determination. What I asked for is not a timeline but the distinction:&lt;br&gt;
a defect that will be fixed, or an undocumented restriction that gets written down. Either&lt;br&gt;
answer lets me state the constraint accurately.&lt;/p&gt;

&lt;p&gt;The session policy issue and the operator inconsistency go to Databricks when this article&lt;br&gt;
publishes.&lt;/p&gt;

&lt;p&gt;This repository carried an entry from May 2026 saying Unity Catalog external locations do not&lt;br&gt;
support S3 Access Points. Registration does work; the entry has been corrected. If you read the&lt;br&gt;
earlier version, please note the change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The FILE type is a well-built mechanism for bringing unstructured data under table governance.&lt;br&gt;
For referencing files on an FSx for ONTAP S3 Access Point with zero copies, though, it does not&lt;br&gt;
get there today. Registration works, so you can try it, but reads fail, and because the cause&lt;br&gt;
sits in the session policy on the vending side, users cannot fix it.&lt;/p&gt;

&lt;p&gt;If zero-copy is the requirement, AWS-native engines that authorise in the caller's own IAM&lt;br&gt;
context are the more straightforward path for now — in exchange for assembling table-level&lt;br&gt;
governance yourself with Lake Formation. If Unity Catalog governance is the requirement, then&lt;br&gt;
for the time being data placement has to be part of the design.&lt;/p&gt;

&lt;p&gt;The object tag character set is awaiting a determination. When it moves, I will update both the&lt;br&gt;
article and the repository.&lt;/p&gt;

&lt;p&gt;I hope this is useful to someone.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related posts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Previous: &lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-lakehouse-part2-databricks" rel="noopener noreferrer"&gt;Part 2: Databricks and FSx for ONTAP S3 Access Points&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next: &lt;a href="https://dev.to/aws-builders/why-delta-iceberg-and-hudi-cant-write-to-fsx-s3-access-points-and-what-works-instead-5be3"&gt;Part 8: OpenSharing and FSx for ONTAP&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Series index: &lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-lakehouse-part0-overview" rel="noopener noreferrer"&gt;What works, what does not, and why&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About this article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This series is based on verification in the &lt;a href="https://github.com/Yoshiki0705/fsxn-lakehouse-integrations" rel="noopener noreferrer"&gt;fsxn-lakehouse-integrations&lt;/a&gt; repository&lt;/li&gt;
&lt;li&gt;Author: Yoshiki Fujiwara&lt;/li&gt;
&lt;li&gt;Affiliation: NetApp G.K. Japan&lt;/li&gt;
&lt;li&gt;Role: Senior Cloud Solutions Architect - AWS&lt;/li&gt;
&lt;li&gt;Topic: Amazon FSx for NetApp ONTAP / Hybrid Cloud / Generative AI Data Architecture&lt;/li&gt;
&lt;li&gt;Level: 200-300&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;All test environments have been deleted. The behaviours described are measurements from&lt;br&gt;
2026-08-12 in one specific configuration and may change with platform updates. Beta features are&lt;br&gt;
involved, so please check against current documentation before making production decisions.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>databricks</category>
      <category>fsxforontap</category>
      <category>lakehouse</category>
    </item>
    <item>
      <title>S3 Burst on ONTAP Files — Collect via S3 API, Consume via FlexCache NFS/SMB</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Mon, 10 Aug 2026 01:06:17 +0000</pubDate>
      <link>https://dev.to/aws-builders/s3-burst-on-ontap-files-collect-via-s3-api-consume-via-flexcache-nfssmb-23nb</link>
      <guid>https://dev.to/aws-builders/s3-burst-on-ontap-files-collect-via-s3-api-consume-via-flexcache-nfssmb-23nb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You have an S3 ingestion pipeline. But the consumers only see an NFS mount. Sound familiar?&lt;/p&gt;

&lt;p&gt;You want to deliver driving logs to a HiL test bench, stage design jobs for an EDA toolchain, push rendering assets to production nodes, or feed genomic sequencer output to an HPC cluster. In each case the writer speaks S3 API in the cloud while the reader speaks NFS/SMB on physical hardware. Without a way to connect these directly, you end up adding a sync job in between — and with it comes latency, cost, and one more thing to monitor.&lt;/p&gt;

&lt;p&gt;This structure spans industries: automotive, semiconductor, media/VFX, oil and gas, life sciences, manufacturing, remote work, and IoT (see the full table in When This Pattern Fits below).&lt;/p&gt;

&lt;p&gt;This post introduces a pattern that removes that sync job. Write to an Amazon FSx for NetApp ONTAP (hereafter FSx for ONTAP) S3 Access Point, and the data appears on a FlexCache NFS mount. No separate copy job and no scheduled replication between the two. FlexCache pulls the data that gets read from the origin and holds it on the cache side, so what this removes is the sync job you would otherwise own — not the data transfer itself. I measured all four directions to see how fast the visibility propagates.&lt;/p&gt;

&lt;p&gt;Here's the conclusion up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 PutObject → FlexCache NFS read visibility: p50 8 ms.&lt;/strong&gt; No separate copy or replication job; the data that gets read is pulled into the cache on demand&lt;/li&gt;
&lt;li&gt;FlexCache overhead is +5 ms (vs. reading origin directly) — nearly transparent for same-region VPC peering&lt;/li&gt;
&lt;li&gt;Reverse direction (NFS write → S3 AP read) is p50 44 ms, dominated by S3 API overhead. Not the main path in this design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FlexCache duality (NAS bucket S3 reads on FlexCache volumes) works, but only after &lt;code&gt;-is-s3-enabled true&lt;/code&gt; is set on the cache volume&lt;/strong&gt; in advanced privilege. My first attempt returned &lt;code&gt;AccessDenied&lt;/code&gt; because that step was missing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files" rel="noopener noreferrer"&gt;Yoshiki0705/s3-burst-on-ontap-files&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdxibfen6060p4uh6bh9g.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdxibfen6060p4uh6bh9g.png" alt="S3 Burst on ONTAP Files — Architecture Overview" width="800" height="249"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[S3 Client] --PutObject--&amp;gt; [S3 Access Point] --&amp;gt; [Origin Volume]
                                                       |
                                                  FlexCache
                                                       |
                                                [NFS/SMB Client]
&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;Layer&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Collect (write)&lt;/td&gt;
&lt;td&gt;S3 Access Point on FSx for ONTAP&lt;/td&gt;
&lt;td&gt;S3 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source of truth&lt;/td&gt;
&lt;td&gt;Origin volume&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distribution&lt;/td&gt;
&lt;td&gt;FlexCache&lt;/td&gt;
&lt;td&gt;Cluster/SVM peering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consume (read)&lt;/td&gt;
&lt;td&gt;Cache volumes&lt;/td&gt;
&lt;td&gt;NFS/SMB only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The S3 Access Point attaches to the origin only. Cache volumes serve NFS/SMB. This single decision simplifies the design considerably:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One write path. The origin is authoritative; writes always go through the S3 AP&lt;/li&gt;
&lt;li&gt;No S3 implementation differences pushed to the edge&lt;/li&gt;
&lt;li&gt;Cache side requires only FlexCache + NFS/SMB — fewer platform constraints to hit&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&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;Date&lt;/td&gt;
&lt;td&gt;2026-08-09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP version&lt;/td&gt;
&lt;td&gt;9.18.1P3D1 (both clusters)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration&lt;/td&gt;
&lt;td&gt;SINGLE_AZ_1, 128 MBps × 2 clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectivity&lt;/td&gt;
&lt;td&gt;VPC peering (same region, same account)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mount&lt;/td&gt;
&lt;td&gt;NFSv3, &lt;code&gt;actimeo=0&lt;/code&gt; (client cache disabled)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object size&lt;/td&gt;
&lt;td&gt;64 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;boto3 persistent session, same host, 30 iterations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why NFS First
&lt;/h3&gt;

&lt;p&gt;The initial measurements used NFS. Three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client cache control.&lt;/strong&gt; NFS &lt;code&gt;actimeo=0&lt;/code&gt; fully disables kernel attribute caching, isolating storage-side visibility latency. SMB oplocks/leases let the client cache independently, making it harder to measure the same thing at the same granularity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matching the identity to the security style.&lt;/strong&gt; An access point authorizes every request as one file system identity, and AWS says to use a UNIX identity for UNIX security-style volumes and a Windows identity for NTFS ones (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/s3-ap-manage-access-fsxn.html" rel="noopener noreferrer"&gt;managing data access&lt;/a&gt;). &lt;strong&gt;NTFS volumes are supported too — this is not a UNIX-only feature&lt;/strong&gt;, which corrects how an earlier version of this post put it. UNIX plus NFS was chosen here because it was the shortest path to a measurement, not because the other side is unavailable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary use-case distribution.&lt;/strong&gt; HiL test benches, rendering farms, and IoT analysis appliances are predominantly Linux + NFS. When the read side is SMB-dominant (Windows workstations in a production studio), the origin security style changes to NTFS — a different design path (see &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/ja/design-first-decisions.md" rel="noopener noreferrer"&gt;First Decisions&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  SMB Shows Identical Results
&lt;/h3&gt;

&lt;p&gt;I ran a separate verification with SMB (&lt;code&gt;mount -t cifs&lt;/code&gt;, &lt;code&gt;cache=none&lt;/code&gt;) on the same architecture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Mount method&lt;/th&gt;
&lt;th&gt;p50&lt;/th&gt;
&lt;th&gt;p90&lt;/th&gt;
&lt;th&gt;max&lt;/th&gt;
&lt;th&gt;n&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SMB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mount -t cifs&lt;/code&gt;, &lt;code&gt;cache=none&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8 ms&lt;/td&gt;
&lt;td&gt;9 ms&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NFS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mount -t nfs&lt;/code&gt;, &lt;code&gt;actimeo=0&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8 ms&lt;/td&gt;
&lt;td&gt;15 ms&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;With persistent mounts, SMB and NFS have identical visibility latency.&lt;/strong&gt; No protocol difference.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;smbclient&lt;/code&gt; (establishing a new session per request) showed p50 43 ms. This is SMB session setup overhead — the same structural problem as the AWS CLI cold-start that inflated our first NFS→S3 measurement to 873 ms. &lt;strong&gt;Production environments with persistent connections won't see this.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  SMB Considerations
&lt;/h3&gt;

&lt;p&gt;FlexCache serves both NFS and SMB. This architecture doesn't exclude SMB — but note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With a UNIX security-style origin, SMB clients receive UNIX permission-based access control, not NTFS ACLs&lt;/li&gt;
&lt;li&gt;For SMB-primary workloads, NTFS security style is the natural choice, and an access point works there with a Windows identity. The trade-off is not availability but the identity you fix on the access point, which cannot be changed after creation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mixed&lt;/code&gt; security style is available in the API but &lt;a href="https://aws.amazon.com/blogs/storage/enabling-multiprotocol-workloads-with-amazon-fsx-for-netapp-ontap/" rel="noopener noreferrer"&gt;officially not recommended by AWS&lt;/a&gt; — it's labeled "advanced users only." Permission type is determined by the last client that wrote, making troubleshooting difficult. This architecture doesn't use it&lt;/li&gt;
&lt;li&gt;SMB needs a CIFS server on the SVM. &lt;strong&gt;An Active Directory join is not required&lt;/strong&gt;: where a domain is not available, AWS documents &lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/smb-server-workgroup-setup.html" rel="noopener noreferrer"&gt;setting up an SMB server in a workgroup&lt;/a&gt; (NTLM only, no Kerberos, and no GPO, VSS or SMB3 CA shares). If you do join AD, every data operation through the access point then needs a reachable domain controller, and &lt;code&gt;HeadBucket&lt;/code&gt; succeeds even when it is not — so it cannot be used to check that&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I used here: &lt;strong&gt;a UNIX security-style origin with a UNIX identity&lt;/strong&gt;, because the read side was NFS. For an SMB-primary workload the equivalent choice is an NTFS origin with a Windows identity. Decide it before the origin volume exists — whether the cache inherits the security style from the origin is unconfirmed on the on-premises path, and if it does, changing it later means rebuilding the serve layer.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;actimeo=0&lt;/code&gt; measures the minimum visibility latency by disabling client-side caching. Production deployments should use appropriate &lt;code&gt;actimeo&lt;/code&gt; values for their workload — the default (~60 s) means subsequent reads hit kernel cache at ~0.05 ms, but changes aren't visible during that window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results — All Four Directions
&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;Direction&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;th&gt;max&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;&lt;strong&gt;S3 AP PutObject → FlexCache NFS read&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9 ms&lt;/td&gt;
&lt;td&gt;19 ms&lt;/td&gt;
&lt;td&gt;19 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;S3 AP PutObject → Origin NFS read (direct)&lt;/td&gt;
&lt;td&gt;3 ms&lt;/td&gt;
&lt;td&gt;5 ms&lt;/td&gt;
&lt;td&gt;8 ms&lt;/td&gt;
&lt;td&gt;8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;NFS write (Origin) → FlexCache NFS read&lt;/td&gt;
&lt;td&gt;6 ms&lt;/td&gt;
&lt;td&gt;7 ms&lt;/td&gt;
&lt;td&gt;25 ms&lt;/td&gt;
&lt;td&gt;25 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;NFS write (Origin) → S3 AP GetObject&lt;/td&gt;
&lt;td&gt;44 ms&lt;/td&gt;
&lt;td&gt;49 ms&lt;/td&gt;
&lt;td&gt;328 ms&lt;/td&gt;
&lt;td&gt;328 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What the Numbers Say
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Direction 1 is the main path.&lt;/strong&gt; S3 write, FlexCache NFS read: p50 8 ms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The +5 ms gap between directions 1 and 2 is FlexCache overhead.&lt;/strong&gt; For same-region VPC peering, FlexCache is nearly transparent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Direction 3 is faster than 1 (6 ms &amp;lt; 8 ms).&lt;/strong&gt; NFS writes commit directly to the origin — no S3 API overhead — and FlexCache propagation alone is faster than the full S3-to-cache path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Direction 4 (reverse) is slowest at 44 ms.&lt;/strong&gt; S3 read-side processing dominates. This design keeps reads on NFS/SMB, so direction 4 isn't the main path.&lt;/p&gt;

&lt;h3&gt;
  
  
  NFS Client Cache Effect
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;p50&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;actimeo=0&lt;/code&gt; (cache disabled)&lt;/td&gt;
&lt;td&gt;7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;actimeo=60&lt;/code&gt; (subsequent access)&lt;/td&gt;
&lt;td&gt;0.05 ms (kernel cache hit)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With defaults, reads within 60 seconds of the last attribute check hit the kernel cache. The tradeoff: you won't see changes during that window. Tune per workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  FlexCache Duality — It Works (With One Extra Step)
&lt;/h2&gt;

&lt;p&gt;S3 access to NAS FlexCache volumes — "duality" — arrives in &lt;a href="https://docs.netapp.com/us-en/ontap/flexcache/enable-flexcache-duality.html" rel="noopener noreferrer"&gt;ONTAP 9.18.1&lt;/a&gt;, not 9.14.1 as an earlier version of this post said. I tested it on FSx for ONTAP 9.18.1P3D1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result: Works After Enabling &lt;code&gt;-is-s3-enabled true&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;My initial test returned &lt;code&gt;AccessDenied&lt;/code&gt; and I concluded it didn't work. After additional investigation, I found &lt;strong&gt;missing configuration step&lt;/strong&gt;: S3 access must be explicitly enabled on the FlexCache volume:&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="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-privilege&lt;/span&gt; advanced
flexcache config modify &lt;span class="nt"&gt;-vserver&lt;/span&gt; &amp;lt;svm&amp;gt; &lt;span class="nt"&gt;-volume&lt;/span&gt; &amp;lt;fcache_vol&amp;gt; &lt;span class="nt"&gt;-is-s3-enabled&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After applying this setting:&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;Before (missing setting)&lt;/th&gt;
&lt;th&gt;After (-is-s3-enabled true)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HeadBucket&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ListObjectsV2&lt;/td&gt;
&lt;td&gt;❌ AccessDenied&lt;/td&gt;
&lt;td&gt;✅ KeyCount=1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetObject&lt;/td&gt;
&lt;td&gt;❌ AccessDenied&lt;/td&gt;
&lt;td&gt;✅ Content verified&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;fsxadmin&lt;/code&gt; role on FSx for ONTAP has access to advanced privilege commands including &lt;code&gt;flexcache config modify&lt;/code&gt;. Documented at &lt;a href="https://docs.netapp.com/us-en/ontap/flexcache/enable-flexcache-duality.html" rel="noopener noreferrer"&gt;NetApp: Enable FlexCache duality&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design Implication
&lt;/h3&gt;

&lt;p&gt;FlexCache duality works, which means S3 reads from the cache side are possible. However, this architecture still recommends &lt;strong&gt;NFS/SMB on the cache side&lt;/strong&gt; because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ONTAP native S3 (NAS buckets) and AWS-managed S3 Access Points are &lt;strong&gt;different mechanisms&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;NAS buckets are read-only (no PutObject)&lt;/li&gt;
&lt;li&gt;Requires additional configuration (advanced privilege + S3 user management)&lt;/li&gt;
&lt;li&gt;No IAM integration or access point policy governance like S3 AP provides&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FlexCache duality becomes an option when "S3 reads at the cache site" is a hard requirement. For AWS service integration (Lambda, Bedrock, etc.), the origin-side S3 AP remains the better fit. &lt;strong&gt;These are separate mechanisms, and the support status of one is not evidence for the other.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Pattern Fits
&lt;/h2&gt;

&lt;p&gt;"Collect via S3 API in the cloud, consume via NFS/SMB at the edge" — this structure exists across industries.&lt;/p&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;Collect side&lt;/th&gt;
&lt;th&gt;Consume side&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Automotive (AV/ADAS)&lt;/td&gt;
&lt;td&gt;Driving logs and sensor data ingested to S3&lt;/td&gt;
&lt;td&gt;HiL test benches replay via NFS&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/blogs/industries/accelerating-hil-testing-for-av-adas-with-a-hybrid-cloud-approach-aws-and-netapp/" rel="noopener noreferrer"&gt;AWS + NetApp: Hybrid Cloud HiL&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semiconductor (EDA)&lt;/td&gt;
&lt;td&gt;Design job I/O staged via S3&lt;/td&gt;
&lt;td&gt;Toolchains (Synopsys, Cadence) run on NFS&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/cn/blogs/industries/eda-scale-with-fsx-for-netapp-ontap-and-ibm-lsf/" rel="noopener noreferrer"&gt;EDA Scale with FSx for ONTAP&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media and VFX&lt;/td&gt;
&lt;td&gt;Rendering assets collected to S3&lt;/td&gt;
&lt;td&gt;Artist workstations mount SMB/NFS&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.netapp.com/data-storage/what-is-flex-cache/" rel="noopener noreferrer"&gt;FlexCache: distributed product development&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oil and Gas&lt;/td&gt;
&lt;td&gt;Seismic survey data uploaded to S3&lt;/td&gt;
&lt;td&gt;Interpretation workstations mount NFS&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/solutions/deploying-vdi-for-subsurface-oil-and-gas-on-aws/index.html" rel="noopener noreferrer"&gt;VDI for Subsurface O&amp;amp;G&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Life Sciences&lt;/td&gt;
&lt;td&gt;Genome sequencer output stored in S3&lt;/td&gt;
&lt;td&gt;Bioinformatics HPC processes via NFS&lt;/td&gt;
&lt;td&gt;Sequencer → S3 → NFS pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manufacturing / QA&lt;/td&gt;
&lt;td&gt;Inspection camera images collected to S3&lt;/td&gt;
&lt;td&gt;Line-side inspection software reads via NFS&lt;/td&gt;
&lt;td&gt;Image → judgment → archive flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote Work&lt;/td&gt;
&lt;td&gt;Central design data updated via S3&lt;/td&gt;
&lt;td&gt;Remote-site WorkSpaces access FlexCache NFS/SMB&lt;/td&gt;
&lt;td&gt;&lt;a href="https://community.netapp.com/t5/Tech-ONTAP-Blogs/Accelerating-Remote-Work-Harnessing-FlexCache-in-AWS-WorkSpaces-for-Data/ba-p/451852" rel="noopener noreferrer"&gt;FlexCache in AWS WorkSpaces&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IoT / Edge&lt;/td&gt;
&lt;td&gt;Sensor data streamed to S3&lt;/td&gt;
&lt;td&gt;On-site analysis appliances read via NFS&lt;/td&gt;
&lt;td&gt;Factory gateway → cloud → shopfloor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Common structure: &lt;strong&gt;Few write sites (often one), multiple read sites. Writes are bursty; reads touch only what's needed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When It Doesn't Fit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Need full S3 semantics&lt;/strong&gt; (versioning, event notifications, lifecycle): Use S3 natively&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object names aren't NAS-friendly&lt;/strong&gt; (flat namespace, millions of keys with no directory separators): Performance degrades as root directory grows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumers need S3 reads at the cache site&lt;/strong&gt;: possible from ONTAP 9.18.1 with duality (see above), but this architecture keeps the cache on NFS/SMB — the NAS bucket is a read-only view, needs advanced privilege, and has no IAM or access point policy governance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need write-back from the cache&lt;/strong&gt;: This pattern keeps cache read-centric&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need conditional writes (If-None-Match)&lt;/strong&gt;: Returns 501 NotImplemented. Handle exclusion at the application layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  S3 Access Point Design Notes
&lt;/h2&gt;

&lt;p&gt;A few things to know when using the S3 AP as the collect layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  S3 AP ≠ Amazon S3
&lt;/h3&gt;

&lt;p&gt;The FSx for ONTAP S3 AP supports a subset of S3 operations. GetObject, PutObject, ListObjectsV2, HeadObject, DeleteObject, and MultipartUpload work. The following do not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 Event Notifications (poll, or read the ONTAP native audit log. &lt;strong&gt;FPolicy + EventBridge is not a substitute&lt;/strong&gt; — see the next section)&lt;/li&gt;
&lt;li&gt;Lifecycle rules (use FabricPool)&lt;/li&gt;
&lt;li&gt;Versioning (use ONTAP Snapshots)&lt;/li&gt;
&lt;li&gt;Conditional writes If-None-Match (returns 501)&lt;/li&gt;
&lt;li&gt;S3 Select, SSE-S3/KMS, Cross-AP Copy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitoring Coverage — FPolicy Does Not See This Path
&lt;/h3&gt;

&lt;p&gt;An earlier version of this post offered FPolicy as the substitute for S3 Event Notifications.&lt;br&gt;
&lt;strong&gt;I measured it, and it does not work.&lt;/strong&gt; Correcting that here.&lt;/p&gt;

&lt;p&gt;Measured 2026-08-26, ap-northeast-1, ONTAP 9.18.1P3D1, with the same result for both a UNIX-identity&lt;br&gt;
and a WINDOWS-identity access point.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Operations through the S3 AP&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FPolicy&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Not notified&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zero notifications in a 90-second idle window, zero across nine S3 AP data-plane calls, while a file-protocol control on the same volume in the same session did fire. The protocols an FPolicy event accepts are &lt;code&gt;cifs&lt;/code&gt;, &lt;code&gt;nfsv3&lt;/code&gt; and &lt;code&gt;nfsv4&lt;/code&gt; only — there is no value for the S3 path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FPolicy &lt;code&gt;mandatory&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Not blocked&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;With a synchronous engine and &lt;code&gt;mandatory=true&lt;/code&gt;, an NFSv3 write returned &lt;code&gt;Permission denied&lt;/code&gt; while PUT, GET, LIST and DELETE through the S3 AP on the same volume all succeeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP native audit log&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Recorded&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;As &lt;code&gt;Source=HTTP&lt;/code&gt; for object operations and &lt;code&gt;Source=S3&lt;/code&gt; for LIST. But &lt;code&gt;SubjectUserName&lt;/code&gt; and &lt;code&gt;SubjectDomainName&lt;/code&gt; are &lt;code&gt;Not Present&lt;/code&gt; and &lt;code&gt;SubjectIP&lt;/code&gt; is an AWS service-side address, so &lt;strong&gt;the requester is not recorded&lt;/strong&gt;. &lt;code&gt;HeadObject&lt;/code&gt; produced nothing across six calls. An audit ACE (SACL) is required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARP&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Detected&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ARP 5.0. 150 high-entropy files written through the access point were recorded as suspect under &lt;code&gt;High Entropy&lt;/code&gt;, with &lt;code&gt;attack_probability&lt;/code&gt; at &lt;code&gt;moderate&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Two things follow for the design.&lt;/strong&gt; First, detection, DLP and blocking that start from an FPolicy&lt;br&gt;
notification do not reach this path — including the guarantee &lt;code&gt;mandatory&lt;/code&gt; is chosen for, that an&lt;br&gt;
unreachable engine stops the operation. Second, the audit log records the operation but not the&lt;br&gt;
requester, so answering "who touched this file" from the audit trail alone is not possible; it needs&lt;br&gt;
correlation with AWS CloudTrail.&lt;/p&gt;

&lt;p&gt;ARP does detect, but &lt;strong&gt;&lt;code&gt;attack_probability&lt;/code&gt; changes more than ten minutes after the write.&lt;/strong&gt; Reading&lt;br&gt;
&lt;code&gt;none&lt;/code&gt; from a short observation window and concluding nothing was detected is a false negative. ARP&lt;br&gt;
blocking is unmeasured.&lt;/p&gt;

&lt;p&gt;Whether FPolicy, auditing or ARP fire on the cache side is unverified. Writes in this architecture&lt;br&gt;
land on the origin, so the cache side remains a separate question.&lt;/p&gt;

&lt;h3&gt;
  
  
  Throughput is shared with NFS/SMB
&lt;/h3&gt;

&lt;p&gt;S3 AP, NFS, and SMB all consume the same FSx for ONTAP provisioned throughput. In this architecture, origin and cache are separate clusters so this rarely matters — but if NFS clients also access the origin directly, account for the shared bandwidth. I have not measured concurrency, so I will not hand you a number: the ceiling follows from provisioned throughput divided by the bandwidth one request consumes, and that second term depends on your object size and request duration. Raise concurrency while watching the &lt;code&gt;SlowDown&lt;/code&gt; (503) rate and p99, and stop below what you can absorb.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That reading is for the first generation (SINGLE_AZ_1). On the second generation the value specified means something else.&lt;/strong&gt; It names the disk throughput &lt;strong&gt;baseline&lt;/strong&gt;, and the network baseline is about twice it. On a file system specified at 1,536 MBps, a 1 MiB sequential read delivered &lt;strong&gt;2,882 MB/s for 27 minutes (1.88x the value specified) and then 1,439 MB/s (0.94x)&lt;/strong&gt;. The drop happens inside a single 10-second interval once the burst allowance runs out, so &lt;strong&gt;a five-minute measurement reports 2.0x too high.&lt;/strong&gt; Treating the value specified as the ceiling puts the estimate out in either direction (&lt;a href="https://github.com/Yoshiki0705/S3-Burst-on-ONTAP-Files/blob/main/docs/ja/verification/throughput-capacity-burst-and-baseline.md" rel="noopener noreferrer"&gt;measurement record&lt;/a&gt;, Japanese).&lt;/p&gt;

&lt;h3&gt;
  
  
  Directory design matters
&lt;/h3&gt;

&lt;p&gt;S3 PutObject keys map directly to directory structure. What sets the ceiling is the volume's &lt;code&gt;maxdir-size&lt;/code&gt;: reach it and the client gets &lt;code&gt;ENOSPC&lt;/code&gt; and can no longer create files. It is a per-volume setting, and raising it &lt;a href="https://docs.netapp.com/us-en/ontap/volumes/cautions-increasing-maximum-directory-size-concept.html" rel="noopener noreferrer"&gt;could affect performance&lt;/a&gt;. Check the value on your own volume and partition by date, tenant, or hash prefix so the entries in one directory stay well short of it. Response time for &lt;code&gt;readdir&lt;/code&gt; and &lt;code&gt;ListObjectsV2&lt;/code&gt; grows with entry count too, though I have not measured where that starts to hurt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This directly affects NFS usability.&lt;/strong&gt; If you flat-dump millions of objects without hierarchy, FlexCache NFS clients will struggle with &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;find&lt;/code&gt;. Design your S3 keys with "how does this look when I &lt;code&gt;ls&lt;/code&gt; on NFS?" in mind.&lt;/p&gt;

&lt;p&gt;Key recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Date-based partitioning (&lt;code&gt;year=YYYY/month=MM/day=DD/&lt;/code&gt;) so the entries in one directory stay well short of &lt;code&gt;maxdir-size&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Separate ingest volumes from consumption volumes; apply FlexCache only to consumption&lt;/li&gt;
&lt;li&gt;On the NFS side, use manifest files or path generation instead of directory traversal (&lt;code&gt;find&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For full details, see the &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/ja/reference/limits/s3ap-design-guide.md" rel="noopener noreferrer"&gt;S3 AP Design Guide&lt;/a&gt; in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Costs — Egress First, Requests Second
&lt;/h2&gt;

&lt;p&gt;I framed this section around S3 request pricing when I first wrote it. That was the wrong axis, and the modelling I did afterwards says so plainly. Corrected below.&lt;/p&gt;

&lt;p&gt;The cost that hurts when your readers sit outside AWS is &lt;strong&gt;data transfer&lt;/strong&gt;. Egress is charged on bytes leaving the Region, so reading the same file ten times moves ten times the bytes and pays ten times over. A cache removes the multiplier: it carries the working set once and every later read is served locally over NFS or SMB, never becoming an S3 request at all.&lt;/p&gt;

&lt;p&gt;A worked case — 20 TiB dataset, 2 TiB monthly working set, 4 MiB objects, each file read 30 times, internet egress from &lt;code&gt;ap-northeast-1&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;Option&lt;/th&gt;
&lt;th&gt;Monthly&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read S3 directly from on premises&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$6,211&lt;/strong&gt; — egress alone is $5,693 (92%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy the whole dataset down with DataSync&lt;/td&gt;
&lt;td&gt;$2,847&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP + FlexCache&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$1,333&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Storage and requests are rounding errors next to transfer here.&lt;/p&gt;

&lt;h3&gt;
  
  
  The read count decides it
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reads per file per month&lt;/th&gt;
&lt;th&gt;Direct&lt;/th&gt;
&lt;th&gt;This architecture&lt;/th&gt;
&lt;th&gt;Ratio&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;$746&lt;/td&gt;
&lt;td&gt;$1,333&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.6x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;$1,680&lt;/td&gt;
&lt;td&gt;$1,333&lt;/td&gt;
&lt;td&gt;1.3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;$2,593&lt;/td&gt;
&lt;td&gt;$1,333&lt;/td&gt;
&lt;td&gt;1.9x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;$6,211&lt;/td&gt;
&lt;td&gt;$1,333&lt;/td&gt;
&lt;td&gt;4.7x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;$18,451&lt;/td&gt;
&lt;td&gt;$1,333&lt;/td&gt;
&lt;td&gt;13.8x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At one read per file the direct path wins — the same bytes move either way and there is no reason to carry a file system's fixed cost. The crossover is between five and ten. &lt;strong&gt;Where it falls is a property of your workload, not of the products.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Requests matter too, but only below a certain object size
&lt;/h3&gt;

&lt;p&gt;I had assumed S3 GET charges were a co-equal problem. At 4 MiB objects they are not: the entire 5.2 million reads in the ten-read case cost &lt;strong&gt;$1.94&lt;/strong&gt;, about a thousandth of the transfer bill. Through an access point it is $0.31, and through FlexCache $0.16 — the reads are NFS and SMB, so they generate no S3 requests, and only the origin-side capacity pool fetch on cache fill remains.&lt;/p&gt;

&lt;p&gt;Where the assumption does hold is small objects. Holding bytes read constant and varying object size:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object size&lt;/th&gt;
&lt;th&gt;Reads/month&lt;/th&gt;
&lt;th&gt;GET charges&lt;/th&gt;
&lt;th&gt;Share of that option's transfer bill&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8 KiB&lt;/td&gt;
&lt;td&gt;2.68 B&lt;/td&gt;
&lt;td&gt;$993&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;48%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;64 KiB&lt;/td&gt;
&lt;td&gt;336 M&lt;/td&gt;
&lt;td&gt;$124&lt;/td&gt;
&lt;td&gt;6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;256 KiB&lt;/td&gt;
&lt;td&gt;84 M&lt;/td&gt;
&lt;td&gt;$31&lt;/td&gt;
&lt;td&gt;1.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 MiB&lt;/td&gt;
&lt;td&gt;5.2 M&lt;/td&gt;
&lt;td&gt;$1.94&lt;/td&gt;
&lt;td&gt;0.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So both charges need designing for, and they respond to opposite remedies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transfer&lt;/strong&gt; falls by carrying fewer bytes — cache the working set, move the readers into AWS, or drop the unit rate with Direct Connect ($0.041/GB against $0.114 for the first 10 TB of internet egress). Making objects bigger does &lt;em&gt;not&lt;/em&gt; help; the byte count is unchanged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests&lt;/strong&gt; fall by making fewer calls — batch at collection time so files are larger, and serve reads over a file protocol so they never become S3 calls. Negotiating transfer rates does not help when the money is on the request side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Batch too far and you hit the collection side: a single &lt;code&gt;PutObject&lt;/code&gt; caps at 5 GiB and a whole object at 50 GiB, and the 50 GiB check happens at &lt;code&gt;CompleteMultipartUpload&lt;/code&gt; — after the entire payload has been transferred and paid for. Both figures being binary (5,368,709,120 and 53,687,091,200 bytes) is confirmed by the vendor. When this was published the documentation still read "5 GB" and "50 GB" and a correction was said to be in progress; &lt;strong&gt;checking again on 2026-09-11, the 50 GiB figure now reads in GiB&lt;/strong&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 support&lt;/a&gt;). &lt;strong&gt;The page stating the 5 GiB single-&lt;code&gt;PutObject&lt;/code&gt; limit, however, was not found that day.&lt;/strong&gt; "Not found" is not "does not exist", so that one rests on the measurement alone. The increase from 5 GiB to 50 GiB was also never announced through any channel. &lt;strong&gt;An unannounced tenfold change leaves every downstream document that quotes the old value wrong, with no signal to re-check.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And that 50 GiB caps uploads, not retrievals.&lt;/strong&gt; The documentation reads "Maximum object size is 50 GiB for uploads, but you can download objects larger than that". &lt;strong&gt;A file larger than 50 GiB cannot be created over S3, but one created over NFS / SMB can be read over S3.&lt;/strong&gt; Even with collection centred on S3, writing just the large files on the file side is a documented way out. &lt;strong&gt;I have not measured this.&lt;/strong&gt; That the documentation says so, and that a file larger than 50 GiB was created over NFS and fetched with &lt;code&gt;GetObject&lt;/code&gt;, are different claims. Batching past the consumer's read unit also sends bytes nobody reads. The repository's &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/ja/reference/limits/s3ap-design-guide.md" rel="noopener noreferrer"&gt;design guide&lt;/a&gt; sets the consumer's read unit as the reference for how far to go, with the measurement and monitoring steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  If the readers can move, move them
&lt;/h3&gt;

&lt;p&gt;Worth stating because it outweighs every storage choice. In-Region transfer is free, so putting the consumers in AWS deletes the entire egress line — $2,079 at ten reads on this workload. Reading S3 directly from EC2 comes to $514/month; &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-files.html" rel="noopener noreferrer"&gt;S3 Files&lt;/a&gt; costs $515, ninety-four cents more, because 4 MiB objects sit above its size threshold and never reach its high-performance storage; FSx for ONTAP in the same Region is $1,053, twice the direct path, since the transfer gap that justified it is gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Files cannot serve this architecture's consumers.&lt;/strong&gt; It speaks NFSv4.1 and NFSv4.2 only — no NFSv3, no SMB — so an appliance pinned to v3 and any Windows stage are out, and the documented compute targets are EC2, Lambda, EKS and ECS. It belongs in the picture as the option you get &lt;em&gt;after&lt;/em&gt; migrating the readers, not as a substitute for a cache.&lt;/p&gt;

&lt;p&gt;This architecture is for the cases where the readers cannot move: equipment on site, proximity to whatever is being measured, capital already spent on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  On the distribution side
&lt;/h3&gt;

&lt;p&gt;A FlexCache cache volume &lt;strong&gt;cannot be tiered&lt;/strong&gt; — an origin with FabricPool tiering can be cached, but the cache itself never tiers, so it sits entirely on SSD. That is affordable because it is sparse: NetApp's guidance is at least 10% of origin, which is also the create default. At 10% the distribution side runs 2.7x to 6.1x below a full second copy, with the copy given its capacity pool discount. Size it like a copy and it inverts — at 100% it costs more than the copy, precisely because it cannot tier.&lt;/p&gt;

&lt;p&gt;Cache volumes are writable, which I understated earlier. Write-around is the default and withholds the client acknowledgement until the origin has committed; write-back, from ONTAP 9.15.1, commits at the cache and propagates asynchronously. The default being synchronous with respect to the origin matters for freshness: there is no window where a cache-side write is missing from the canonical copy.&lt;/p&gt;

&lt;p&gt;One assumption drives more of these numbers than any other, so treat it carefully. &lt;strong&gt;Background storage efficiency does not run on data once it has been tiered&lt;/strong&gt; — only savings applied while the block was on SSD are preserved, and a block tiered before efficiency ran keeps none (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/manage-vol-SE.html" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, &lt;a href="https://kb.netapp.com/Advice_and_Troubleshooting/Data_Storage_Software/ONTAP_OS/Does_ONTAP_apply_efficiencies_to_blocks_that_are_tiered-out_to_Fabricpool%3F" rel="noopener noreferrer"&gt;NetApp&lt;/a&gt;). Expecting better than 40% on a tiering-enabled volume is optimistic. The model takes the SSD rate from AWS's published per-workload figures, assumes the pool tier retains half of it, and carries a sensitivity table — because the assumption only ever flatters this architecture: ONTAP deduplication does not reduce an S3 storage bill.&lt;/p&gt;

&lt;p&gt;Full breakdown, every rate with its effective date, the workload models and the cross-layer pitfalls: &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/ja/reference/comparison/finops-s3-vs-s3ap.md" rel="noopener noreferrer"&gt;FinOps cost structure&lt;/a&gt; and the &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/ja/reference/limits/s3ap-design-guide.md" rel="noopener noreferrer"&gt;S3 AP Design Guide&lt;/a&gt; (Japanese). Cost tables are generated from a model in the repository, so a price change moves one declaration rather than fifty typed totals.&lt;/p&gt;

&lt;h2&gt;
  
  
  A verification copy that costs no capacity — FlexClone and the S3 Access Point
&lt;/h2&gt;

&lt;p&gt;Everything above is about distribution, so I measured duplication as well.&lt;/p&gt;

&lt;p&gt;I checked whether FlexClone works on a volume with an access point attached (2026-08-26, ap-northeast-1, ONTAP 9.18.1P3D1). NetApp records FlexClone as unsupported for ONTAP S3, so I expected the same restriction might appear on this path, but it did not appear as a restriction.&lt;/p&gt;

&lt;p&gt;FlexClone has two granularities, and they behave quite differently.&lt;/p&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;File granularity&lt;/th&gt;
&lt;th&gt;Volume granularity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where it lands&lt;/td&gt;
&lt;td&gt;The same volume. It appears as a new key on the same access point&lt;/td&gt;
&lt;td&gt;A separate volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization boundary&lt;/td&gt;
&lt;td&gt;Same as the parent&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Its own access point, its own policy, its own identity&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time before it is usable&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;A volume created through the ONTAP API took 599 to 1,177 s to appear on the AWS side, then the junction path has to be set before attaching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visible over S3&lt;/td&gt;
&lt;td&gt;Yes. &lt;code&gt;StorageClass=FSX_ONTAP&lt;/code&gt;, sha256 matches the source&lt;/td&gt;
&lt;td&gt;Yes. LIST / GET / PUT through the clone's own access point&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At either granularity the clone shares data blocks with its parent. Cloning a 256 MiB file four times gives these figures:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Point&lt;/th&gt;
&lt;th&gt;Logical&lt;/th&gt;
&lt;th&gt;Physical&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Just after one 256 MiB PUT&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;253,534,208 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After four file-granularity clones&lt;/td&gt;
&lt;td&gt;1,350,942,720 B&lt;/td&gt;
&lt;td&gt;277,200,896 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Logical grew to five copies, but physical grew by only about 23 MB.&lt;/p&gt;

&lt;h3&gt;
  
  
  FinOps — no capacity to provision per duplicate
&lt;/h3&gt;

&lt;p&gt;FSx for ONTAP bills &lt;strong&gt;provisioned SSD capacity&lt;/strong&gt;. If a duplicate shares blocks with its parent, there is no capacity to provision for it. Doing the same on S3 requires &lt;code&gt;CopyObject&lt;/code&gt;, every duplicate carries full storage charges, and the copy requests are billed on top. That difference is structural, so a unit rate does not close it.&lt;/p&gt;

&lt;p&gt;The saving does reach the invoice only as capacity you did not have to provision, though. Where headroom is already generous, the figure does not move.&lt;/p&gt;

&lt;p&gt;Two notes on what the space figures assume. NetApp states that copies consume no storage except what is required for metadata until changes are written to the copy (&lt;a href="https://docs.netapp.com/us-en/ontap/concepts/flexclone-volumes-files-luns-concept.html" rel="noopener noreferrer"&gt;FlexClone volumes, files, and LUNs&lt;/a&gt;), so &lt;strong&gt;changes written to the clone are not shared and consume new blocks for whatever was written&lt;/strong&gt;. And deleting data on the parent does not return capacity while a clone or a snapshot still references it.&lt;/p&gt;

&lt;p&gt;The operation that ends the sharing is a &lt;strong&gt;split&lt;/strong&gt; (&lt;code&gt;volume clone split start&lt;/code&gt;). I did not measure it, so this is the documentation: from ONTAP 9.4, on AFF systems where the volume guarantee is &lt;code&gt;none&lt;/code&gt;, the split shares the physical blocks rather than copying the data, and space efficiency is preserved. After the split, however, both the parent and the clone require the full space allocation set by their volume guarantees. The space needed can be checked beforehand with &lt;code&gt;volume clone show -estimate&lt;/code&gt; (&lt;a href="https://docs.netapp.com/us-en/ontap/volumes/split-flexclone-from-parent-task.html" rel="noopener noreferrer"&gt;splitting procedure&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Excellence — no AWS-side work per duplicate
&lt;/h3&gt;

&lt;p&gt;What helps operationally is that making a duplicate adds no AWS-side resource. A file-granularity clone appears as a new key under the existing access point, so there is nothing to attach and nothing to wait for. A verification dataset can be produced from the production bytes and handed to the S3 consumers already in place, such as Amazon Bedrock, AWS Glue and Amazon Athena, without a staging copy job.&lt;/p&gt;

&lt;p&gt;Where separation is needed, volume granularity is the one to use. Attaching a separate access point to the clone keeps its identity and policy apart from the parent's, and a write to the clone did not appear on the parent. That gives a way to separate the authorization boundary without duplicating the bytes. The cost is the 10-to-20-minute wait noted above.&lt;/p&gt;

&lt;p&gt;There are two operational cautions as well.&lt;/p&gt;

&lt;p&gt;The first is that &lt;strong&gt;a file-granularity clone gives no observable failure&lt;/strong&gt;. &lt;code&gt;POST /api/storage/file/clone&lt;/code&gt; returns 202 and a job UUID, but the UUID resolved to &lt;code&gt;404 entry doesn't exist&lt;/code&gt; and appeared in no job listing. The same &lt;code&gt;fsxadmin&lt;/code&gt; retrieves volume-create and volume-clone jobs as &lt;code&gt;state=success&lt;/code&gt;, so this is not a permissions problem. A call naming a destination directory that does not exist also returned 202 and created nothing. Judge the outcome by inspecting the destination file.&lt;/p&gt;

&lt;p&gt;The second is that &lt;strong&gt;teardown gains an ordering constraint&lt;/strong&gt;. Delete the clone before the parent. While the deleted clone sits in ONTAP's volume recovery queue, the parent keeps &lt;code&gt;clone.has_flexclone&lt;/code&gt; at &lt;code&gt;true&lt;/code&gt;, and the AWS-side &lt;code&gt;delete-volume&lt;/code&gt; returned &lt;code&gt;DELETING&lt;/code&gt; and then silently went back to &lt;code&gt;CREATED&lt;/code&gt;. Purging the recovery queue clears the flag and the same call works. That queue appears in neither the console nor the FSx for ONTAP API, so watching only the AWS side leaves you stuck here.&lt;/p&gt;

&lt;p&gt;What I did not measure is LUN-granularity cloning: creating a LUN needs an iSCSI configuration, which is not on this architecture's path. The procedure and the controls are in the repository's &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/en/reference/limits/s3ap-interoperability.md" rel="noopener noreferrer"&gt;interoperability&lt;/a&gt; page, and the cost structure in &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/en/reference/comparison/finops-s3-vs-s3ap.md" rel="noopener noreferrer"&gt;FinOps cost structure&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;CloudFormation and Terraform templates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files" rel="noopener noreferrer"&gt;Yoshiki0705/s3-burst-on-ontap-files&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;"Collect via S3, consume via NFS" — one volume, no separate copy or replication job. FSx for ONTAP S3 Access Point + FlexCache. Main path p50 8 ms. FlexCache adds ~5 ms for same-region — nearly transparent. Transfer still happens for whatever gets read; what is absent is the sync job.&lt;/p&gt;

&lt;p&gt;This verification was done entirely on AWS (FSx for ONTAP to FSx for ONTAP over VPC peering), but the cache side isn't limited to AWS. AWS documents exactly three FlexCache configurations, and with FSx for ONTAP as the origin the cache is either on-premises ONTAP or FSx for ONTAP (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/using-flexcache.html" rel="noopener noreferrer"&gt;replicating with FlexCache&lt;/a&gt;). Cloud Volumes ONTAP, ONTAP Select, Azure NetApp Files and Google Cloud NetApp Volumes are absent from that table, so I record them as unconfirmed rather than assuming they work because they are ONTAP-based — and unconfirmed is not the same as unsupported. The repository's &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/ja/portability.md" rel="noopener noreferrer"&gt;Portability&lt;/a&gt; and &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/ja/support-matrix.md" rel="noopener noreferrer"&gt;Support Matrix&lt;/a&gt; pages track what's confirmed and what's next. Cross-platform verification is on the roadmap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reverse direction is outside that table too,&lt;/strong&gt; and &lt;strong&gt;the verdict splits two ways.&lt;/strong&gt; Another&lt;br&gt;
cloud's file storage as the origin with FSx for ONTAP as the cache is &lt;em&gt;unconfirmed&lt;/em&gt; for Google Cloud&lt;br&gt;
NetApp Volumes and Azure NetApp Files — something that looking could still resolve. For Google Cloud&lt;br&gt;
Filestore, Azure Managed Lustre, Azure Blob NFS and OCI File Storage it is &lt;strong&gt;out of scope as a&lt;br&gt;
mechanism&lt;/strong&gt;, because they are not ONTAP and the cluster and SVM peering FlexCache requires cannot&lt;br&gt;
exist. Network reachability does not change the second group. &lt;strong&gt;I do not write the two with the same&lt;br&gt;
word.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wrote up the network underneath it separately. AWS Interconnect – multicloud is GA for Google&lt;br&gt;
Cloud (eight pairs) and OCI (one pair, us-east-1 to us-ashburn-1), and at Preview for Azure since&lt;br&gt;
2026-08 with four pairs. &lt;strong&gt;No Japanese Region appears in any CSP's pairs&lt;/strong&gt;, and &lt;strong&gt;a pair being listed&lt;br&gt;
is not the same as that pair being GA.&lt;/strong&gt; Azure also has a facing service, Azure Multicloud&lt;br&gt;
Interconnect, though the only sources I found are Microsoft blogs rather than the Microsoft Learn&lt;br&gt;
reference. Starting from Japan therefore means joining Direct Connect to the other cloud's circuit&lt;br&gt;
inside an interconnection provider's fabric, and whether that is possible is decided not by the&lt;br&gt;
Region-pair table but by whether Direct Connect locations, the other cloud's connection locations and&lt;br&gt;
the provider's footprint overlap. &lt;strong&gt;Taking the partner route does not add Regions to the managed&lt;br&gt;
service&lt;/strong&gt; — it switches to a different construction.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fno8ir37irt31l0jya435.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fno8ir37irt31l0jya435.png" alt="Cross-cloud connectivity" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The arrows in the figure stop at the AWS VPC. &lt;strong&gt;Not drawing the next hop as a solid edge is what the figure is for.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same user guide carries &lt;code&gt;AWS Interconnect – last mile&lt;/code&gt;. &lt;strong&gt;The name is close, but it does not&lt;br&gt;
join two CSPs: it brings a carrier circuit to AWS.&lt;/strong&gt; It is offered with Lumen, from the New Jersey&lt;br&gt;
sites in us-east-1 to any AWS Region, or from anywhere in the continental United States over Lumen's&lt;br&gt;
fabric (&lt;a href="https://docs.aws.amazon.com/interconnect/latest/userguide/region-availability.html" rel="noopener noreferrer"&gt;Regional Availability&lt;/a&gt;).&lt;br&gt;
Where the serve side sits at a site it can form part of the path, but &lt;strong&gt;it is a different axis, so it&lt;br&gt;
is not among the three ways in the figure above.&lt;/strong&gt; The stage is documented; I have not measured it.&lt;/p&gt;

&lt;p&gt;Encryption also sits at two layers worth keeping apart. MACsec on the physical link and cluster&lt;br&gt;
peering encryption over FlexCache traffic (ONTAP 9.6 or later, TLS 1.2 AES-256 GCM) are different&lt;br&gt;
things, and &lt;strong&gt;the first does not remove the need for the second.&lt;/strong&gt; Whether ONTAP itself offers MACsec&lt;br&gt;
on an intercluster LIF is something I could find no statement for. The options and the Region&lt;br&gt;
coverage are in &lt;a href="https://github.com/Yoshiki0705/s3-burst-on-ontap-files/blob/main/docs/en/multi-cloud-connectivity.md" rel="noopener noreferrer"&gt;cross-cloud connectivity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;FlexCache duality (S3 reads on the cache side) does work, once S3 access is enabled on the cache volume itself with &lt;code&gt;-is-s3-enabled true&lt;/code&gt; in advanced privilege. The earlier &lt;code&gt;AccessDenied&lt;/code&gt; was a missing setting, not a platform limitation. The architecture still keeps cache-side access to NFS/SMB, because ONTAP native S3 and the AWS-managed access point are separate mechanisms and the NAS bucket is a read-only view without IAM integration.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All test resources torn down. Numbers are from a specific test environment and vary by workload and configuration.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>fsxforontap</category>
      <category>s3accesspoints</category>
      <category>fileburst</category>
    </item>
    <item>
      <title>Letting an AI agent operate on FSx for ONTAP data — AgentCore MCP, with a human approving (File Portal Part 3)</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Mon, 03 Aug 2026 10:21:40 +0000</pubDate>
      <link>https://dev.to/aws-builders/embedding-ai-agents-into-a-file-portal-from-agentcore-mcp-to-multi-agent-teams-part-3-19m1</link>
      <guid>https://dev.to/aws-builders/embedding-ai-agents-into-a-file-portal-from-agentcore-mcp-to-multi-agent-teams-part-3-19m1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Let an AI agent work the NAS, with a human approving&lt;/strong&gt; — Amazon Bedrock AgentCore and MCP (part 3 of 6)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Japanese version of this article is available: &lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-file-portal-3-ai-agent-mcp" rel="noopener noreferrer"&gt;日本語版&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887"&gt;Part 1&lt;/a&gt; I built the file portal foundation on the S3 Access Points of Amazon FSx for NetApp ONTAP (hereafter FSx for ONTAP), and in &lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-file-portal-2-ransomware-worm" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt; I embedded storage operations (ARP/AI incident response, Tamperproof Snapshots, regulatory retention management). At that point the UI was sufficient for anyone who already knew which button to press — but a different kind of friction was still there. "Where did I put last week's simulation results?" "Summarize the key points of this PDF in three lines." The intent is perfectly clear; the cost of translating it into system operations was what stayed high.&lt;/p&gt;

&lt;p&gt;So I integrated AI agents into the portal. Express your intent in natural language, and the agent finds the files, reads them, analyzes the content, and proposes storage management operations when needed. Because storage management includes irreversible operations, the execution decision stays with a human through a Human-in-the-Loop (HITL) flow.&lt;/p&gt;

&lt;p&gt;Here's the conclusion up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Combining the Amazon Bedrock Converse API with MCP tools lets you explore and analyze files on an S3 AP in natural language&lt;/li&gt;
&lt;li&gt;Connecting a Bedrock Knowledge Base directly to an S3 AP means you can reach a file through semantic search without knowing its name&lt;/li&gt;
&lt;li&gt;Wrapping destructive operations in a HITL approval modal keeps agent autonomy and safety in the same design&lt;/li&gt;
&lt;li&gt;Multi-agent collaboration pays off only for tasks with several phases — "discover → analyze → judge"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I'll cover the 3-mode AgentChat design, file access via MCP tools, the HITL flow, and the implementation patterns for multi-agent collaboration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who this is for&lt;/strong&gt;: anyone who wants to run an AI agent against files on a NAS, and anyone interested in putting human approval in front of a destructive operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this article does not cover&lt;/strong&gt;: quality or latency comparisons between models (not measured), the cost of embeddings, and confirmation of the agents through real browser operation — as the verification status below records, they sit in the "automated tests only" category.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9601cfniv0xfuub2v4wk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9601cfniv0xfuub2v4wk.png" alt="The overall architecture for adding AI agents to the file portal. The web browser reaches an agent-execution AWS Lambda through AWS Amplify and AWS AppSync; that Lambda runs inference on Amazon Bedrock (Converse API) and calls Amazon Bedrock AgentCore over MCP. The MCP tool Lambda that AgentCore invokes reads files on Amazon FSx for NetApp ONTAP through an Amazon S3 Access Point using the S3 API" width="800" height="1143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part3-ai-agent-overview-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available, and every figure is listed in the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/architecture-diagrams.en.md" rel="noopener noreferrer"&gt;architecture diagram index&lt;/a&gt;.&lt;/em&gt;&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/tree/main/solutions/amplify-portal" rel="noopener noreferrer"&gt;solutions/amplify-portal/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Terminology note&lt;/strong&gt;: "MCP" in this article refers to &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; — a standard protocol that allows AI models to invoke external tools (file operations, database queries, etc.). AgentCore Gateway acts as the MCP server, exposing Lambda-based tools to the AI model.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Where this agent sits
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It does not replace a general-purpose AI assistant.&lt;/strong&gt; General question answering is better served there. What is added here is discovery, content understanding and storage-operation proposals against the files on the NAS.&lt;/p&gt;

&lt;p&gt;The properties on this side:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files are reached on the NAS directly through the S3 AP. No upload&lt;/li&gt;
&lt;li&gt;The scope is the whole volume, searchable across it&lt;/li&gt;
&lt;li&gt;Storage operations (enabling SnapLock, blocking a user) can be proposed, and executing one goes through HITL approval&lt;/li&gt;
&lt;li&gt;ONTAP-side operations are available: ARP/AI status, snapshot operations, export-policy changes&lt;/li&gt;
&lt;li&gt;File contents are processed by Bedrock in the same Region&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Verification status&lt;/strong&gt;: how far each feature in this article has actually been confirmed is recorded in &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/solutions/amplify-portal/docs/verification-results.en.md" rel="noopener noreferrer"&gt;verification results&lt;/a&gt;, split four ways (live E2E / live read / tests only / DemoMode). Agent and team execution sits in the "tests only" category: handler and component tests pass, but it has not been driven from a browser against a real system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;The portal moved from "browse files and check operational state" to "ask in natural language, and the agent finds files, understands content, and proposes storage operations." Here's what was added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AgentChat&lt;/strong&gt;: Multi-tool AI chat with 3 switchable modes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SemanticSearch&lt;/strong&gt;: Vector search via Bedrock Knowledge Base — resolves "where did I put that file?" in natural language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent Directory / Creator&lt;/strong&gt;: Define custom agents and share them with your team&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent Teams&lt;/strong&gt;: Run multiple agents in collaboration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ActionApproval (HITL)&lt;/strong&gt;: Insert a human approval flow before destructive operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal input&lt;/strong&gt;: Drag and drop an image into the chat for file analysis&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Embed AI Agents in the Portal
&lt;/h2&gt;

&lt;p&gt;The admin features from Part 2 work well for people who already know "what to operate and how." But what I actually hear in practice sounds more like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Where did I put last week's simulation results?"&lt;/li&gt;
&lt;li&gt;"Can you summarize what caused the error in this log file?"&lt;/li&gt;
&lt;li&gt;"I want to run legal review on the contracts folder, but I don't know the steps"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't cases of "not knowing how to operate the system." They're cases of "I know what I want, but translating that intent into system operations is tedious." This applies equally to non-technical team members:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Where's the attachment for last month's expense report?"&lt;/li&gt;
&lt;li&gt;"Summarize this PDF in 3 lines"&lt;/li&gt;
&lt;li&gt;"Compile a file list from this folder for my weekly report"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The motivation for embedding AI agents was to eliminate this translation cost entirely. Express your intent in natural language, and the agent finds files, reads them, analyzes content, and proposes admin operations if needed. Users only need to think about "what they want to accomplish."&lt;/p&gt;

&lt;p&gt;However, giving an agent unrestricted authority in a storage management context is dangerous. Enabling SnapLock is irreversible. Modifying an export-policy immediately cuts off user access. So I combine the Human-in-the-Loop (HITL) pattern: "the agent proposes, but humans approve execution."&lt;/p&gt;




&lt;h2&gt;
  
  
  AgentChat — 3-Mode Integrated Chat
&lt;/h2&gt;

&lt;p&gt;A persistent AI chat in the portal's right panel, fully replacing the previous "Bedrock Q&amp;amp;A" (single-file question-answering).&lt;/p&gt;

&lt;h3&gt;
  
  
  3 Modes
&lt;/h3&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;Icon&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;🧠 Knowledge&lt;/td&gt;
&lt;td&gt;KB&lt;/td&gt;
&lt;td&gt;Cross-volume semantic search. Answers "where's that file?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📁 File Agent&lt;/td&gt;
&lt;td&gt;agent&lt;/td&gt;
&lt;td&gt;File read/list/analyze. Accesses S3 AP via MCP tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🤖 Multi-Agent&lt;/td&gt;
&lt;td&gt;multi&lt;/td&gt;
&lt;td&gt;Multiple agents collaborate. File discovery → content analysis → operation proposal in sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Modes switch via pill buttons below the header. Welcome screen task cards change per mode, showing "what you can do in this mode."&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser (React)
    ↓ AppSync Query (agentQuery)
Lambda: Agent Orchestrator (outside VPC)
    ├── mode=kb   → Bedrock KB RetrieveAndGenerate
    ├── mode=agent → Bedrock Converse + MCP Tools
    └── mode=multi → Multi-agent collaboration
                         ↓ MCP Client
                    AgentCore Gateway
                         ↓ Lambda Invoke
                    MCP Tool Lambda (list_files / read_file / search_files)
                         ↓ S3 API
                    FSx for ONTAP S3 Access Point
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftqfwn0jvb0ipij4wos8m.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftqfwn0jvb0ipij4wos8m.png" alt="The three AgentChat modes and file access via MCP tools. A request arriving at the AgentChat AWS Lambda from AWS AppSync branches into mode=kb (semantic search only, using kb_search against Amazon Bedrock Knowledge Bases), mode=agent (file tools only), and mode=multi (all tools, coordinated). The latter two go through Amazon Bedrock and Amazon Bedrock AgentCore to the MCP tool Lambda, which runs list / read / search against the Amazon S3 Access Point" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part3-agentchat-modes-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The VPC split principle applies here too. The Agent Orchestrator Lambda is placed outside VPC, accessing Bedrock API and S3 AP (Internet-origin). Admin operations calling ONTAP REST API are handled by a separate VPC-internal Lambda.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool Execution Visibility
&lt;/h3&gt;

&lt;p&gt;When the agent invokes tools, a timeline appears in the chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔧 Tool Calls (3)                          2.1s
├── ✅ 🔍 file-explorer: list_files         {"prefix": "/engineering/"}
├── ✅ 📄 file-explorer: read_file          {"key": "/engineering/thermal-spec-v3.pdf"}
└── ✅ 🧠 knowledge-analyst: analyze_file   {"key": "...", "question": "..."}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tool's input/output examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Input Example&lt;/th&gt;
&lt;th&gt;Output Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_files&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"prefix": "/engineering/", "max_keys": 20}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File path list + size + last modified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;read_file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"key": "/engineering/thermal-spec-v3.pdf"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File content (text-extracted)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_files&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"query": "thermal limit exceeded"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matched files + relevant snippets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Displayed in &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; — expanded by default when 3 or fewer tool calls, collapsed when 4+. This balances Nielsen's "visibility of system status" with Wroblewski's "progressive disclosure."&lt;/p&gt;

&lt;h3&gt;
  
  
  Chat History Persistence
&lt;/h3&gt;

&lt;p&gt;Conversation sessions auto-save to DynamoDB (2-second debounce). Past sessions can be recalled from the history panel to continue questioning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Auto-save: 2 seconds after message change → DynamoDB&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;currentSessionId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;saveCurrentSession&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;2000&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&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="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentSessionId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rationale: forcing an explicit "Save" button interrupts the natural flow of chat. Close the browser, and next time you open it, you can pick up from "where we left off yesterday."&lt;/p&gt;

&lt;h3&gt;
  
  
  Multimodal Input
&lt;/h3&gt;

&lt;p&gt;Drag-and-drop images into the chat for Bedrock Vision analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Circuit diagram image → "Where's the bottleneck in this circuit?"&lt;/li&gt;
&lt;li&gt;Screenshot → "What's causing this error screen?"&lt;/li&gt;
&lt;li&gt;Graph image → "Explain this performance degradation trend"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supports JPEG/PNG/GIF/WebP under 5MB. Base64-encoded and sent to Bedrock Converse API.&lt;/p&gt;




&lt;h2&gt;
  
  
  SemanticSearch — Vector Search
&lt;/h2&gt;

&lt;p&gt;Keyword search on file names has limits. When you want to find "the spec document about thermal design limits," you might find &lt;code&gt;thermal-spec-v3.pdf&lt;/code&gt; by name, but &lt;code&gt;TC-2024-0089.pdf&lt;/code&gt;? No chance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keyword / Semantic Mode Toggle
&lt;/h3&gt;

&lt;p&gt;Mode toggle pills in the search bar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔍 [Search files...                        ] [🔍]
   [📂 Keyword]  [🧠 Semantic]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyword mode&lt;/strong&gt;: S3 AP ListObjectsV2 + prefix filter. Auto-searches with 500ms debounce after 2+ characters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic mode&lt;/strong&gt;: Bedrock Knowledge Base RetrieveAndGenerate API. Vectorizes natural language queries for similarity search&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bedrock Knowledge Base Integration
&lt;/h3&gt;

&lt;p&gt;FSx for ONTAP's S3 AP can be specified directly as a Knowledge Base data source (&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-build-rag-with-bedrock.html" rel="noopener noreferrer"&gt;AWS official tutorial&lt;/a&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 Volume
    ↓ S3 AP (Data Source)
Bedrock Knowledge Base
    ↓ Embeddings (Titan V2)
OpenSearch Serverless (Vector Store)
    ↓ RetrieveAndGenerate
Results: related chunks + source file path + relevance score
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29ngw4t1oz7b4hq2sbb3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F29ngw4t1oz7b4hq2sbb3.png" alt="Semantic search with Bedrock Knowledge Bases. The search query flows from AWS AppSync to an AWS Lambda function, which calls RetrieveAndGenerate on Amazon Bedrock (Knowledge Bases). Knowledge Bases runs the vector search on Amazon OpenSearch Service and generates embeddings with Amazon Bedrock (Titan Text Embeddings V2). The data source is Amazon FSx for NetApp ONTAP through an Amazon S3 Access Point" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part3-semantic-search-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Results include scores (percentage display) and snippets. Clicking a result navigates to the file in the All Files view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Semantic Search Input Examples
&lt;/h3&gt;

&lt;p&gt;When KB is unconfigured, help text guides users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;💡 Semantic search examples:
• "Files with anomalies in last month's sales reports"
• "Test results exceeding thermal design specifications"
• "Description of the approval process for contract renewal"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Constraints
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This portal does not decide when a newly written file becomes searchable.&lt;/strong&gt; The Knowledge Base is a bring-your-own resource and the portal only calls &lt;code&gt;retrieve&lt;/code&gt;. Nothing in the portal starts an ingestion job — no EventBridge rule, no schedule. A newly written file is therefore not searchable until ingestion runs&lt;/li&gt;
&lt;li&gt;The time to appear is &lt;strong&gt;unmeasured&lt;/strong&gt;. With no trigger on the portal side, the timing belongs to whoever operates the KB&lt;/li&gt;
&lt;li&gt;Large file environments incur OpenSearch Serverless indexing costs&lt;/li&gt;
&lt;li&gt;PDF/Office text extraction accuracy depends on file quality&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;bedrockKbId&lt;/code&gt; is empty, semantic mode shows a "KB not configured" error. Keyword search does not go through the KB and is not subject to any of this&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;KB sync detection&lt;/strong&gt;: Data source sync status can be checked via the &lt;code&gt;bedrock-agent:GetIngestionJob&lt;/code&gt; API. Periodic sync can be scheduled in the Bedrock console. If immediate search reflection after file changes is required, consider an EventBridge + &lt;code&gt;StartIngestionJob&lt;/code&gt; API configuration for on-demand sync triggers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Agent Directory — Agent Catalog
&lt;/h2&gt;

&lt;p&gt;A UI for discovering "what agents are available."&lt;/p&gt;

&lt;h3&gt;
  
  
  Preset Agents
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Icon&lt;/th&gt;
&lt;th&gt;Specialty&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;file-explorer&lt;/td&gt;
&lt;td&gt;📁&lt;/td&gt;
&lt;td&gt;File operations (list, read, search)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;knowledge-analyst&lt;/td&gt;
&lt;td&gt;🧠&lt;/td&gt;
&lt;td&gt;Vector search + document analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;safety-controller&lt;/td&gt;
&lt;td&gt;🛡️&lt;/td&gt;
&lt;td&gt;ARP status checks + containment action proposals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compliance-auditor&lt;/td&gt;
&lt;td&gt;📋&lt;/td&gt;
&lt;td&gt;Retention period and SnapLock configuration audits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ops-advisor&lt;/td&gt;
&lt;td&gt;⚙️&lt;/td&gt;
&lt;td&gt;EMS event analysis + operational recommendations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Custom Agent Creation (Agent Creator)
&lt;/h3&gt;

&lt;p&gt;When presets aren't enough, define your own agents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent Creation Wizard:
1. Set name and icon
2. Write system prompt
3. Select tools to use (checkboxes)
4. Test execution
5. Sharing settings (personal only / team-shared)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Created agents appear in the Agent Directory, available to team members (when shared). This lets you codify patterns like "this department frequently does this kind of analysis" as reusable agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agent Teams — Multi-Agent Collaboration
&lt;/h2&gt;

&lt;p&gt;For complex tasks that a single agent can't handle alone, multiple agents collaborate.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Multi-Agent vs Single Agent
&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;Recommended&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;"Summarize this PDF"&lt;/td&gt;
&lt;td&gt;Single agent&lt;/td&gt;
&lt;td&gt;Completes with one tool (read_file + analysis)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Analyze all files in engineering/ and report security issues"&lt;/td&gt;
&lt;td&gt;Multi-agent&lt;/td&gt;
&lt;td&gt;Requires file discovery → content analysis → security judgment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Audit files changed last month for compliance violations"&lt;/td&gt;
&lt;td&gt;Multi-agent&lt;/td&gt;
&lt;td&gt;File identification → content reading → regulatory assessment → report generation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Decision criteria&lt;/strong&gt;: Multi-agent is effective when the task has multiple phases ("discover → analyze → judge") requiring different expertise at each phase. For simple tasks, multi-agent adds cost and latency without benefit — use single agent instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Team Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Team: "Security Audit Team"
├── 🔍 file-explorer (Collaborator): Identify target files
├── 🛡️ safety-controller (Supervisor): Verify ARP/security state
└── 📋 compliance-auditor (Reviewer): Final compliance judgment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Roles
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Supervisor&lt;/td&gt;
&lt;td&gt;Manages overall task progress. Directs other agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collaborator&lt;/td&gt;
&lt;td&gt;Performs work based on instructions (file ops, analysis, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;Reviews work results. Quality checks and approve/reject&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Team Creation Wizard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Enter team name and description&lt;/li&gt;
&lt;li&gt;Add agents from Agent Directory (minimum 2)&lt;/li&gt;
&lt;li&gt;Assign roles to each agent&lt;/li&gt;
&lt;li&gt;Sharing settings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Team execution flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: "Analyze all simulation results in the engineering/ folder"
    ↓
Supervisor (safety-controller):
    → Instructs file-explorer: "Get file listing for engineering/"
    ↓
Collaborator (file-explorer):
    → list_files → read_file (multiple)
    → Result: 12 files found, 8 are simulation results
    ↓
Supervisor:
    → Instructs knowledge-analyst: "Create summaries for each file"
    ↓
Collaborator (knowledge-analyst):
    → Analyzes each file, generates summaries
    ↓
Reviewer (compliance-auditor):
    → "Verify no PHI data is included"
    → Result: 2 files may contain personal information → recommends guardrail application
    ↓
Final response to user:
    "Analyzed 8 of 12 files. Summaries below.
     Note: 2 files were filtered due to potential personal information."
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8dp2yos0ed3oenf78rs5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8dp2yos0ed3oenf78rs5.png" alt="Multi-agent coordination led by a Supervisor. On a user request, the Supervisor (safety-controller) hands exploration to a Collaborator (file-explorer), analysis to a Collaborator (knowledge-analyst), and review to a Reviewer (compliance-auditor), then returns the consolidated answer to the user" width="798" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part3-agent-teams-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each agent leverages its specialty while the Supervisor coordinates the overall flow. From the user's perspective, a single chat message completes a complex task.&lt;/p&gt;




&lt;h2&gt;
  
  
  ActionApproval — Human-in-the-Loop
&lt;/h2&gt;

&lt;p&gt;When the AI agent proposes a destructive or irreversible operation, a modal requesting human approval appears before execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why HITL Matters
&lt;/h3&gt;

&lt;p&gt;In a storage management context, some operations are irreversible or have immediate impact. Enabling SnapLock can never be undone. A SnapMirror break severs the replication relationship. If an AI agent autonomously executes these, recovery may be impossible. The HITL pattern ensures the agent "proposes" but the final execution decision rests with a human.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which Operations Require Approval
&lt;/h3&gt;

&lt;p&gt;Operations flagged with &lt;code&gt;isDestructive: true&lt;/code&gt; or &lt;code&gt;isReversible: false&lt;/code&gt; require approval:&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;Icon&lt;/th&gt;
&lt;th&gt;Irreversible?&lt;/th&gt;
&lt;th&gt;Why Approval is Required&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File deletion&lt;/td&gt;
&lt;td&gt;🗑️&lt;/td&gt;
&lt;td&gt;△ (recoverable from snapshot)&lt;/td&gt;
&lt;td&gt;Prevent unintended bulk deletion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapLock enable&lt;/td&gt;
&lt;td&gt;🔐&lt;/td&gt;
&lt;td&gt;◎ (completely irreversible)&lt;/td&gt;
&lt;td&gt;Once enabled, cannot be disabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapMirror break&lt;/td&gt;
&lt;td&gt;🔗&lt;/td&gt;
&lt;td&gt;◎ (relationship must be re-established)&lt;/td&gt;
&lt;td&gt;Replication severance requires manual recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User block&lt;/td&gt;
&lt;td&gt;🚫&lt;/td&gt;
&lt;td&gt;○ (reversible but immediate impact)&lt;/td&gt;
&lt;td&gt;Risk of business disruption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP block&lt;/td&gt;
&lt;td&gt;🚫&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;Risk of service disruption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retention period change&lt;/td&gt;
&lt;td&gt;⏱️&lt;/td&gt;
&lt;td&gt;△ (can only extend, never shorten)&lt;/td&gt;
&lt;td&gt;Shortening is not possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threat containment&lt;/td&gt;
&lt;td&gt;🛡️&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;May affect multiple users&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How the Approval Flow Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Agent proposes a destructive operation
2. UI displays modal (showing action, target, reason)
3. User reviews the details
4. [Approve] → operation executes / [Reject] → operation cancelled, agent notified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: SnapMirror Break Approval Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: "Execute failover to the DR site"
    ↓
Agent: Determines SnapMirror break is required
    ↓
┌──────────────────────────────────────────┐
│ ⚠️  The agent is requesting approval     │
├──────────────────────────────────────────┤
│                                          │
│ Action:  SnapMirror break                │
│ Target:  vol_production → vol_dr         │
│ Reason:  DR failover execution           │
│                                          │
│ 🔴 This operation severs the replication │
│    relationship. Re-sync requires manual │
│    intervention.                         │
│                                          │
│ [❌ Reject]              [✅ Approve]     │
└──────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  UI Design
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────┐
│ ⚠️  The agent is requesting approval     │
├──────────────────────────────────────────┤
│                                          │
│ Action:  User block                      │
│ Target:  DOMAIN\suspicious_user          │
│ Reason:  ARP detected abnormal write     │
│          patterns                        │
│                                          │
│ ⚠️ This operation will immediately cut   │
│    off the target user's SMB access      │
│                                          │
│ [❌ Reject]              [✅ Approve]     │
└──────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Safe-side default&lt;/strong&gt;: &lt;code&gt;Reject&lt;/code&gt; button has &lt;code&gt;autoFocus&lt;/code&gt;. Reflexively pressing Enter falls to the safe side. Clicking outside the modal also triggers "reject."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Irreversibility warning&lt;/strong&gt;: Operations with &lt;code&gt;isReversible: false&lt;/code&gt; show a red warning bar, ensuring users visually recognize the severity of the operation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bedrock Guardrails — Response Filtering
&lt;/h2&gt;

&lt;p&gt;Bedrock Guardrails are applied to all AgentChat responses. While the PHI guardrail (Part 2) blocks based on path patterns, Guardrails filter response content itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Applied Checks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PII detection&lt;/td&gt;
&lt;td&gt;Input/Output&lt;/td&gt;
&lt;td&gt;Masking (&lt;code&gt;***-****-****&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Harmful content&lt;/td&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;Block + substitute message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic denial&lt;/td&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;Refuse response to specific topics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grounding check&lt;/td&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;Suppress ungrounded answers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When Guardrails are applied, a 🛡️ badge appears on the message. Users see "some information was filtered" with filter reason details available via tooltip.&lt;/p&gt;




&lt;h2&gt;
  
  
  Model, Cost, and Fallback
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Model Configuration
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Actual value&lt;/th&gt;
&lt;th&gt;Where it is decided&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;&lt;code&gt;amazon.nova-lite-v1:0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The agent reads the &lt;code&gt;AGENT_MODEL_ID&lt;/code&gt; environment variable. The ask-about-file side is hardcoded as &lt;code&gt;BEDROCK_MODEL_ID&lt;/code&gt; in &lt;code&gt;backend.ts&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max output tokens&lt;/td&gt;
&lt;td&gt;2048 for the agent, 1024 for ask-about-file&lt;/td&gt;
&lt;td&gt;Hardcoded in the handlers. Not a setting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;History sent&lt;/td&gt;
&lt;td&gt;Last 10 messages&lt;/td&gt;
&lt;td&gt;Hardcoded in the frontend, &lt;code&gt;messages.slice(-10)&lt;/code&gt; in &lt;code&gt;AgentChat.tsx&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per request&lt;/td&gt;
&lt;td&gt;~$0.001–$0.01&lt;/td&gt;
&lt;td&gt;For Nova Lite. Larger context from file reads increases cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To swap the model, pass &lt;code&gt;AGENT_MODEL_ID&lt;/code&gt; for the agent. The ask-about-file side is hardcoded in &lt;code&gt;backend.ts&lt;/code&gt;, so changing it means touching code. &lt;strong&gt;Nova Lite is the only model this verification used; no quality or latency comparison against another model was measured.&lt;/strong&gt; Which model suits you is not something this article answers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cost note&lt;/strong&gt;: Token cost per conversation depends on tool call count and file sizes. A typical "file search + summarize" conversation costs ~$0.005–$0.02 with Nova Lite. The number of history messages sent is hardcoded as &lt;code&gt;messages.slice(-10)&lt;/code&gt; in &lt;code&gt;AgentChat.tsx&lt;/code&gt;. Reducing it lowers cost and loses context in longer conversations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data residency note&lt;/strong&gt;: When using Bedrock in ap-northeast-1 (Tokyo), input data processing completes within the same region. Deploying FSx for ONTAP, the portal, and Bedrock in the same region ensures file contents never leave the region. Important for financial institutions and healthcare organizations with data residency requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: File contents are included in Bedrock API request bodies, but a VPC-contained configuration is possible. Bedrock does not use request data for model training (&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/data-protection.html" rel="noopener noreferrer"&gt;AWS Bedrock Data Protection&lt;/a&gt;). With VPC endpoint routing, data never leaves the VPC.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Bedrock KB Configuration Parameters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Recommended&lt;/th&gt;
&lt;th&gt;Rationale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Embedding Model&lt;/td&gt;
&lt;td&gt;Titan Embeddings V2&lt;/td&gt;
&lt;td&gt;Japanese support, cost-efficient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chunk Size&lt;/td&gt;
&lt;td&gt;512 tokens&lt;/td&gt;
&lt;td&gt;Appropriate granularity for file-level search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chunk Overlap&lt;/td&gt;
&lt;td&gt;64 tokens&lt;/td&gt;
&lt;td&gt;Prevents context loss at chunk boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top K&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Balance of precision and cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector Store&lt;/td&gt;
&lt;td&gt;OpenSearch Serverless&lt;/td&gt;
&lt;td&gt;Auto-provisioned with Managed KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The cost that bites is recomputing embeddings.&lt;/strong&gt; Every ingestion run creates embeddings for the files in scope, so the sync frequency and the scope are the bill. &lt;strong&gt;Not measured in this article.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are starting-point recommendations. Adjust based on average file size and content characteristics.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Chunk Size tradeoff&lt;/strong&gt;: Smaller chunks (256 tokens) improve recall but fragment context, reducing precision. Larger chunks (1024 tokens) provide richer context but allow irrelevant content to mix in. 512 tokens balances well for "file-level search." For technical documents (long sections), consider 768–1024.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Fallback Behavior
&lt;/h3&gt;

&lt;p&gt;When Bedrock API is unavailable (regional outage, throttling, etc.), the agent chat displays "Cannot connect. Please use conventional search" and falls back to keyword search. The design ensures "agent unavailable ≠ portal unavailable."&lt;/p&gt;

&lt;h3&gt;
  
  
  Chat History Retention Policy
&lt;/h3&gt;

&lt;p&gt;Set TTL on DynamoDB conversation history. Default: auto-deleted after 90 days. If auditing "who asked what" is required, separately retain CloudTrail API call logs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Right to deletion&lt;/strong&gt;: If a user requests deletion of their chat history, administrators can manually delete the relevant DynamoDB sessions. For GDPR/privacy law environments, implementing a self-service "Clear history" button is recommended.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Design Tradeoffs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Tradeoff&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AppSync Query (not Subscription)&lt;/td&gt;
&lt;td&gt;Simple implementation, single Cold Start&lt;/td&gt;
&lt;td&gt;No streaming response (waits for full completion)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Orchestrator outside VPC&lt;/td&gt;
&lt;td&gt;Short Cold Start (no ENI)&lt;/td&gt;
&lt;td&gt;Cannot call ONTAP REST API (delegates to VPC Lambda)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB session storage&lt;/td&gt;
&lt;td&gt;History shared across browsers&lt;/td&gt;
&lt;td&gt;Cost (write capacity), TTL management needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HITL via modal&lt;/td&gt;
&lt;td&gt;Reliably blocks destructive ops&lt;/td&gt;
&lt;td&gt;UX interruption, reduced agent autonomy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mode pill toggle (3 modes)&lt;/td&gt;
&lt;td&gt;Optimal agent for each purpose&lt;/td&gt;
&lt;td&gt;Learning cost of mode concept&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock Guardrails always-on&lt;/td&gt;
&lt;td&gt;Structural PII leak prevention&lt;/td&gt;
&lt;td&gt;Latency increase (hundreds of ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-agent (Agent Teams)&lt;/td&gt;
&lt;td&gt;Handles complex tasks&lt;/td&gt;
&lt;td&gt;Harder debugging, increased cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  On Streaming Responses
&lt;/h3&gt;

&lt;p&gt;The current implementation uses AppSync Query (synchronous), so agent responses display only after full generation completes. For long responses (file analysis, etc.), users wait several seconds.&lt;/p&gt;

&lt;p&gt;Token-level streaming via AppSync Subscription (WebSocket) is technically possible, but implementation complexity (reconnection handling, partial response parsing, error management) increases. I've deferred this as a next step for when latency becomes a problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Model (AI Agent Specific)
&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;Implementation&lt;/th&gt;
&lt;th&gt;Intent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;Cognito Groups (authenticated)&lt;/td&gt;
&lt;td&gt;All authenticated users can use chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool permissions&lt;/td&gt;
&lt;td&gt;Agent autonomously executes read-only tools&lt;/td&gt;
&lt;td&gt;Write operations require HITL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input filter&lt;/td&gt;
&lt;td&gt;Bedrock Guardrails (input)&lt;/td&gt;
&lt;td&gt;Prompt injection countermeasure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output filter&lt;/td&gt;
&lt;td&gt;Bedrock Guardrails (output)&lt;/td&gt;
&lt;td&gt;PII masking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHI path&lt;/td&gt;
&lt;td&gt;isPhiPath() check&lt;/td&gt;
&lt;td&gt;Don't pass &lt;code&gt;/dicom/&lt;/code&gt; etc. file contents to agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost control&lt;/td&gt;
&lt;td&gt;Only last 10 messages sent as history&lt;/td&gt;
&lt;td&gt;Token consumption cap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit&lt;/td&gt;
&lt;td&gt;CloudTrail + DynamoDB session logs&lt;/td&gt;
&lt;td&gt;Track "who asked what"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Monitoring
&lt;/h3&gt;

&lt;p&gt;Recommended operational metrics:&lt;/p&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;Source&lt;/th&gt;
&lt;th&gt;Alert Threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent Orchestrator error rate&lt;/td&gt;
&lt;td&gt;Lambda CloudWatch Metrics&lt;/td&gt;
&lt;td&gt;&amp;gt; 5% warrants investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response latency (p99)&lt;/td&gt;
&lt;td&gt;Lambda Duration&lt;/td&gt;
&lt;td&gt;&amp;gt; 10s check model or tool side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock Throttling&lt;/td&gt;
&lt;td&gt;Bedrock CloudWatch Metrics&lt;/td&gt;
&lt;td&gt;ThrottledCount &amp;gt; 0 consider provisioned throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB write throttling&lt;/td&gt;
&lt;td&gt;DynamoDB ConsumedWriteCapacityUnits&lt;/td&gt;
&lt;td&gt;On-demand auto-scales, typically no issue&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key design decision&lt;/strong&gt;: The agent can propose &lt;code&gt;storage-admin&lt;/code&gt; operations, but execution is authorized against the user's Cognito Group. If a regular user asks the agent "enable SnapLock," it fails with an authorization error. The agent explains "you don't have permission for this operation."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Compliance note&lt;/strong&gt;: When acting on AI responses about regulated data, the final judgment responsibility lies with the human who approved the operation. Agent responses are "assistive information" — not substitutes for legal or regulatory judgment. Audit trail via CloudTrail + DynamoDB session logs records "who asked what, and what was approved."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The following addresses common questions from different perspectives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Cost note&lt;/strong&gt;: "What's the token cost per conversation? How do I control context window size?" — With Nova Lite, ~$0.005–$0.02/conversation (varies by tool call count). The history count is hardcoded as &lt;code&gt;messages.slice(-10)&lt;/code&gt; in &lt;code&gt;AgentChat.tsx&lt;/code&gt;. Lowering it lowers cost and loses context in long conversations. Adjust it to balance cost vs. context retention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: "What data leaves the VPC? Can Bedrock see file contents?" — File contents are included in Bedrock API requests, but Bedrock does not use data for model training. With VPC endpoint configuration, data stays within the VPC. S3 AP access in Internet-origin configuration also completes within the same region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operations note&lt;/strong&gt;: "How do I add a new MCP tool? What's the deploy process?" — New tools are implemented as Lambda functions and registered as targets in AgentCore Gateway. Deploy via &lt;code&gt;sam deploy&lt;/code&gt; or Amplify CI/CD pipeline. See &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/agentcore-mcp-tools.md" rel="noopener noreferrer"&gt;AgentCore MCP Tools Reference&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;End-user note&lt;/strong&gt;: "Can I just ask 'find my expense report from last month' and it works?" — Yes. With Knowledge mode and semantic search enabled, you can search by intent without knowing file names. File Agent mode can also traverse folder structures to locate files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling note&lt;/strong&gt;: "How does this work with multiple Knowledge Bases across departments?" — KB data sources can be specified per-volume via S3 AP, so you can create separate KBs per department volume. The agent switches target KB via &lt;code&gt;bedrockKbId&lt;/code&gt; configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Front-end note&lt;/strong&gt;: "How do I customize the chat UI? Can I add my own components?" — The chat UI is implemented as a React component (&lt;code&gt;AgentChat.tsx&lt;/code&gt;), supporting style customization and custom message renderers. Tool call result displays are also extensible via the &lt;code&gt;ToolCallTimeline&lt;/code&gt; component.&lt;/p&gt;




&lt;h2&gt;
  
  
  Staged Adoption Steps
&lt;/h2&gt;

&lt;p&gt;All AI agent features support DemoMode. They work without FSx for ONTAP for evaluation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: No KB, No Agent (File Search Only)
&lt;/h3&gt;

&lt;p&gt;Leave &lt;code&gt;bedrockKbId&lt;/code&gt; empty in &lt;code&gt;portal-config.ts&lt;/code&gt;. Only keyword search available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add Bedrock Knowledge Base
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Register FSx for ONTAP S3 AP as KB data source&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;bedrockKbId&lt;/code&gt; in &lt;code&gt;portal-config.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Semantic search mode becomes active&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Enable AgentChat (File Agent Mode)
&lt;/h3&gt;

&lt;p&gt;Deploy MCP tool Lambda. &lt;code&gt;list_files&lt;/code&gt;, &lt;code&gt;read_file&lt;/code&gt;, &lt;code&gt;search_files&lt;/code&gt; become available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Multi-Agent Mode and HITL
&lt;/h3&gt;

&lt;p&gt;Add admin operation tools (block_user, enable_snaplock, etc.). HITL modal activates automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Agent Teams and Custom Agents
&lt;/h3&gt;

&lt;p&gt;Define team-specific analysis patterns as agents. Share via Agent Directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/amplify-portal" rel="noopener noreferrer"&gt;GitHub: solutions/amplify-portal/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/solutions/amplify-portal/docs/IMPLEMENTATION.md" rel="noopener noreferrer"&gt;Implementation Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/solutions/amplify-portal/docs/GETTING-STARTED.md" rel="noopener noreferrer"&gt;Getting Started Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/agentcore-mcp-tools.md" rel="noopener noreferrer"&gt;AgentCore MCP Tools Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/quick-desktop-mcp-setup.md" rel="noopener noreferrer"&gt;Quick Desktop MCP Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-scaling-guide.md" rel="noopener noreferrer"&gt;Scaling Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/tutorial-build-rag-with-bedrock.html" rel="noopener noreferrer"&gt;AWS: Build RAG with Bedrock KB + FSx for ONTAP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html" rel="noopener noreferrer"&gt;AWS: Bedrock Guardrails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887"&gt;Part 1: Putting a File Portal on FSx for ONTAP S3 Access Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/embedding-storage-operations-into-a-file-portal-from-arpai-incident-response-to-regulatory-1oih"&gt;Part 2: From ARP/AI Incident Response to Regulatory Retention Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/putting-fsx-for-ontap-operations-on-a-file-portal-on-aws-182-actions-and-the-design-of-425g"&gt;Part 4: Putting FSx for ONTAP operations on a file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/what-i-learned-driving-fsx-for-ontap-from-a-file-portal-on-aws-flexgroup-creation-capacity-3gkd"&gt;Part 5: What I learned driving FSx for ONTAP from a file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/what-i-left-off-the-file-portal-on-aws-how-far-fsx-for-ontap-features-reach-and-the-work-handed-hk8"&gt;Part 6: What I left off the file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary and Next Steps
&lt;/h2&gt;

&lt;p&gt;I integrated AI agents into the "file portal + storage operations" foundation built in Parts 1 and 2:&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;What became possible&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AgentChat (3 modes)&lt;/td&gt;
&lt;td&gt;Complete file operations, analysis, and admin ops in natural language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SemanticSearch&lt;/td&gt;
&lt;td&gt;Solve "where did I put that file?" with vector search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Directory/Creator&lt;/td&gt;
&lt;td&gt;Codify team-specific analysis patterns as agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Teams&lt;/td&gt;
&lt;td&gt;Collaboratively process complex tasks with multiple agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HITL (ActionApproval)&lt;/td&gt;
&lt;td&gt;Execute destructive operations only after human approval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock Guardrails&lt;/td&gt;
&lt;td&gt;Structurally prevent PII leaks and harmful responses&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This series documented the portal's evolution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt; covered the file portal foundation (S3 AP + Amplify Gen2 / Nextcloud)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt; added storage operations (ARP/AI, Tamperproof, regulatory retention)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt; integrated AI agents (natural language operations, semantic search, multi-agent)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parts 4 to 6&lt;/strong&gt; continue into delegating operations and recording them, what the running cluster stopped, and what was left off the screen&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Through the progression of "browse files" → "manage storage" → "talk to AI," the portal reached a point where daily operations and data utilization complete in the browser — without opening ONTAP System Manager or the CLI.&lt;/p&gt;

&lt;p&gt;All code is published in the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/amplify-portal" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For next steps, I'd suggest starting with keyword search in DemoMode, then adding a Bedrock Knowledge Base for semantic search, and layering on MCP tools and HITL after that. When you move on to a production connection, see the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this post helps someone out there.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>amplify</category>
      <category>fsxforontap</category>
      <category>bedrock</category>
    </item>
    <item>
      <title>Running ARP/AI and SnapLock on FSx for ONTAP from a browser — ransomware detection and WORM retention (File Portal Part 2)</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Mon, 03 Aug 2026 10:20:41 +0000</pubDate>
      <link>https://dev.to/aws-builders/embedding-storage-operations-into-a-file-portal-from-arpai-incident-response-to-regulatory-1oih</link>
      <guid>https://dev.to/aws-builders/embedding-storage-operations-into-a-file-portal-from-arpai-incident-response-to-regulatory-1oih</guid>
      <description>&lt;p&gt;&lt;strong&gt;Run detection through containment from the screen&lt;/strong&gt; — ARP/AI, SnapLock, audit logs (part 2 of 6)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Japanese version of this article is available: &lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-file-portal-2-ransomware-worm" rel="noopener noreferrer"&gt;日本語版&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887"&gt;Part 1&lt;/a&gt;, I looked at putting a file portal on top of the S3 Access Point of Amazon FSx for NetApp ONTAP (hereafter FSx for ONTAP S3 AP, or S3 AP), comparing Amplify Gen2 and Nextcloud as two approaches. The portal at that point stopped at "browse the files" — the moment you wanted to know something about the storage layer, you had to go back to the ONTAP CLI or the REST API.&lt;/p&gt;

&lt;p&gt;Once people started using it, the first two things that came back were "I want to check whether ARP has detected anything myself" and "is this snapshot actually locked?" Both requests came from team members outside storage administration, and the sticking point was the same: you cannot check any of it without a VPN connection to the management LIF. So I added ONTAP operations features to the Amplify Gen2 portal, up to the point where health checks and incident first response can be done entirely in a browser.&lt;/p&gt;

&lt;p&gt;Here's the conclusion up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ONTAP System Manager-equivalent admin operations can be executed from a browser through AppSync + Lambda&lt;/li&gt;
&lt;li&gt;Separating &lt;code&gt;storage-admin&lt;/code&gt; from regular users with Cognito Groups gives you a safe split between read-only viewing and change operations&lt;/li&gt;
&lt;li&gt;Managing ransomware response as four states — Detected → Contained → Investigating → Resolved — removes the hesitation from first response&lt;/li&gt;
&lt;li&gt;Turning regulatory retention periods (FISC 7 years / SOX 5 years / HIPAA 6 years) into presets prevents day-count mistakes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I'll cover the design of the storage operations features embedded in the portal, the ONTAP REST API implementation behind them, and the points I was careful about when handling irreversible operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who this is for&lt;/strong&gt;: anyone who wants to hand ransomware response and retention settings to people who are not the storage administrator. It is a record of how much to show someone who never touches the ONTAP CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this article does not cover&lt;/strong&gt;: the interpretation of the regulations themselves (that is a legal and compliance judgement), absolute performance figures, tenant isolation design.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmf2ttr6dk25dfvvir4zp.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmf2ttr6dk25dfvvir4zp.png" alt="The admin operation path for the storage operations features in the portal. The user's web browser reaches AWS Amplify over HTTPS, Amazon Cognito handles authentication and group checks, and AWS AppSync invokes an AWS Lambda function inside the VPC. That Lambda reads credentials from AWS Secrets Manager and drives the ONTAP REST API on Amazon FSx for NetApp ONTAP" width="800" height="1213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part2-admin-operations-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available, and every figure is listed in the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/architecture-diagrams.en.md" rel="noopener noreferrer"&gt;architecture diagram index&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Relationship to existing tools&lt;/strong&gt;: This portal makes the same storage management operations previously available only through the ONTAP CLI or REST API accessible from a browser with Cognito authentication. It is not a replacement for those — it's an additional layer that opens the same operations to people who should not hold cluster-administrator credentials.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The features added are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storage Dashboard&lt;/strong&gt;: a 4-card health overview&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ARP/AI Incident Lifecycle&lt;/strong&gt;: managed as four states — Detected → Contained → Investigating → Resolved&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Object Lock Configuration UI&lt;/strong&gt;: select a bucket, specify a retention mode, apply&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHI Guardrail&lt;/strong&gt;: refuses AI processing on a path-name match such as &lt;code&gt;/dicom/&lt;/code&gt;. It does not look at contents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EMS Events&lt;/strong&gt;: ONTAP alerts shown on screen (fetched when the dashboard is opened)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regulatory Retention Presets&lt;/strong&gt;: FISC 7-year / SOX 5-year / HIPAA 6-year&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Welcome Modal&lt;/strong&gt;: first-time user onboarding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Log&lt;/strong&gt;: file access trail via CloudTrail, viewable in the UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FlexClone Restore&lt;/strong&gt;: recovery from a snapshot, selected in the UI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Athena Query&lt;/strong&gt;: SQL analysis on NAS data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scope: this article is an implementation record of adding "operations features usable by members outside storage administration" to the file portal built in Part 1. Part 3 covers AI agent integration.&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/tree/main/solutions/amplify-portal" rel="noopener noreferrer"&gt;solutions/amplify-portal/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Embed Admin Operations in a Portal
&lt;/h2&gt;

&lt;p&gt;For FSx for ONTAP, the management interfaces you can reach without routing through an additional third-party SaaS are three: the AWS Management Console / Amazon FSx API, the ONTAP CLI over SSH, and the ONTAP REST API. ONTAP System Manager is not among them — System Manager support for FSx for ONTAP is delivered through the vendor's SaaS console, and that SaaS covers FSx for ONTAP only in its SaaS-connected mode (sources in &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/fsx-ontap-management-interfaces.md" rel="noopener noreferrer"&gt;management interfaces&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So learning anything about the ONTAP side means SSH to the ONTAP CLI or a REST call, both of which assume cluster-administrator credentials. When team members outside storage administration (security staff, compliance officers, data protection teams) want to "check ARP status" or "verify whether a snapshot is locked," the way to let them is to hand over an SSH key.&lt;/p&gt;

&lt;p&gt;Motivation for embedding admin operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Security staff should be able to check ransomware detection state without entering the ONTAP CLI, and execute containment actions&lt;/li&gt;
&lt;li&gt;Compliance officers should be able to verify retention settings and audit logs themselves&lt;/li&gt;
&lt;li&gt;Operations teams should be able to check ONTAP alerts without opening another tool&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cognito Groups (&lt;code&gt;storage-admin&lt;/code&gt; / &lt;code&gt;authenticated&lt;/code&gt;) separate authorization — regular users are read-only, only admins can execute changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Used to Do vs What You Can Do Now
&lt;/h3&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;Previous Method&lt;/th&gt;
&lt;th&gt;Portal Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Check ARP threats and contain&lt;/td&gt;
&lt;td&gt;CLI: &lt;code&gt;security anti-ransomware volume show&lt;/code&gt; → block by hand&lt;/td&gt;
&lt;td&gt;Detected on the dashboard, contained from there&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WORM-lock a snapshot&lt;/td&gt;
&lt;td&gt;CLI: &lt;code&gt;volume snapshot modify-retention&lt;/code&gt; or direct REST API call&lt;/td&gt;
&lt;td&gt;Lock panel → select preset → apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DR failover with SnapMirror&lt;/td&gt;
&lt;td&gt;CLI: &lt;code&gt;snapmirror break&lt;/code&gt; / &lt;code&gt;snapmirror resync&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Status list → click action button&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Storage Dashboard — Admin Landing Page
&lt;/h2&gt;

&lt;p&gt;The first thing admins see after login is a 4-card health dashboard. This follows ONTAP System Manager's "dashboard first" pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ 💾 12       │  │ 🛡️ 8         │  │ 🔐 5        │  │ 📊 3.2x 　   │
│ Volumes     │  │ARP Protected│  │ Locked Snaps│  │ Efficiency  │
│ Avg: 62%    │  │✅ No threats│  │ Tamperproof │  │ 69% saved   │
└─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 4 Cards
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Card&lt;/th&gt;
&lt;th&gt;Shows&lt;/th&gt;
&lt;th&gt;Navigates to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Volume Capacity&lt;/td&gt;
&lt;td&gt;Volume count + average utilization&lt;/td&gt;
&lt;td&gt;Volume Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARP/AI Protection&lt;/td&gt;
&lt;td&gt;Protected volumes + threat count&lt;/td&gt;
&lt;td&gt;ARP Admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Locked Snapshots&lt;/td&gt;
&lt;td&gt;Locked snapshot count&lt;/td&gt;
&lt;td&gt;Snapshot Admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage Efficiency&lt;/td&gt;
&lt;td&gt;Dedup/compression ratio + savings&lt;/td&gt;
&lt;td&gt;Efficiency Panel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Four APIs fetched in parallel with &lt;code&gt;Promise.allSettled&lt;/code&gt;. If one fails (e.g., ARP not configured), remaining cards still render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;volResp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arpResp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;snapResp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;effResp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allSettled&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nf"&gt;adminQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listVolumes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;adminQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listArpVolumes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;protectionQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listSnapshots&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;maxResults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;adminQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getEfficiencyStats&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&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;Why &lt;code&gt;Promise.allSettled&lt;/code&gt; over &lt;code&gt;Promise.all&lt;/code&gt;: with &lt;code&gt;Promise.all&lt;/code&gt;, one failure rejects everything. On a storage dashboard, "ARP not configured" is a normal state, not an error.&lt;/p&gt;




&lt;h2&gt;
  
  
  ARP/AI Incident Lifecycle
&lt;/h2&gt;

&lt;p&gt;After ONTAP Autonomous Ransomware Protection (ARP/AI) detects a threat, the response flow is managed across 4 states.&lt;/p&gt;

&lt;h3&gt;
  
  
  State Transitions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 Detected
  → 🟠 Contained
    → 🟡 Investigating
      → 🟢 Resolved
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnynn5v32lh5j92dq06gb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnynn5v32lh5j92dq06gb.png" alt="The ARP/AI incident lifecycle tracked as four states. Detected moves to Contained on containment, then to Investigating, and finally to Resolved. The notes list what each state records" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part2-arp-incident-lifecycle-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Recorded at Each State
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Timestamp&lt;/th&gt;
&lt;th&gt;Additional Data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Detected&lt;/td&gt;
&lt;td&gt;detectedAt&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contained&lt;/td&gt;
&lt;td&gt;containedAt&lt;/td&gt;
&lt;td&gt;blockedUsers[], blockedIps[], snapshotName&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Investigating&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;notes (free text)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resolved&lt;/td&gt;
&lt;td&gt;resolvedAt&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Containment Actions
&lt;/h3&gt;

&lt;p&gt;Actions executable directly from the portal:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;ONTAP REST API&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Block SMB User&lt;/td&gt;
&lt;td&gt;name-mapping deny (win→unix)&lt;/td&gt;
&lt;td&gt;Immediately denies target user's SMB access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block NFS IP&lt;/td&gt;
&lt;td&gt;export-policy deny rule&lt;/td&gt;
&lt;td&gt;Immediately denies NFS access from target IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full Containment&lt;/td&gt;
&lt;td&gt;snapshot + block + disconnect&lt;/td&gt;
&lt;td&gt;Preserves snapshot + blocks all + disconnects sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unblock&lt;/td&gt;
&lt;td&gt;delete name-mapping/export-policy rules&lt;/td&gt;
&lt;td&gt;Recovery after investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This implements the equivalent of NetApp DII Storage Workload Security blocking directly via ONTAP REST API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useIncidentState hook — persists per-volume in localStorage&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IncidentState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contained&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;investigating&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resolved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useIncidentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;volumeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIncident&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IncidentRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;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="nf"&gt;loadIncident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;volumeName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markContained&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contained&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;snapshotName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;blockedUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;blockedIps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ips&lt;/span&gt; &lt;span class="o"&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&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 UI displays a state badge with the next action button changing dynamically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[🔴 Detected] → [Execute Containment] button
[🟠 Contained] → [Start Investigation] button
[🟡 Investigating] → [Resolve] button
[🟢 Resolved] → (complete)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Current limitation&lt;/strong&gt;: Incident state is stored in &lt;code&gt;localStorage&lt;/code&gt; and not shared across browsers. There is also a risk of losing incident state if the browser crashes. DynamoDB persistence is recommended for production use so that multiple members see the same state.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Incident response note&lt;/strong&gt;: After executing containment actions, integrate with your existing incident runbooks (PagerDuty, OpsGenie, etc.). Portal containment is a first-response action and should be positioned as part of your full incident management workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: Executing containment actions (SMB block, NFS IP block) requires membership in the &lt;code&gt;storage-admin&lt;/code&gt; group. Unblocking requires the same privilege, so even if a regular user's browser session is compromised, the attacker cannot lift a block. Compromise of a &lt;code&gt;storage-admin&lt;/code&gt; account itself is guarded by Cognito MFA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-tenant note&lt;/strong&gt;: When multiple teams (tenants) share the same portal, ensure tenant A cannot view tenant B's ARP alerts by combining Cognito Groups with volume-level access control. In the current implementation, all &lt;code&gt;storage-admin&lt;/code&gt; group members can view ARP status for all volumes. If tenant isolation is required, consider deploying separate portal instances per SVM.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  PHI Guardrail — refusing AI processing by path name
&lt;/h2&gt;

&lt;p&gt;Stops "PHI went to a managed AI service by accident" in a HIPAA environment, using a folder naming convention. &lt;strong&gt;The decision is a path-name match and nothing else — not the file contents, not the extension, not the tags.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where this sits, first. &lt;strong&gt;It is a guard against a mistake, not the boundary itself.&lt;/strong&gt; The boundary is IAM and the Cognito group path prefixes: a key the caller cannot reach cannot be handed to an AI either. The folder convention sits in front of that, to keep a key the caller &lt;em&gt;can&lt;/em&gt; reach from being submitted (the predicate itself is &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/shared/portal_regulated_path.py" rel="noopener noreferrer"&gt;portal_regulated_path.py&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hiding a button does not stop a call to AppSync.&lt;/strong&gt; So the check sits both in the browser and in the five endpoints that hand file contents outward — asking about a file, Textract, Comprehend, Rekognition label detection, and the agent's file read. The list of endpoints is pinned by a test, so a sixth one added later cannot ship without it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;Files in paths containing &lt;code&gt;/dicom/&lt;/code&gt;, &lt;code&gt;/phi/&lt;/code&gt;, &lt;code&gt;/pii/&lt;/code&gt;, &lt;code&gt;/hipaa/&lt;/code&gt;, or &lt;code&gt;/protected-health/&lt;/code&gt; have their AI processing button automatically disabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isPhiPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/(&lt;/span&gt;&lt;span class="sr"&gt;dicom|phi|pii|hipaa|protected-health&lt;/span&gt;&lt;span class="se"&gt;)[\/&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
         &lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dicom/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phi/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pii/&lt;/span&gt;&lt;span class="dl"&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;UI display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal folder: [⚡ Run AI Processing]     ← clickable
PHI folder:    [🚫 PHI — AI Blocked]      ← disabled, not clickable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Design Intent
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Guardrail, not detect-and-respond&lt;/strong&gt;: rather than "processed it, then discovered it was PHI," this prevents processing from starting at all&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cannot be bypassed even by admins&lt;/strong&gt;: the button itself is disabled regardless of permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depends on folder naming convention&lt;/strong&gt;: path-pattern based, so it presupposes organizational folder structure rules&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important limitation&lt;/strong&gt;: This guardrail blocks based on folder path only — it does NOT scan file contents. A file like &lt;code&gt;/contracts/sensitive-patient.pdf&lt;/code&gt; placed outside PHI paths will not be blocked. For content-based PHI detection, combine with Bedrock Guardrails (response filtering) described in Part 3. Path guardrail + content filtering gives you ideal 2-layer defense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HIPAA note&lt;/strong&gt;: Using this portal in a HIPAA environment presupposes a Business Associate Agreement (BAA) with AWS. Cognito, Lambda, S3, Step Functions, and Bedrock are all &lt;a href="https://aws.amazon.com/compliance/hipaa-eligible-services-reference/" rel="noopener noreferrer"&gt;HIPAA eligible services&lt;/a&gt; (checked against the list dated 2026-09-03). &lt;strong&gt;That list is updated, so read the original at the point you adopt this.&lt;/strong&gt; The PHI guardrail refuses in the browser and in the five endpoints that hand file contents to a managed AI service. Both the BAA and technical controls are still required.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  S3 Object Lock — WORM Protection for Output Buckets
&lt;/h2&gt;

&lt;p&gt;AI processing results and compliance reports are stored in standard S3 buckets outside FSx for ONTAP. The portal provides a UI to configure S3 Object Lock on these output buckets.&lt;/p&gt;

&lt;h3&gt;
  
  
  3-Tab Lock Panel
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tab&lt;/th&gt;
&lt;th&gt;Target&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;ONTAP SnapLock&lt;/td&gt;
&lt;td&gt;Volumes&lt;/td&gt;
&lt;td&gt;WORM protection for NAS data (shared across NFS/SMB/S3 AP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Object Lock&lt;/td&gt;
&lt;td&gt;S3 Buckets&lt;/td&gt;
&lt;td&gt;WORM protection for AI processing results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tamperproof Snapshot&lt;/td&gt;
&lt;td&gt;Snapshots&lt;/td&gt;
&lt;td&gt;Tamper prevention for specific snapshots&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  S3 Object Lock Configuration Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Fetch bucket list → select from dropdown
2. Choose mode:
   - Governance: Authorized users can override (recommended for AI output)
   - Compliance: No one, including root, can delete until retention expires (regulatory archives)
3. Specify retention days
4. [Apply] sets Object Lock Configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Use SnapLock vs S3 Object Lock
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Characteristic&lt;/th&gt;
&lt;th&gt;ONTAP SnapLock&lt;/th&gt;
&lt;th&gt;S3 Object Lock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Target&lt;/td&gt;
&lt;td&gt;Files on NAS volumes&lt;/td&gt;
&lt;td&gt;Objects in S3 buckets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access protocols&lt;/td&gt;
&lt;td&gt;NFS/SMB/S3 AP&lt;/td&gt;
&lt;td&gt;S3 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Source data WORM protection&lt;/td&gt;
&lt;td&gt;AI results/reports protection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regulatory&lt;/td&gt;
&lt;td&gt;SEC 17a-4, FISC, HIPAA, NARA&lt;/td&gt;
&lt;td&gt;SEC 17a-4, HIPAA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Combining both achieves "source data AND processing results both tamper-proof."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Compliance framework note&lt;/strong&gt;: The AWS services used by this portal (Cognito, Lambda, S3, Step Functions, Bedrock, AppSync) are in scope for &lt;a href="https://aws.amazon.com/compliance/ismap/" rel="noopener noreferrer"&gt;ISMAP&lt;/a&gt; and &lt;a href="https://aws.amazon.com/compliance/soc-faqs/" rel="noopener noreferrer"&gt;SOC 2&lt;/a&gt; (as of 2026-09; scope changes). FedRAMP environments for US government agencies require deployment in AWS GovCloud regions. Confirm compliance requirements with your legal and compliance teams — this portal is a technical implementation pattern and does not provide compliance judgments.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Regulatory Retention Period Presets
&lt;/h2&gt;

&lt;p&gt;The Tamperproof Snapshot lock form offers retention periods as dropdown selections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preset List
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Preset&lt;/th&gt;
&lt;th&gt;Retention Days&lt;/th&gt;
&lt;th&gt;Regulatory Basis&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;30 days&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;Short-term validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;90 days&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;td&gt;Quarterly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 year&lt;/td&gt;
&lt;td&gt;365&lt;/td&gt;
&lt;td&gt;Annual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SOX/J-SOX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,825 (5 years)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Securities Exchange Act — financial records retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HIPAA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,192 (6 years)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medical records minimum retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FISC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,557 (7 years)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Center for Financial Industry Information Systems standard, Chapter 9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Compliance note&lt;/strong&gt;: FISC/SOX/HIPAA presets are provided for technical convenience. Whether the regulatory mapping and retention periods apply to your organization's specific situation should be validated with your legal and compliance teams. Regulatory interpretation varies by industry, business scope, and jurisdiction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why Presets Are Needed
&lt;/h3&gt;

&lt;p&gt;SnapLock retention settings are &lt;strong&gt;irreversible&lt;/strong&gt;. Once a snapshot is locked, the retention period cannot be shortened.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents "how many days is 5 years?" calculation errors&lt;/li&gt;
&lt;li&gt;Each preset shows a tooltip with regulatory basis&lt;/li&gt;
&lt;li&gt;Minimizes risk of incorrect selection (can extend but never shorten)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;{1825}&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"SOX/J-SOX: Financial records must be retained for 5 years"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  1,825 days — SOX/J-SOX (5 years)
&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;{2192}&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"HIPAA: Medical records require minimum 6-year retention"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  2,192 days — HIPAA (6 years)
&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;{2557}&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"FISC: Financial institution data requires 7-year retention"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  2,557 days — FISC (7 years)
&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  EMS Events — ONTAP Alert Viewer
&lt;/h2&gt;

&lt;p&gt;Retrieves events from ONTAP's Event Management System (EMS) with severity alert/error/emergency and displays them in the admin panel. The filtering happens in the ONTAP query (&lt;code&gt;message.severity&lt;/code&gt;), and at most the 50 most recent are fetched, newest first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no duplicate suppression.&lt;/strong&gt; A repeating message fills all 50 rows. It is read-only, with no acknowledge action. If you need counts collapsed, rows capped or events acknowledged, pair it with pushing EMS events out through EventBridge + SNS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend Implementation
&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;_get_ems_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&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="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;ONTAP REST: GET /api/support/ems/events&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;max_records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&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="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;maxRecords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;severity_filter&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;severity&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;alert,error,emergency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;query&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;/support/ems/events?max_records=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_records&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;query&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;&amp;amp;severity=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;severity_filter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;query&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;amp;order_by=time desc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;query&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;amp;fields=time,severity,message.name,message.text,node.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_ontap_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&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;time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e&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;time&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;severity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e&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;severity&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messageName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e&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;message&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="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;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="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messageText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e&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;message&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="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;text&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e&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;node&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="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;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="p"&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;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&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;records&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="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;events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;events&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example Events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Message Name&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;emergency&lt;/td&gt;
&lt;td&gt;ha.takeover.byPartner&lt;/td&gt;
&lt;td&gt;HA partner takeover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;raid.disk.predictiveFailure&lt;/td&gt;
&lt;td&gt;Disk predictive failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;error&lt;/td&gt;
&lt;td&gt;scsiblade.san.netLIFDown&lt;/td&gt;
&lt;td&gt;SAN LIF down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;arw.volume.attack&lt;/td&gt;
&lt;td&gt;ARP detected ransomware attack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Storage administrators can check recent alerts directly in the portal without opening an ONTAP CLI session.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Refresh frequency note&lt;/strong&gt;: EMS events are fetched on demand when the dashboard is displayed (not by periodic polling). If you need real-time alerting, push EMS events via EventBridge + SNS instead (see &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations" rel="noopener noreferrer"&gt;fsxn-observability-integrations&lt;/a&gt;). The portal is for "checking recent events"; for "detecting in real time," consider pairing it with a separate observability stack.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Welcome Modal — First-Time Onboarding
&lt;/h2&gt;

&lt;p&gt;First-time users see a 3-step guided tour.&lt;/p&gt;

&lt;h3&gt;
  
  
  3 Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;📂 &lt;strong&gt;File Browsing&lt;/strong&gt; — Browse and search NAS files from your browser&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;AI Processing&lt;/strong&gt; — Select files and trigger AI/ML workflows&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Data Protection&lt;/strong&gt; — Snapshots, WORM locks, ransomware protection&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;WelcomeModal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dismissed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDismissed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;portal-welcome-dismissed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dismissed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 3-step carousel with dot navigation&lt;/span&gt;
  &lt;span class="c1"&gt;// "Don't show again" checkbox → localStorage persistence&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Design Intent
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Standard onboarding pattern in SaaS products&lt;/li&gt;
&lt;li&gt;Often omitted in internal tools, but highly effective for conveying "what this portal can do" in 10 seconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localStorage&lt;/code&gt; state management, works without backend changes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Resource Management — 20-Panel Card-Grid Admin
&lt;/h2&gt;

&lt;p&gt;ONTAP admin operations organized into 5 categories × 20 panels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Category Structure
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Panels&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🗄️ Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Volumes / 🧬 FlexClone / ⚡ FlexCache / Qtrees / Quotas / Efficiency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🔐 Access Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Export Policies / SMB Shares / 👤 Local Users / 🔀 Name Mapping / QoS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🛡️ Data Protection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ARP/AI / Snapshots / SnapLock / 📡 FPolicy / 🦠 Vscan / 🪞 SnapMirror&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🖥️ Cluster&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔗 Peering / 🖥️ Cluster Information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🤖 Services&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AI Settings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each panel displays as a card grid, clicking navigates to the detail view. Follows System Manager's card-based navigation.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmv0m6qcfzhdo2wlspo2m.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmv0m6qcfzhdo2wlspo2m.png" alt="The Resource management landing page in the portal. A storage health row of four cards sits above five categories — Storage, Access control, Data protection, Cluster and AI services — holding twenty panel cards in total" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Cluster category covers the part the AWS Management Console does not expose. Cluster peering and SVM peering had to be done from the ONTAP CLI or by hand-writing REST calls, which is a real operational cost for an AWS-centric team that otherwise never leaves the console.&lt;/p&gt;

&lt;h3&gt;
  
  
  New Panel Highlights
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vscan — "Zero to Configured" Setup Guidance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vscan (antivirus scanning) has the highest setup barrier of any ONTAP feature — it requires external Windows/Linux servers and antivirus vendor licenses. When unconfigured, the Vscan panel displays a 5-step guided wizard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vendor selection (6-vendor comparison table with license page links)&lt;/li&gt;
&lt;li&gt;NetApp Antivirus Connector download&lt;/li&gt;
&lt;li&gt;EC2 deployment (architecture diagram + AWS Blog/GitHub samples)&lt;/li&gt;
&lt;li&gt;ONTAP CLI commands (scanner-pool / policy / enable)&lt;/li&gt;
&lt;li&gt;Verification in this panel&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even someone starting from "what is Vscan?" can follow the setup path directly from the portal.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhlmggqb924k6jonmhg4t.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhlmggqb924k6jonmhg4t.png" alt="The Vscan panel with the setup guide expanded. It lists interoperable products with links to their license pages, where to obtain the connector, an architecture line, and example ONTAP CLI commands for scanner-pool, on-access-policy and enable" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One correction from the first version of this panel: the guidance only rendered while Vscan was disabled, so it disappeared the moment Vscan came up — exactly when you still want the scanner-side steps and the interoperability matrix link. It now stays reachable from a toolbar toggle in both states.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FlexClone — Instant Zero-Copy Clones&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create and split volume clones from the UI for ransomware recovery, forensics, or dev/test. Metadata-only copy completes in seconds with near-zero additional capacity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SnapMirror — Replication Lifecycle Management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Monitor DR and cross-region replication relationships in the browser and execute actions (sync, break, resync, quiesce, resume, delete). Click a relationship to expand the last 10 transfer records (size, duration, success/failure). Lag times exceeding your RPO target display in red so they are noticed immediately.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DR planning note&lt;/strong&gt;: SnapMirror Break/Resync operations are executable from the browser, but understand the RPO impact before clicking Break. After a break, the replication relationship is severed and resync requires delta transfer. For production environments, pair the confirmation dialog (implemented) with your team's DR runbook procedures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;FlexCache — Remote Read Cache CRUD&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create FlexCache volumes (async, with prepopulate path support), view the list (origin→cache arrow display), and delete (automated 3-step: unmount→offline→delete). When unconfigured, guidance is displayed with a datalist for selecting origin volumes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Async polling note&lt;/strong&gt;: FlexCache creation uses async polling to detect completion. If the user closes the browser during creation, the ONTAP-side operation continues. The created volume appears on the next list refresh — no data loss or inconsistent state results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Cluster and SVM Peering — the gap the console leaves&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Peering is a two-sided operation, and the authentication passphrase has to travel between the two clusters. The panel drives one side at a time.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8fskly3zr3rkiayssqhj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8fskly3zr3rkiayssqhj.png" alt="The Peering panel showing the cluster peers tab. Two peers are listed with state badges — one available and authenticated, one pending with authentication absent — each with Accept and Delete actions" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The prerequisite that trips people up is the intercluster LIF, so it gets its own tab rather than being left as a footnote. Both clusters need at least one in &lt;code&gt;up&lt;/code&gt; state, and TCP 11104, 11105 plus ICMP have to be allowed between their addresses — the intercluster LIF addresses, not the management LIF.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq1av49klx0fbdz3gvs82.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq1av49klx0fbdz3gvs82.png" alt="The intercluster LIF tab listing two LIFs in up state on separate nodes, with a Ready for peering badge and a note that cluster peering needs at least one such LIF on both sides" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flow is: check the LIFs, create the peer on one cluster with &lt;code&gt;Generate passphrase&lt;/code&gt;, then enter that passphrase under &lt;code&gt;Accept&lt;/code&gt; on the other. The generated value is shown &lt;strong&gt;once&lt;/strong&gt; — ONTAP returns it in the creation response only, so the panel surfaces it in a banner you have to dismiss deliberately. Lose it and the peer has to be deleted and recreated.&lt;/p&gt;

&lt;p&gt;SVM peers come after the cluster peer reaches &lt;code&gt;available&lt;/code&gt;, and are accepted on the remote side without a passphrase. An SVM-level SnapMirror additionally needs the source subtype &lt;code&gt;default&lt;/code&gt; and the destination &lt;code&gt;dp_destination&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cluster Information — nodes, licences, LIFs, protocols, DNS, jobs&lt;/strong&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75kmu6rs32q243wsndxu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F75kmu6rs32q243wsndxu.png" alt="The Cluster information panel overview tab, showing the cluster name and ONTAP version, with the node and licence lists empty and a note under each explaining that this is expected on FSx for ONTAP rather than an error" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Four tabs: overview (cluster name and version, nodes, licences), interfaces (LIF list with enable/disable), services (NFS, SMB and S3 state with enable/disable, plus the SVM DNS domains and servers), and jobs.&lt;/p&gt;

&lt;p&gt;The overview tab is quieter than that list suggests. On the cluster I tested (ONTAP 9.17.1P7D1) both &lt;code&gt;/cluster/nodes&lt;/code&gt; and &lt;code&gt;/cluster/licensing/licenses&lt;/code&gt; returned zero records with no error, because AWS manages the cluster rather than the tenant. The first version of the panel rendered that as a bare "No nodes", which reads as a failure, so it now states that an empty list is expected here. The cluster name and ONTAP version come from &lt;code&gt;/cluster&lt;/code&gt; and are still populated.&lt;/p&gt;

&lt;p&gt;The jobs tab matters more than it looks. FlexCache creation, FlexClone split, SnapMirror transfers and peering all run as asynchronous ONTAP jobs, so this is where their progress and, more usefully, their failure reasons show up.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgtgaar5wk1z1o4e03eb3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgtgaar5wk1z1o4e03eb3.png" alt="The Cluster information services tab. NFS, CIFS and S3 are listed as enabled with their detail column showing the AD domain for CIFS, each with a Disable action, above the DNS domains and servers form" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DNS note&lt;/strong&gt;: an AD-joined SVM resolves its domain controllers through the servers set here. A wrong value breaks SMB, and on an AD-joined SVM it also makes S3 Access Point data operations fail with &lt;code&gt;AccessDenied&lt;/code&gt; — while &lt;code&gt;HeadBucket&lt;/code&gt; keeps succeeding, which sends you looking at IAM instead of the file system layer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Destructive operations are gated in both layers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SnapMirror break, resync and delete, Vscan and FPolicy policy deletion, peer deletion, and disabling a LIF or a protocol all show an inline confirmation row before anything is sent.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fajjmcxj6wqx4x6qe2s3x.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fajjmcxj6wqx4x6qe2s3x.png" alt="The Vscan panel with a delete confirmation row expanded under the policy, warning that deleting the policy stops its scope being scanned, with Execute and Cancel buttons" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The confirmation is not only in the UI. The Lambda refuses the same actions unless &lt;code&gt;confirm=true&lt;/code&gt; is present, so a direct call that bypasses the browser is rejected identically. That split matters: a UI-only guard is a suggestion, not a control.&lt;/p&gt;

&lt;p&gt;Getting this wrong is instructive. In the first cut, the Vscan and FPolicy delete buttons sent no &lt;code&gt;confirm&lt;/code&gt; flag and had no confirmation row, and the handler did not check for one either — while the documentation already claimed the operation was confirm-gated. The button appeared to work and the docs looked right. The fix was to make all three agree, and to pin the contract in tests: for every confirm-gated action, assert it refuses without &lt;code&gt;confirm&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; succeeds with exactly the parameters the UI sends. The second half of that assertion is the one that would have caught it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security Model
&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;Implementation&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;Cognito User Pool + MFA (TOTP/SMS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization (API)&lt;/td&gt;
&lt;td&gt;Cognito Groups (&lt;code&gt;storage-admin&lt;/code&gt; / &lt;code&gt;authenticated&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization (Files)&lt;/td&gt;
&lt;td&gt;S3 AP + UNIX/Windows file system identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transport encryption&lt;/td&gt;
&lt;td&gt;HTTPS (AppSync) + TLS 1.2 (ONTAP REST API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets management&lt;/td&gt;
&lt;td&gt;Secrets Manager (ONTAP credentials)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit&lt;/td&gt;
&lt;td&gt;CloudTrail S3 data events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WORM&lt;/td&gt;
&lt;td&gt;SnapLock (Compliance/Enterprise) + S3 Object Lock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ransomware protection&lt;/td&gt;
&lt;td&gt;ONTAP ARP/AI + portal containment actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHI protection&lt;/td&gt;
&lt;td&gt;Path-based guardrail (AI processing block)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Only users in the &lt;code&gt;storage-admin&lt;/code&gt; group can execute admin operations (volume creation, SnapLock configuration, ARP containment, etc.). Regular users are limited to file browsing and AI processing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Enterprise SSO note&lt;/strong&gt;: Cognito User Pool supports SAML 2.0 and OIDC federation. To integrate with an existing IdP such as Okta, Azure AD, or Google Workspace, see Cognito's &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-saml-idp.html" rel="noopener noreferrer"&gt;Adding SAML identity providers&lt;/a&gt;. Group mapping lets you map IdP groups to the Cognito &lt;code&gt;storage-admin&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure protection note&lt;/strong&gt;: Amplify Hosting is served via Amazon CloudFront, so AWS Shield Standard (DDoS protection) is automatically applied. If additional WAF rules are needed, attach AWS WAF to the CloudFront distribution.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Usage Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: Ransomware Response
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Storage Dashboard shows 🚨 ARP Threats: 1
2. Navigate to ARP/AI panel → view threat details
3. Click [Contain] → SMB user blocked + snapshot taken
4. State transitions to 🟠 Contained
5. After forensic analysis, [Investigation Complete] → [Resolve]
6. Unblock to resume normal operations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 2: Compliance Audit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Lock panel → SnapLock tab to review Compliance volume list
2. Tamperproof tab to apply FISC 7-year lock to snapshots
3. S3 Object Lock tab to verify output bucket retention settings
4. Audit Trail to review operation history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scenario 3: Daily Monitoring
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Storage Dashboard — glance at 4 cards (30 seconds)
2. If any volume exceeds 85% capacity, drill into Volume Manager
3. Check EMS Events for recent alerts
4. No issues → done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Design Trade-offs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Design Decision&lt;/th&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Trade-off&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;localStorage (Incident State)&lt;/td&gt;
&lt;td&gt;Simple deployment&lt;/td&gt;
&lt;td&gt;Not shared across browsers; incident state can be lost if the browser crashes. DynamoDB migration recommended for production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHI path regex&lt;/td&gt;
&lt;td&gt;Simple to implement&lt;/td&gt;
&lt;td&gt;Depends on the folder naming convention being followed. Contents are not inspected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic dispatch&lt;/td&gt;
&lt;td&gt;Avoids CFn 1MB limit&lt;/td&gt;
&lt;td&gt;IAM policy granularity is coarser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VPC split&lt;/td&gt;
&lt;td&gt;Cold start optimization&lt;/td&gt;
&lt;td&gt;Two types of Lambda required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promise.allSettled (Dashboard)&lt;/td&gt;
&lt;td&gt;Resilient to partial failures&lt;/td&gt;
&lt;td&gt;Failed cards show 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regulatory presets (hardcoded)&lt;/td&gt;
&lt;td&gt;Ready to use immediately&lt;/td&gt;
&lt;td&gt;Code change needed if regulations update&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Rollback Procedure
&lt;/h3&gt;

&lt;p&gt;Recovery paths if a portal update causes problems:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Rollback method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend UI issue&lt;/td&gt;
&lt;td&gt;Revert to the previous build from the Amplify Hosting console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda function issue&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git revert&lt;/code&gt; + &lt;code&gt;git push&lt;/code&gt; triggers automatic redeploy (Amplify Gen2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP configuration change issue&lt;/td&gt;
&lt;td&gt;Restore directly via ONTAP REST API (export-policy, name-mapping, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito configuration change&lt;/td&gt;
&lt;td&gt;CDK stack rollback is not available. Revert manually to the previous state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because frontend and backend are both under Git management, &lt;code&gt;git revert&lt;/code&gt; + &lt;code&gt;git push&lt;/code&gt; rolls back immediately. Some ONTAP-side changes (such as enabling SnapLock) are irreversible, so verification before execution matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  8-Language Support (i18n)
&lt;/h2&gt;

&lt;p&gt;All admin features included, the entire portal supports 8 languages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ja&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;日本語&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;en&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;English&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ko&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;한국어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zh-CN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;简体中文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zh-TW&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;繁體中文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Français&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;de&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deutsch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;es&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Español&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Technical terms (ONTAP, SnapLock, FlexClone, S3 AP, ARP/AI, WORM, FISC, SOX, HIPAA) are not translated. Browser &lt;code&gt;navigator.language&lt;/code&gt; auto-detection with instant switching via the header language picker.&lt;/p&gt;




&lt;h2&gt;
  
  
  Audit Log — Checking "Who Accessed What and When" in the UI
&lt;/h2&gt;

&lt;p&gt;The most frequent request from compliance officers was "I want to check the file access trail myself." Grepping CloudTrail logs from the CLI is not realistic for anyone outside the security team.&lt;/p&gt;

&lt;p&gt;The portal's Audit tab runs filtered queries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File path&lt;/td&gt;
&lt;td&gt;Show only accesses under &lt;code&gt;/contracts/&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event type&lt;/td&gt;
&lt;td&gt;READ / WRITE / ALL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time range&lt;/td&gt;
&lt;td&gt;2026-07-01 – 2026-07-28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AppSync Query → Lambda → Athena SQL → CloudTrail S3 Data Event logs
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frh1kzeq277eyvyi0105g.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frh1kzeq277eyvyi0105g.png" alt="The path for reviewing audit logs from the UI. The web browser calls an AWS Lambda function through AWS AppSync, and Amazon Athena runs the SQL. Athena reads the table definition from AWS Glue (Data Catalog) and scans the Amazon S3 logs where AWS CloudTrail recorded the S3 data events" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part2-audit-log-pipeline-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;CloudTrail S3 data events are registered as a Glue table and queried with Athena. Results display in table form, and access denials (AccessDenied) are highlighted in red.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enable data events for the S3 AP ARN on a CloudTrail Trail&lt;/li&gt;
&lt;li&gt;Create the Athena table via Glue Crawler or a manual &lt;code&gt;CREATE TABLE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;ATHENA_AUDIT_DATABASE&lt;/code&gt;, &lt;code&gt;ATHENA_AUDIT_TABLE&lt;/code&gt;, and &lt;code&gt;ATHENA_AUDIT_OUTPUT&lt;/code&gt; as Lambda environment variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If these are unconfigured, the UI displays "configuration required" guidance with the setup steps. The design treats this as onboarding rather than an error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Retention
&lt;/h3&gt;

&lt;p&gt;Query results are stored in the Athena workgroup output location (S3). Retention of the original CloudTrail logs is controlled by the S3 bucket lifecycle policy on the Trail's bucket. Set retention periods such as FISC 7 years or HIPAA 6 years according to your audit requirements.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cost note&lt;/strong&gt;: If you store audit trails in DynamoDB, storage costs grow in large environments. Set a retention policy (TTL) and archive old records to S3 Glacier. CloudTrail log retention is controlled separately via the Trail's lifecycle policy.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  FlexClone Restore — Instant Recovery from Snapshots
&lt;/h2&gt;

&lt;p&gt;When recovering from ransomware damage or accidental deletion, the requirement is "I want to go back to the snapshot from before the infection." Previously this meant creating a FlexClone from the CLI and attaching an S3 AP as a sequence of manual operations.&lt;/p&gt;

&lt;p&gt;In the portal, from the 📸 button on the Files tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Enter the snapshot name (e.g., daily.2026-07-18_0010)
2. Click [Clone &amp;amp; Attach]
3. Step Functions automatically runs FlexClone creation → S3 AP attach
4. FlexClone information appears on the Results tab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  FlexClone Status Display
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔄 FlexClone Volume
  Volume:  clone-uc6-20260718-abc123
  Parent:  vol_data
  Status:  🟢 online
  Created: 2026-07-18 15:00:00
  Size:    128 MB
  S3 AP:   clone-uc6-abc123-s3alias
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Cases
&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;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ransomware recovery&lt;/td&gt;
&lt;td&gt;Immediate access to a pre-infection snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forensics&lt;/td&gt;
&lt;td&gt;Isolate a point-in-time copy for investigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;Validate against a clone without touching production data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;FlexClone is a metadata-only copy, so it completes in seconds with near-zero additional capacity. Differential capacity is consumed only as writes occur.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Accessibility note&lt;/strong&gt;: Confirmation dialogs for FlexCache deletion and volume deletion are displayed inline. For screen reader users, confirmation messages are announced via &lt;code&gt;role="alertdialog"&lt;/code&gt; and &lt;code&gt;aria-describedby&lt;/code&gt;. Keyboard navigation (Tab/Enter/Escape) is fully supported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throughput note&lt;/strong&gt;: A FlexClone volume shares the throughput budget with its parent volume. If clones are used actively in parallel in production, account for the performance impact on the parent volume. For read-oriented uses such as forensics this is typically not a concern.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Athena Query — SQL Analytics on NAS Data
&lt;/h2&gt;

&lt;p&gt;When files on FSx for ONTAP are cataloged via Glue Crawler, you can run SQL queries using Athena. The portal's "Analytics" tab lets you enter a database name and SQL, and execute directly.&lt;/p&gt;

&lt;p&gt;There is no need to open the AWS Athena console separately — everything completes inside the portal.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI Layout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;┌──────────────────────────────────────────────────┐&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="k"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Athena&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;        &lt;span class="p"&gt;]&lt;/span&gt;        &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;├──────────────────────────────────────────────────┤&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;💡&lt;/span&gt; &lt;span class="n"&gt;How&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;catalog&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;FSx&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ONTAP&lt;/span&gt;    &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Glue&lt;/span&gt; &lt;span class="n"&gt;Crawler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;analyze&lt;/span&gt; &lt;span class="n"&gt;them&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;   &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="k"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;                                             &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;▸&lt;/span&gt; &lt;span class="err"&gt;📝&lt;/span&gt; &lt;span class="k"&gt;View&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collapsible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;├──────────────────────────────────────────────────┤&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;                           &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;                                                  &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                                      &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;└──────────────────────────────────────────────────┘&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First time&lt;/strong&gt;: run &lt;code&gt;SHOW TABLES IN default&lt;/code&gt; to discover available tables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Once you know table names&lt;/strong&gt;: execute specific SELECT queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Results&lt;/strong&gt;: displayed in table form&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Practical Query Examples
&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;-- Find large files (capacity planning)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_objects&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000000000&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;

&lt;span class="c1"&gt;-- Total size of a specific folder (project sizing)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&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;size&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;total_bytes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_objects&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'engineering/%'&lt;/span&gt;

&lt;span class="c1"&gt;-- Files modified in last 7 days (change tracking)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;key&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="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s3_objects&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;last_modified&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;current_date&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'7'&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The portal UI includes an expandable "View query examples" section, so even users unfamiliar with SQL can get started by copy-pasting.&lt;/p&gt;

&lt;p&gt;Not just "browse the files" but "ask questions of the data" — directly from the portal.&lt;/p&gt;

&lt;h3&gt;
  
  
  File Formats Glue Crawler Recognizes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Formats&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Structured data&lt;/td&gt;
&lt;td&gt;CSV, TSV, JSON, Parquet, ORC, Avro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;Apache/NGINX logs, CloudTrail JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documents&lt;/td&gt;
&lt;td&gt;— (text extraction requires Textract or Bedrock)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CAD/EDA binary files cannot be cataloged by Glue Crawler, but file metadata (size, last modified, path) is still retrievable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Athena Cost Optimization Tips
&lt;/h3&gt;

&lt;p&gt;Athena bills on the volume of data scanned ($5/TB). Ways to keep costs down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Date partitions (&lt;code&gt;year=/month=/day=&lt;/code&gt;) reduce scan volume when a WHERE clause narrows the time range&lt;/li&gt;
&lt;li&gt;With columnar formats such as Parquet/ORC, specifying only the columns you need instead of &lt;code&gt;SELECT *&lt;/code&gt; reduces cost&lt;/li&gt;
&lt;li&gt;Make it a habit to always add a &lt;code&gt;LIMIT&lt;/code&gt; clause to exploratory queries&lt;/li&gt;
&lt;li&gt;Setting a query limit on the Athena workgroup (for example 10 GB/day) caps unexpected scan volume&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Cost Awareness
&lt;/h2&gt;

&lt;p&gt;Rough additional cost if every feature in this article is enabled. Unit prices vary by Region and change over time, so price an actual deployment from each service's pricing page:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Estimate&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;VPC Lambda (Admin)&lt;/td&gt;
&lt;td&gt;~$5/month&lt;/td&gt;
&lt;td&gt;Depends on invocation frequency. Negligible for daily checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudTrail S3 Data Events&lt;/td&gt;
&lt;td&gt;~$10–$50/month&lt;/td&gt;
&lt;td&gt;Proportional to file access volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena queries&lt;/td&gt;
&lt;td&gt;$5/TB scanned&lt;/td&gt;
&lt;td&gt;Audit queries scan little; partitions reduce it further&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;$0.40/secret/month&lt;/td&gt;
&lt;td&gt;One ONTAP credential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DemoMode&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;No FSx for ONTAP needed. Runs on S3 alone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cost of FSx for ONTAP itself (~$194/month at 128 MBps) was covered in Part 1. The portal's incremental cost depends on usage frequency, but evaluating in DemoMode adds nothing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment Time Estimates
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Prerequisite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DemoMode deploy&lt;/td&gt;
&lt;td&gt;~15 min&lt;/td&gt;
&lt;td&gt;Amplify CLI installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add VPC + ONTAP connectivity&lt;/td&gt;
&lt;td&gt;~30 min&lt;/td&gt;
&lt;td&gt;FSx for ONTAP file system running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit Log configuration&lt;/td&gt;
&lt;td&gt;~20 min&lt;/td&gt;
&lt;td&gt;CloudTrail + Glue Crawler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full feature deploy&lt;/td&gt;
&lt;td&gt;~60 min&lt;/td&gt;
&lt;td&gt;All of the above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What to Expect After Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Once the DemoMode deploy completes, the portal is reachable at &lt;code&gt;https://&amp;lt;branch&amp;gt;.amplifyapp.com&lt;/code&gt;. Cognito sign-up → log in → the file list shows test data from the S3 bucket&lt;/li&gt;
&lt;li&gt;Once VPC connectivity is added, the Storage Dashboard shows actual volume counts and utilization, and protected volumes appear in the ARP panel&lt;/li&gt;
&lt;li&gt;Once all features are deployed, the Audit Log tab can run CloudTrail queries and the Lock panel shows SnapLock/Object Lock settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Environment Variables and Configuration Parameters
&lt;/h3&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;Description&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;DEMO_MODE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run without FSx for ONTAP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; (PoC) / &lt;code&gt;false&lt;/code&gt; (production)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ONTAP_SECRET_ARN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager ARN for ONTAP credentials&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:fsxn-admin-XXXXXX&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATHENA_AUDIT_DATABASE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Glue database name for audit queries&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cloudtrail_logs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATHENA_AUDIT_TABLE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Athena table name for audit queries&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s3_data_events&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ATHENA_AUDIT_OUTPUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;S3 location for Athena query results&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s3://my-audit-results/athena-output/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  PoC → Production Checklist
&lt;/h3&gt;

&lt;p&gt;After evaluating in DemoMode, use this checklist when moving to production connectivity:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Verification&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;VPC subnet contains the FSx for ONTAP ENIs; Security Group allows TCP 443 (management LIF). Note: NFS (2049) / SMB (445) are for data access and are not needed for the portal's admin operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;Cognito User Pool MFA enabled; &lt;code&gt;storage-admin&lt;/code&gt; group members confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets&lt;/td&gt;
&lt;td&gt;ONTAP credentials registered in Secrets Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit&lt;/td&gt;
&lt;td&gt;CloudTrail Trail with S3 data events enabled for the S3 AP ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;portal-config.ts&lt;/code&gt; values in Git (secrets excluded)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;CloudTrail data event estimate confirmed (proportional to access volume)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSO&lt;/td&gt;
&lt;td&gt;(Optional) SAML/OIDC federation configured if using an enterprise IdP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Role-Based Documentation (8 Languages × 3 Guides)
&lt;/h2&gt;

&lt;p&gt;Alongside the feature additions, role-specific documentation was built. Based on usability principles (visibility of system status, task-oriented structure, ease of error recovery), 3 guides are provided in 8 languages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Target Role&lt;/th&gt;
&lt;th&gt;Key Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End users&lt;/td&gt;
&lt;td&gt;Sign in, file operations, AI processing, FAQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compliance Guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security/Compliance officers&lt;/td&gt;
&lt;td&gt;ARP verification, WORM checks, audit trail, PHI guardrail validation, regulatory mapping (FISC/HIPAA/SOX/NIST/ISO)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quick Reference&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All roles&lt;/td&gt;
&lt;td&gt;1-page cheat sheet (navigation, tasks by role, status indicators, troubleshooting)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Supported languages: 日本語, English, 한국어, 简体中文, 繁體中文, Français, Deutsch, Español&lt;/p&gt;

&lt;p&gt;Design choices for the compliance officer guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All tasks executable without &lt;code&gt;storage-admin&lt;/code&gt; privileges (read-only access is sufficient)&lt;/li&gt;
&lt;li&gt;Each task includes "Evidence for auditors" instructions&lt;/li&gt;
&lt;li&gt;Explicit "What you cannot do" escalation table showing who to contact&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FlexCache / SnapMirror Management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Direct ONTAP REST API Operations from the Browser
&lt;/h3&gt;

&lt;p&gt;FlexCache, FlexClone, SnapMirror, Vscan, FPolicy, cluster and SVM peering, and the cluster services are all managed through the browser UI — 110 actions in total. A VPC-deployed Lambda connects to the FSx for ONTAP management endpoint via HTTPS, calling ONTAP REST API with credentials from Secrets Manager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser → AppSync (Cognito auth) → Lambda (in VPC) → ONTAP REST API
                                                       ↓
                                                Secrets Manager
                                                (fsxadmin credentials)
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9n3edt7rht9yvk3rfcgn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9n3edt7rht9yvk3rfcgn.png" alt="Reaching the ONTAP REST API from the browser. The web browser authenticates with Cognito and connects to AWS AppSync; an AWS Lambda function inside the VPC reads the fsxadmin credentials from AWS Secrets Manager and calls the ONTAP REST API on Amazon FSx for NetApp ONTAP. The Lambda sits in the VPC because the ONTAP management LIF is private" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part2-ontap-rest-api-path-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  FlexCache Create UI
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmkdko18hihx8h37791x.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmkdko18hihx8h37791x.png" alt="The FlexCache create screen in the portal. The origin volume is picked from a dropdown, then cache name, size and prepopulate paths are entered before creating. Existing caches and their state are listed on the same screen" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the origin volume from a dropdown (datalist), specify cache name, size, and prepopulate paths, then create. The experience mirrors ONTAP System Manager — accessible from any browser with Cognito authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  SnapMirror Lifecycle Management
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkha0ihymyj350i0y8sj8.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkha0ihymyj350i0y8sj8.png" alt="The SnapMirror list screen in the portal. The relationship shows source and destination paths, its policy, a Broken-off state badge, and the action buttons that apply in that state including Resync and Delete" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The replication relationship list shows state badges (✅ Snapmirrored / 🔴 Broken-off / 🔄 Transferring / ⏸️ Paused) with context-sensitive action buttons. DR failover (Break → Resync) is fully accessible from the browser. Lag times exceeding your RPO target display in red with a ⚠️ RPO warning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FPolicy — Event Definitions and Notification Policies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FPolicy is where file access notification is configured: an event definition&lt;br&gt;
names the protocol and the operations to watch, and a policy subscribes to those&lt;br&gt;
events and points at an engine. The panel exposes all of it — create, enable,&lt;br&gt;
disable, delete — across three tabs for policies, events and connections.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjkjwchhkvf3dwv7xnt5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpjkjwchhkvf3dwv7xnt5.png" alt="The Policies tab of the FPolicy panel. One row shows the audit_all policy as enabled with priority 1, engine external and the file_ops_cifs event set, alongside Disable and Delete actions. Delete is not clickable because the policy is enabled" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the Delete button on the enabled policy. ONTAP refuses to delete a policy&lt;br&gt;
while it is enabled, so the panel disables the control rather than letting the&lt;br&gt;
call fail. Disable first, then delete. The same ordering applies through the&lt;br&gt;
REST API, and it is the kind of constraint that is easier to encode in the UI&lt;br&gt;
than to explain in a runbook.&lt;/p&gt;
&lt;h3&gt;
  
  
  Where the Portal Is Self-Contained, and Where It Needs Something Else
&lt;/h3&gt;

&lt;p&gt;The containment actions in this portal are a port of &lt;code&gt;ontap_response.py&lt;/code&gt; from a&lt;br&gt;
separate repository, &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations" rel="noopener noreferrer"&gt;fsxn-observability-integrations&lt;/a&gt;&lt;br&gt;
— the module docstring records it. The ONTAP mechanisms are identical:&lt;br&gt;
name-mapping deny, export-policy deny rule, protective snapshot, CIFS session&lt;br&gt;
disconnect. What differs is the trigger, and the layers around it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-contained in the portal&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;Capability&lt;/th&gt;
&lt;th&gt;Requires&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Review ARP/AI state, attack probability and suspect files&lt;/td&gt;
&lt;td&gt;The portal reaching the ONTAP REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block and unblock an SMB user or an NFS client IP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;storage-admin&lt;/code&gt; group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a protective snapshot; lock a snapshot (WORM)&lt;/td&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disconnect CIFS sessions&lt;/td&gt;
&lt;td&gt;An AD-joined SVM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List active blocks and lift them individually&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block expiry and automatic lifting (24 hours by default)&lt;/td&gt;
&lt;td&gt;The block ledger (DynamoDB) being reachable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply the same block across several SVMs (&lt;code&gt;svms&lt;/code&gt; or &lt;code&gt;allSvms&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit file access that went through the S3 Access Point&lt;/td&gt;
&lt;td&gt;CloudTrail data events + Athena&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexClone a snapshot to browse it; diff two generations&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every one of these &lt;strong&gt;starts when a person clicks&lt;/strong&gt;. Nothing in the portal&lt;br&gt;
contains a threat unattended — expiry is the one exception, and it only ever&lt;br&gt;
works in the direction of ending a lockout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Needs something outside the portal&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;Goal&lt;/th&gt;
&lt;th&gt;What it takes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contain without waiting for a human&lt;/td&gt;
&lt;td&gt;An SNS topic plus a response Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Be told about a detection instead of finding it&lt;/td&gt;
&lt;td&gt;EMS → webhook → SIEM or an observability platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cut NFS off without the client cache window&lt;/td&gt;
&lt;td&gt;A VPC NACL deny rule (network layer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Judge a recovery point before restoring from it&lt;/td&gt;
&lt;td&gt;A verification workflow (FlexClone + isolated scan)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detect anomalies against a per-user ML baseline&lt;/td&gt;
&lt;td&gt;A SIEM with anomaly detection, or a dedicated storage security product&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trace file access that arrived over NFS or SMB directly&lt;/td&gt;
&lt;td&gt;An ONTAP audit log / FPolicy delivery pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The audit boundary is the one most likely to be misread. The portal's audit trail&lt;br&gt;
reads CloudTrail S3 data events for the S3 Access Point. Access that arrived over&lt;br&gt;
NFS or SMB does not appear there at all — that needs ONTAP's own audit log or&lt;br&gt;
FPolicy events. The two are complementary, not substitutes.&lt;/p&gt;

&lt;p&gt;The delivery pipeline is still yours to build, but there is now somewhere for it&lt;br&gt;
to deliver to. Once FPolicy (or Transfer Family) publishes to EventBridge, a&lt;br&gt;
bridge Lambda writes what arrives into a notification table and a &lt;strong&gt;Folder&lt;br&gt;
Watch&lt;/strong&gt; section shows the events under the prefixes you are watching. It is&lt;br&gt;
enabled by an admin toggle and off by default: until an admin states that a&lt;br&gt;
publisher exists, turning it on would only ever show an inbox that cannot fill.&lt;br&gt;
Filtering applies the Cognito group path boundary first and each user’s own&lt;br&gt;
watches second. A watch is your own record, so you may register &lt;code&gt;/&lt;/code&gt;, but that&lt;br&gt;
cannot reveal anything outside the group boundary.&lt;/p&gt;

&lt;p&gt;So the portal is the &lt;strong&gt;hands&lt;/strong&gt; of incident response: it puts the ONTAP&lt;br&gt;
containment actions in a browser, behind Cognito groups, with confirmation and an&lt;br&gt;
audit trail. If you need something watching around the clock and moving those&lt;br&gt;
hands for you, detection and response belong in a pipeline. Conversely, if you&lt;br&gt;
already detect in a SIEM and only lack a way to stop it at the storage layer, an&lt;br&gt;
SNS-triggered response Lambda fits that shape better than a portal button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One click was too few&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing this section is what exposed the gap worth fixing. A block removes a&lt;br&gt;
principal's data access across the whole SVM. The first version executed on a&lt;br&gt;
single click, with no confirmation and no &lt;code&gt;confirm&lt;/code&gt; parameter in the payload —&lt;br&gt;
the only guards were the Cognito group and a protected-accounts list. At the time&lt;br&gt;
there was also no expiry, so a block stayed until a person lifted it.&lt;/p&gt;

&lt;p&gt;There are now two gates. The UI asks, with wording specific to the action,&lt;br&gt;
because the consequences differ: an NFS block is subject to client-side attribute&lt;br&gt;
caching for up to 60 seconds, an SMB block is not, and disconnecting sessions on&lt;br&gt;
its own does not stop the next login. The Lambda then refuses any call that&lt;br&gt;
arrives without &lt;code&gt;confirm: true&lt;/code&gt;, in front of the ONTAP client construction — a&lt;br&gt;
dialog in a browser is a suggestion, and anything calling AppSync directly skips&lt;br&gt;
it.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5fk4v9e8gk1vlcrmlit.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5fk4v9e8gk1vlcrmlit.png" alt="The containment form in the portal with domain, username, client IP and reason filled in. Below the four action buttons, a confirmation row explains that the action creates a snapshot, blocks the targets and disconnects their SMB sessions across the whole SVM, with Run and Cancel buttons" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Session disconnect became its own action in the same pass. It existed in the&lt;br&gt;
handler but only ever ran as step 4 of full containment, so an operator who had&lt;br&gt;
already blocked a user had no way to cut the sessions that survived the block.&lt;br&gt;
Order matters: block first, then disconnect. Disconnecting before the block is in&lt;br&gt;
place just invites a successful reconnect.&lt;/p&gt;
&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Async Operations with &lt;code&gt;return_timeout=0&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;FlexCache creation is an asynchronous ONTAP job taking 30-120 seconds. By specifying &lt;code&gt;return_timeout=0&lt;/code&gt;, ONTAP returns immediately with 202 Accepted + job UUID, preventing Lambda timeout. The UI auto-refreshes the list at 10s/30s/60s intervals.&lt;/p&gt;
&lt;h4&gt;
  
  
  One Wrong Field Name Empties the Whole List
&lt;/h4&gt;

&lt;p&gt;The SnapMirror list was requesting &lt;code&gt;fields=...,last_transfer_type,last_transfer_size&lt;/code&gt;. &lt;code&gt;last_transfer_size&lt;/code&gt; is not a field on &lt;code&gt;/snapmirror/relationships&lt;/code&gt;, and ONTAP rejects the &lt;em&gt;entire&lt;/em&gt; request when a single field name is unknown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The value "last_transfer_size" is invalid for field "fields" (&amp;lt;field,...&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So every relationship vanished — not with a visible error, but as an empty list. What made this survive the test suite is worth noting: the mock ONTAP in the unit tests returns records regardless of which &lt;code&gt;fields&lt;/code&gt; are requested, so a test that asserts on the mapped response passes either way. The fix was to assert on the outgoing query string instead. Per-transfer byte counts were already available from &lt;code&gt;/snapmirror/relationships/{uuid}/transfers&lt;/code&gt;, so nothing was lost by dropping the field.&lt;/p&gt;

&lt;h4&gt;
  
  
  Automated 3-Step FlexCache Deletion
&lt;/h4&gt;

&lt;p&gt;Mounted FlexCache volumes cannot be deleted directly. The portal automates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove junction path (unmount)&lt;/li&gt;
&lt;li&gt;Set volume offline&lt;/li&gt;
&lt;li&gt;Delete FlexCache (async)&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Credential Synchronization
&lt;/h4&gt;

&lt;p&gt;When changing the &lt;code&gt;fsxadmin&lt;/code&gt; password, update &lt;strong&gt;both&lt;/strong&gt; the FSx for ONTAP API and Secrets Manager simultaneously. Updating only one side causes authentication failures that can trigger ONTAP's account lockout mechanism. Recovery: &lt;code&gt;aws fsx update-file-system&lt;/code&gt; password reset → Secrets Manager sync.&lt;/p&gt;

&lt;h3&gt;
  
  
  FSx for ONTAP Specifics
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;FSx for ONTAP&lt;/th&gt;
&lt;th&gt;On-prem ONTAP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Aggregate specification&lt;/td&gt;
&lt;td&gt;Not needed (auto-selected)&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVM creation&lt;/td&gt;
&lt;td&gt;AWS API only&lt;/td&gt;
&lt;td&gt;CLI/REST available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intra-cluster FlexCache&lt;/td&gt;
&lt;td&gt;Supported (no peering needed)&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fsxadmin password reset&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws fsx update-file-system&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;security login password&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Coexistence with Existing Tools
&lt;/h3&gt;

&lt;p&gt;This portal is not a replacement for the ONTAP CLI or REST API. It makes a subset of those operations accessible from a browser UI with Cognito authentication — an additional management layer, not a substitute.&lt;/p&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;Portal's Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Using cloud file-sharing SaaS&lt;/td&gt;
&lt;td&gt;AI processing and audit for large-scale NAS data only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running self-hosted file sharing&lt;/td&gt;
&lt;td&gt;Add S3 AP as External Storage for management operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using hybrid file services&lt;/td&gt;
&lt;td&gt;Add data protection and ARP/AI visibility layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating daily through the ONTAP CLI / REST API&lt;/td&gt;
&lt;td&gt;Provide visibility to non-storage-admin team members&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The portal provides a management layer for NAS-specific capabilities (Snapshot, FlexClone, FlexCache, SnapMirror, ARP/AI) accessible from the browser. Adoption can be incremental — management operations only, AI processing only, or the full suite — depending on existing workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  PoC → Production Migration Flow
&lt;/h3&gt;

&lt;p&gt;After evaluating in DemoMode (no FSx for ONTAP required), migrate to production connectivity in three phases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────┐     ┌────────────────┐     ┌────────────────┐
│ Phase 1: PoC   │     │ Phase 2: VPC   │     │ Phase 3: Prod  │
│ (~15 min)      │     │   Connectivity │     │   Hardening    │
│                │     │ (~30 min)      │     │ (~60 min)      │
│ DemoMode=true  │ ──→ │ ONTAP mgmt LIF │ ──→ │ Least-privilege│
│ S3 bucket      │     │ Secrets Mgr    │     │ MFA required   │
│ Auth: Cognito  │     │ VPC Endpoint   │     │ WAF added      │
│                │     │                │     │ Audit enabled  │
└────────────────┘     └────────────────┘     └────────────────┘
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ci2y0n59jy5pir4k0xb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ci2y0n59jy5pir4k0xb.png" alt="Three phases from PoC to production connectivity. Phase 1 runs with DemoMode=true against an S3 bucket only; Phase 2 adds the ONTAP management LIF connection, AWS Secrets Manager, and VPC endpoints; Phase 3 applies least-privilege IAM, required MFA, AWS WAF, and audit logging" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/part2-poc-to-production-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Added Cost&lt;/th&gt;
&lt;th&gt;What You Get&lt;/th&gt;
&lt;th&gt;What Changes&lt;/th&gt;
&lt;th&gt;What Stays the Same&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 (PoC)&lt;/td&gt;
&lt;td&gt;~15 min&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;UI/UX evaluation, AI processing test&lt;/td&gt;
&lt;td&gt;Nothing (fresh deploy)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 (VPC)&lt;/td&gt;
&lt;td&gt;~30 min&lt;/td&gt;
&lt;td&gt;Lambda VPC ~$5/mo&lt;/td&gt;
&lt;td&gt;ONTAP admin operations, dashboard&lt;/td&gt;
&lt;td&gt;Lambda VPC config added, Secrets Manager registered&lt;/td&gt;
&lt;td&gt;Frontend UI, Cognito settings, S3 bucket config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 (Prod)&lt;/td&gt;
&lt;td&gt;~60 min&lt;/td&gt;
&lt;td&gt;CloudTrail ~$10–50/mo&lt;/td&gt;
&lt;td&gt;Audit, WAF, MFA, least-privilege IAM&lt;/td&gt;
&lt;td&gt;IAM policy tightening, WAF attachment, MFA enforcement&lt;/td&gt;
&lt;td&gt;Application code, ONTAP connection settings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Post-Deployment Verification
&lt;/h3&gt;

&lt;p&gt;Expected state after each phase completes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;After Phase 1&lt;/strong&gt;: you can log in to the portal, and DemoMode file listing and AI processing tests work. The Storage Dashboard still shows "not connected"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After Phase 2&lt;/strong&gt;: the Storage Dashboard shows actual volume information, and ONTAP data appears in the ARP panel and EMS Events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After Phase 3&lt;/strong&gt;: login is impossible without MFA, CloudTrail records all operations, and WAF applies rate limiting&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Irreversible operations warning&lt;/strong&gt;: The following operations cannot be undone once executed. Verify your organization's policies before enabling them in Phase 2+:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SnapLock Compliance enablement&lt;/strong&gt;: Volume SnapLock type cannot be changed after creation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tamperproof Snapshot retention period&lt;/strong&gt;: Once set, the period cannot be shortened (only extended)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 Object Lock Compliance mode&lt;/strong&gt;: No one, including root, can delete objects until retention expires&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;For detailed migration steps, see the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization Model (PoC → Production)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;PoC&lt;/th&gt;
&lt;th&gt;Production&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AppSync auth&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allow.authenticated()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allow.groups(["storage-admin"])&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM resource scope&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"*"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Specific ARN patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;generateClient&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;authMode: "userPool"&lt;/code&gt; required&lt;/td&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In Amplify Gen2 with multiple auth providers, &lt;code&gt;generateClient()&lt;/code&gt; must explicitly specify &lt;code&gt;authMode: "userPool"&lt;/code&gt; to ensure the Cognito token is sent to AppSync. This is a recommended pattern per official documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/amplify-portal" rel="noopener noreferrer"&gt;GitHub: solutions/amplify-portal/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-user-guide.md" rel="noopener noreferrer"&gt;User Guide (8 languages)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-compliance-guide.md" rel="noopener noreferrer"&gt;Compliance Guide (8 languages)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-quick-reference.md" rel="noopener noreferrer"&gt;Quick Reference (8 languages)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/solutions/amplify-portal/docs/IMPLEMENTATION.md" rel="noopener noreferrer"&gt;Implementation Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/solutions/amplify-portal/docs/GETTING-STARTED.md" rel="noopener noreferrer"&gt;Getting Started Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/admin-resource-management-demo.md" rel="noopener noreferrer"&gt;Admin Demo Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/solutions/amplify-portal/docs/ai-agent-demo-guide.en.md" rel="noopener noreferrer"&gt;AI Agent Demo Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/security-review-template.md" rel="noopener noreferrer"&gt;Security review template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-scaling-guide.md" rel="noopener noreferrer"&gt;Scaling Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-accessibility.md" rel="noopener noreferrer"&gt;Accessibility Statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/anti-ransomware-vol.html" rel="noopener noreferrer"&gt;AWS: ONTAP ARP/AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html" rel="noopener noreferrer"&gt;AWS: S3 Object Lock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887"&gt;Part 1: Adding a File Portal to FSx for ONTAP S3 Access Points&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/embedding-ai-agents-into-a-file-portal-from-agentcore-mcp-to-multi-agent-teams-part-3-19m1"&gt;Part 3: Embedding AI Agents into a File Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/putting-fsx-for-ontap-operations-on-a-file-portal-on-aws-182-actions-and-the-design-of-425g"&gt;Part 4: Putting FSx for ONTAP operations on a file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/what-i-learned-driving-fsx-for-ontap-from-a-file-portal-on-aws-flexgroup-creation-capacity-3gkd"&gt;Part 5: What I learned driving FSx for ONTAP from a file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/what-i-left-off-the-file-portal-on-aws-how-far-fsx-for-ontap-features-reach-and-the-work-handed-hk8"&gt;Part 6: What I left off the file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary and Next Steps
&lt;/h2&gt;

&lt;p&gt;Building on the file portal from Part 1, ONTAP management operations and data protection features were embedded:&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;What it enables&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage Dashboard&lt;/td&gt;
&lt;td&gt;Health at a glance immediately after login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident Lifecycle&lt;/td&gt;
&lt;td&gt;Ransomware response with state management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHI Guardrail&lt;/td&gt;
&lt;td&gt;Refuses AI processing on regulated folders by path name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Object Lock&lt;/td&gt;
&lt;td&gt;Tamper prevention for AI results, configurable from UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMS Events&lt;/td&gt;
&lt;td&gt;ONTAP alerts without CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retention Presets&lt;/td&gt;
&lt;td&gt;Just pick the regulation, get the right retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit Log&lt;/td&gt;
&lt;td&gt;"Who accessed what and when" — self-service for compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexClone Restore&lt;/td&gt;
&lt;td&gt;Recover from snapshot in seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexCache CRUD&lt;/td&gt;
&lt;td&gt;Create/delete read cache volumes from browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapMirror Lifecycle&lt;/td&gt;
&lt;td&gt;Sync, break, resync, quiesce — DR from a browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena Query&lt;/td&gt;
&lt;td&gt;Ask SQL questions against NAS data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Welcome Modal&lt;/td&gt;
&lt;td&gt;First-time users productive in 10 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From a portal that "just browses files" to one that "completes storage operations from the web." Daily monitoring and incident first response now finish in the browser without opening an ONTAP CLI session.&lt;/p&gt;

&lt;p&gt;All code is available in the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/amplify-portal" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a next step, after running DemoMode using the instructions in Part 1, try adding the VPC configuration to enable ONTAP connectivity. That gets you to a state where every feature in this article is usable. Migration details are collected in the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Part 3, I'll write about embedding AI agents into this portal — completing file operations, analysis, and admin operations in natural language. Parts 4 to 6 continue into delegating operations, what the running cluster stopped, and what was left off the screen.&lt;/p&gt;

&lt;p&gt;I hope this post helps someone out there.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>amplify</category>
      <category>fsxforontap</category>
      <category>security</category>
    </item>
    <item>
      <title>Reaching an FSx for ONTAP NAS from a browser through an S3 Access Point — Amplify Gen 2 or Nextcloud (File Portal Part 1)</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Mon, 03 Aug 2026 10:16:29 +0000</pubDate>
      <link>https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887</link>
      <guid>https://dev.to/aws-builders/adding-a-file-portal-to-fsx-for-ontap-s3-access-points-choosing-between-amplify-gen2-and-887</guid>
      <description>&lt;p&gt;&lt;strong&gt;Reach the NAS from a browser and from AI, without stopping existing mounts&lt;/strong&gt; — choosing between Amplify Gen2 and Nextcloud (part 1 of 6)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Japanese version of this article is available: &lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-file-portal-1-browser-access" rel="noopener noreferrer"&gt;日本語版&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I've been asked more than a few times how to open files sitting on a NAS in a browser and run AI over them. The data lives on NFS/SMB volumes and runs to hundreds of terabytes — between the sheer volume, the latency, and data-residency rules, copying it out to an external service isn't realistic. Requiring an NFS mount instead puts the data out of reach for anyone who isn't an engineer. Closing that gap felt worth doing, so I built a verification environment for it.&lt;/p&gt;

&lt;p&gt;What I used is Amazon FSx for NetApp ONTAP S3 Access Points (hereafter FSx for ONTAP S3 AP, or S3 AP — an access point that exposes NAS volumes for read/write through the S3 API). Because it reaches the data in place, you can add a browser UI and AI processing while leaving existing NFS/SMB workflows exactly as they are. I implemented the portal with two approaches: Amplify Gen2 as an AI-processing dashboard, and Nextcloud as a file-sharing UI.&lt;/p&gt;

&lt;p&gt;Here's the conclusion up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S3 AP gives you browser access to NAS volumes with no data migration&lt;/li&gt;
&lt;li&gt;Amplify Gen2 and Nextcloud aren't mutually exclusive — both can attach to the same volume at the same time&lt;/li&gt;
&lt;li&gt;The deciding axis: Nextcloud when file sharing is the center of gravity, Amplify Gen2 when AI processing and ONTAP admin operations are&lt;/li&gt;
&lt;li&gt;With DemoMode you can bring the portal up in under 30 minutes without FSx for ONTAP, and try file browsing and AI processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I'll walk through the design of the two approaches and how to choose between them, the DemoMode startup steps, and the places where I got stuck during implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who this is for&lt;/strong&gt;: anyone who wants to reach files on a NAS from a browser and run AI over them, and the frontend or full-stack developer building that screen. Operating the storage itself — volumes, SnapMirror, SnapLock — starts in &lt;a href="https://dev.to/aws-builders/putting-fsx-for-ontap-operations-on-a-file-portal-on-aws-182-actions-and-the-design-of-425g"&gt;Part 4&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this article does not cover&lt;/strong&gt;: absolute performance figures, production hosting steps (this is an Amplify sandbox), tenant isolation design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teardown&lt;/strong&gt;: a volume that has carried an S3 Access Point can keep a NAS bucket on the ONTAP side after every access point is detached, and ONTAP then refuses to delete the volume. Tear down with &lt;code&gt;aws fsx delete-volume&lt;/code&gt; (&lt;a href="https://dev.to/aws-builders/what-i-left-off-the-file-portal-on-aws-how-far-fsx-for-ontap-features-reach-and-the-work-handed-hk8"&gt;measured in Part 6&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Here's the overall picture first.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx0168qcel1pv3om6m1s2.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx0168qcel1pv3om6m1s2.png" alt="File portal architecture built on FSx for ONTAP S3 Access Points. Users reach two frontends from a web browser — Amplify Gen2 as an AI processing dashboard and Nextcloud as a file sharing UI — and both read and write the same FSx for ONTAP volume through one S3 Access Point. Existing NFS and SMB clients access the same volume concurrently" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/architecture-overview-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available, and every figure is listed in the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/architecture-diagrams.en.md" rel="noopener noreferrer"&gt;architecture diagram index&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: Overall architecture — two frontends reaching the same volume through one S3 Access Point&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The part worth noticing is that neither frontend moves any data. The S3 Access Point reads the existing NFS/SMB volume in place, so the same files stay available to existing clients at the same time.&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;Role it plays&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amplify Gen2&lt;/td&gt;
&lt;td&gt;AI processing dashboard (file selection → AI trigger → result display)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nextcloud&lt;/td&gt;
&lt;td&gt;File sharing UI (browsing, sync, sharing links)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pick based on what you want to do. Both can connect to the same volume simultaneously, so you can start incrementally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;License&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/LICENSE" rel="noopener noreferrer"&gt;MIT License&lt;/a&gt;. Commercial use, modification, and redistribution permitted.&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;Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites and Positioning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Audience&lt;/strong&gt;: Anyone with unstructured data on NAS who wants to protect and leverage that data. On-premises NAS, cloud NAS (FSx for ONTAP, EFS, Azure NetApp Files, etc.) — the platform doesn't matter.&lt;/p&gt;

&lt;p&gt;File data on NAS shares several common challenges:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data grows but stays underutilized&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Files accumulate year over year — ownership unclear, labels missing, search difficult (&lt;a href="https://cloudsecurityalliance.org/articles/unstructured-data-surges-as-enterprises-struggle-to-maintain-visibility-and-security-cloud-security-alliance-study-finds" rel="noopener noreferrer"&gt;CSA study&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ransomware target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared drives are primary targets for encryption attacks. Without detection/stop mechanisms, damage spreads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Want AI but can't move data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data volume, latency requirements, and data residency regulations make copying to external services impractical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Limited remote access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VPN + mount is required — browser-based access needs additional infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit &amp;amp; compliance overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Access logs exist but answering "who accessed what when" in a UI requires a separate analytics layer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This project implements approaches to these challenges using &lt;strong&gt;FSx for ONTAP S3 Access Points&lt;/strong&gt;. FSx for ONTAP was chosen because it combines NFS/SMB/S3 multi-protocol access with integrated data protection: Snapshots, SnapLock (WORM), FlexClone, and ARP/AI (autonomous ransomware detection).&lt;/p&gt;

&lt;p&gt;How you use the portal depends on your environment:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your Environment&lt;/th&gt;
&lt;th&gt;How to Use This Portal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Considering migration from on-prem NAS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build FSx for ONTAP + S3 AP for browser access, AI processing, and data protection in one step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Already using FSx for ONTAP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create an S3 AP against the existing volume and connect. IAM, network reachability and &lt;strong&gt;a volume security style matching the access point identity&lt;/strong&gt; are prerequisites — a mismatch attaches as &lt;code&gt;AVAILABLE&lt;/code&gt; and then fails every data operation with &lt;code&gt;AccessDenied&lt;/code&gt; (&lt;a href="https://dev.to/aws-builders/what-i-left-off-the-file-portal-on-aws-how-far-fsx-for-ontap-features-reach-and-the-work-handed-hk8"&gt;measured in Part 6&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NAS + SaaS side by side&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep SaaS as-is. Add AI processing, audit, and protection for NAS data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Using another cloud NAS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Move onto FSx for ONTAP first. SnapMirror replicates between ONTAP systems and preserves permissions and snapshots; DataSync copies between unlike systems and preserves a different set of metadata. Which one you use changes what survives the move&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What this portal provides&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser access to NAS files (no VPN or mount setup on the user side)&lt;/li&gt;
&lt;li&gt;AI analysis, classification, semantic search (Bedrock integration)&lt;/li&gt;
&lt;li&gt;ONTAP-specific data protection (Snapshot, SnapLock, FlexClone, ARP/AI)&lt;/li&gt;
&lt;li&gt;Audit trail visibility (CloudTrail + Athena)&lt;/li&gt;
&lt;li&gt;Sharing links (Presigned URLs), QR code access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How it differs from a browser-native file sharing service&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No online simultaneous editing. Co-authoring happens on the SMB/NFS mount side&lt;/li&gt;
&lt;li&gt;No desktop sync client (NFS/SMB mount serves this purpose)&lt;/li&gt;
&lt;li&gt;No native mobile app (responsive web UI as alternative)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Whatever file sharing service you already use, keep it.&lt;/strong&gt; What this portal adds is browser access, AI processing, an audit trail and data protection for the data that stays on the NAS. Authentication is independent through Cognito (SAML/OIDC federation supported), so existing contracts and licences are unaffected.&lt;/p&gt;

&lt;p&gt;The shape this fits is an environment where the files live in one place and the processing you want belongs somewhere else. Two of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare&lt;/strong&gt;: DICOM images on the NAS, clinical data in a separate system. What gets added is browser access and retention management on the image side only. A deployable implementation of that shape is &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/industry/healthcare-dicom" rel="noopener noreferrer"&gt;healthcare-dicom&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manufacturing&lt;/strong&gt;: CAD files on NFS, day-to-day document sharing left where it is, AI quality inspection added on the CAD files alone. The implementation is &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/industry/manufacturing-analytics" rel="noopener noreferrer"&gt;manufacturing-analytics&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other sectors — financial document processing, media, legal, research — sit on the same base. The overview is &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/industry-workload-mapping.md" rel="noopener noreferrer"&gt;industry workload mapping&lt;/a&gt; and the implementations are under &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/solutions/industry" rel="noopener noreferrer"&gt;solutions/industry/&lt;/a&gt;. &lt;strong&gt;This article does not cover them.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding Web Experiences to NFS/SMB File Servers
&lt;/h2&gt;

&lt;p&gt;NFS/SMB file servers have clear strengths: high throughput, low latency, multi-protocol support, and deep integration with workstation tools. These qualities make them the backbone of many organizations' workflows. Meanwhile, some of the web-based experiences that SaaS file sharing provides — browser access, AI integration, searchable audit trails — require additional tooling when working with file servers.&lt;/p&gt;

&lt;p&gt;This portal adds those web experiences to existing file server environments without changing anything about how the servers operate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Experience Added&lt;/th&gt;
&lt;th&gt;Traditional Approach&lt;/th&gt;
&lt;th&gt;How This Portal Delivers It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Browser access&lt;/strong&gt; (no VPN on the user side)&lt;/td&gt;
&lt;td&gt;VPN + mount configuration&lt;/td&gt;
&lt;td&gt;S3 AP + Cognito auth (Internet-origin). The path into the VPC remains, on the Lambda side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Natural language file search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;grep or find on file names/paths&lt;/td&gt;
&lt;td&gt;Bedrock Knowledge Base semantic search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Sharing links&lt;/strong&gt; (expiring, password-protected)&lt;/td&gt;
&lt;td&gt;Manual smbclient or email attachment&lt;/td&gt;
&lt;td&gt;Presigned URL time-limited links + QR codes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Version history visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Snapshots exist but require CLI/API&lt;/td&gt;
&lt;td&gt;Snapshot list in the UI, restored through FlexClone by selecting one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit trail in a UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Parse auditd or FPolicy logs&lt;/td&gt;
&lt;td&gt;CloudTrail + Athena self-service query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI analysis &amp;amp; classification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires building a separate pipeline&lt;/td&gt;
&lt;td&gt;Bedrock + Step Functions triggered from the screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ransomware visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ONTAP ARP runs but checking needs the ONTAP CLI / REST API&lt;/td&gt;
&lt;td&gt;ARP/AI dashboard in browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mobile / tablet access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires dedicated apps or VPN&lt;/td&gt;
&lt;td&gt;Responsive web UI + QR code access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compliance reporting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run SnapLock CLI commands and compile manually&lt;/td&gt;
&lt;td&gt;Lock panel with FISC/SOX/HIPAA presets + status overview&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;No data movement required.&lt;/strong&gt; S3 AP reads and writes data directly on the NAS volume. Edit via NFS/SMB → browse via S3 AP in browser → AI results written back to the same volume. All the same data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Existing NFS/SMB workflows are &lt;strong&gt;unaffected&lt;/strong&gt;. NFS mounts from CAD tools, SMB access from Avid, cron job file processing — all continue working exactly as before. S3 AP is an additional access path to the same volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Authentication is independent from existing SaaS (Cognito or LDAP/SAML). No impact on existing SaaS contracts or licenses.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How to Choose
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Selection Flow (Q1-Q3)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q1. Do users need to operate files from a browser?
  → No: EventBridge Scheduler (scheduled batch) is sufficient
  → Yes: Go to Q2

Q2. Primary need: "file sharing &amp;amp; sync"? Or "AI/ML processing launch &amp;amp; result viewing"?
  → File sharing focused: Nextcloud (or Storage Browser for S3)
  → AI processing focused: Amplify Gen2 portal
  → Both: Use together (covered in this article)

Q3. Are users technical? Or non-engineers?
  → Technical: Any of the GUI portals above
  → Non-engineers: Add Amazon Quick (MCP, natural language operation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you want to do&lt;/th&gt;
&lt;th&gt;Suited approach&lt;/th&gt;
&lt;th&gt;Trade-off&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trigger AI/ML processing from UI, see results on screen&lt;/td&gt;
&lt;td&gt;Amplify Gen2&lt;/td&gt;
&lt;td&gt;Custom code required, React knowledge assumed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browse, upload, sync, and share files via web&lt;/td&gt;
&lt;td&gt;Nextcloud&lt;/td&gt;
&lt;td&gt;AI integration needs Webhook development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-custom-code file operation UI&lt;/td&gt;
&lt;td&gt;Storage Browser for S3&lt;/td&gt;
&lt;td&gt;Limited features (browse/DL/UL only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Natural language file search &amp;amp; analysis&lt;/td&gt;
&lt;td&gt;Amazon Quick + AgentCore MCP&lt;/td&gt;
&lt;td&gt;Preview stage, region availability limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No frontend needed, batch is fine&lt;/td&gt;
&lt;td&gt;EventBridge Scheduler&lt;/td&gt;
&lt;td&gt;No interactive operation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How to choose&lt;/strong&gt;: Amplify Gen2, Nextcloud, Storage Browser for S3, and Amazon Quick each suit a different context. The trade-offs above are listed symmetrically, including the constraints of the option I reached for, and the article records what actually worked on technical grounds. Read it as material for choosing against your own requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Cost Estimates
&lt;/h3&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;Monthly Cost&lt;/th&gt;
&lt;th&gt;Includes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amplify Gen2 portal&lt;/td&gt;
&lt;td&gt;~$18-46&lt;/td&gt;
&lt;td&gt;Cognito/AppSync/Lambda (within Free Tier) + AI pay-per-use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nextcloud (EC2)&lt;/td&gt;
&lt;td&gt;~$70-110&lt;/td&gt;
&lt;td&gt;EC2 + RDS + ALB. Local Docker = $0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage Browser for S3&lt;/td&gt;
&lt;td&gt;~$5&lt;/td&gt;
&lt;td&gt;Amplify Hosting only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick + AgentCore MCP&lt;/td&gt;
&lt;td&gt;~$5-15&lt;/td&gt;
&lt;td&gt;Lambda execution only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge direct&lt;/td&gt;
&lt;td&gt;~$1-5&lt;/td&gt;
&lt;td&gt;Lambda + Step Functions execution only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cost note&lt;/strong&gt;: FSx for ONTAP infrastructure (~$194/month, 128 MBps configuration) is common across all approaches. The portal itself adds only tens of dollars per month. At 100 users, Cognito/AppSync/Lambda costs remain within pay-per-use bounds (serverless). At 1000 users, FSx for ONTAP throughput scaling (128→256 MBps, +~$194/month) is the primary scaling cost.&lt;/p&gt;

&lt;p&gt;Figures vary by Region, configuration and exchange rate. Price an actual deployment from the &lt;a href="https://aws.amazon.com/fsx/netapp-ontap/pricing/" rel="noopener noreferrer"&gt;FSx for ONTAP pricing page&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Approach 1: Amplify Gen2 — AI Processing Dashboard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Amplify Gen2 (Key Points for NAS Users)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.amplify.aws/" rel="noopener noreferrer"&gt;AWS Amplify Gen2&lt;/a&gt; is a full-stack development framework that lets you define both frontend (UI) and backend (auth, API, database, storage) in TypeScript and deploy to AWS.&lt;/p&gt;

&lt;p&gt;What this means for NAS/file server users:&lt;/p&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;What Amplify Gen2 Handles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No infrastructure management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No servers to provision, patch, or scale. Everything runs serverless (Lambda, AppSync, Cognito)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built-in authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cognito provides email login, MFA, and enterprise SSO (SAML/OIDC) out of the box. No custom auth system to build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Direct AWS service integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Connect to S3 Access Points, Bedrock (AI), Step Functions (workflows), Athena (analytics) with minimal code. No need to design IAM from scratch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Git-based deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git push&lt;/code&gt; triggers automatic build and deploy. No separate CI/CD pipeline required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Per-developer sandboxes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated environments auto-created per developer. Team members work in parallel without conflicts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CDK extensibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;When standard features aren't enough, add any AWS resource (VPC Lambda, DynamoDB, etc.) via AWS CDK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In short: when you want "browser UI + AI processing + authentication on NAS data," Amplify Gen2 delivers this without building or maintaining web servers. You write UI in React; AWS managed services handle the backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────────────────────┐
│  Amplify Gen2                                     │
│  ┌──────────┐  ┌───────────────────────────────┐  │
│  │ Cognito  │  │ AppSync (Generic Dispatch)    │  │
│  │ + MFA    │  │  fileQuery → ListFiles λ      │  │
│  │ + Groups │  │  adminQuery → ResourceMgmt λ  │  │
│  └──────────┘  │  arpQuery → ArpResponse λ     │  │
│                │  protectionQuery → Snapshot λ │  │
│                └──────────────┬────────────────┘  │
└───────────────────────────────┼───────────────────┘
                                │
          ┌─────────────────────┼────────────────────┐
          │                     │                    │
          ▼                     ▼                    ▼
┌──────────────────┐  ┌─────────────────┐  ┌────────────────┐
│ S3 Access Point  │  │ ONTAP REST API  │  │ Step Functions │
│ (Internet-origin)│  │ (inside VPC)    │  │ (AI/ML jobs)   │
└──────────────────┘  └─────────────────┘  └────────────────┘
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fug4u6ypi1jo4mdaq8wb6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fug4u6ypi1jo4mdaq8wb6.png" alt="Amplify Gen2 AI processing portal architecture. A web browser and Amazon Quick reach AWS Amplify, Amazon Cognito, and the Amazon Bedrock AgentCore; AppSync GraphQL API invokes Lambda functions running outside the VPC on ARM64. Those functions call Bedrock, Rekognition, Athena, Textract, and Comprehend, and read and write the FSx for ONTAP volume through the S3 Access Point. Audit logs are written to S3 Object Lock as WORM" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/amplify-vpc-split-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: Amplify Gen2 architecture — Lambda outside the VPC reads and writes the volume through the S3 Access Point&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Keeping Lambda outside the VPC and letting it call the S3 Access Point directly is the crux of this design. Here's why.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Design Decisions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;VPC split architecture&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Non-VPC Lambda&lt;/strong&gt;: Reads/writes files via S3 AP (Internet-origin). Fast cold starts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Lambda&lt;/strong&gt;: Calls ONTAP REST API (management LIF). Must be inside VPC.&lt;/li&gt;
&lt;li&gt;Never combine both in one Lambda (Internet-origin S3 AP unreachable via S3 Gateway VPC Endpoint).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Network note&lt;/strong&gt;: VPC Lambda handles admin operations only. Required VPC Endpoints: Secrets Manager and CloudWatch Logs. S3 AP data access uses non-VPC Lambda, so NAT Gateway is not needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Generic Dispatch pattern&lt;/strong&gt;: 155 operations consolidated into 10 generic endpoints (&lt;code&gt;action&lt;/code&gt; + &lt;code&gt;params: AWSJSON&lt;/code&gt;) to avoid CloudFormation 1MB limit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Extensibility note&lt;/strong&gt;: Adding one operation needs no AppSync schema or CloudFormation change, because the endpoints take an &lt;code&gt;action&lt;/code&gt; string and a &lt;code&gt;params&lt;/code&gt; payload. It is not only the handler, though: the generated &lt;code&gt;src/lib/dispatchActions.ts&lt;/code&gt; has to be regenerated (&lt;code&gt;scripts/portal_action_types.py --emit&lt;/code&gt;), and a new UI string has to be added across 8 locales. CI fails until both agree. See the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/infrastructure/pstk-automation/docs/pstk-action-catalog.md" rel="noopener noreferrer"&gt;PSTK Action Catalog&lt;/a&gt; for the full action list.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  UI Layout (Sidebar)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────┬─────────────────────────────────────┐
│ Browse        │                                     │
│  📂 All Files │  [Main Content Area]                │
│  ⭐ Favorites │                                     │
│  🕐 Recent    │                                     │
│  🔔 Watch     │                                     │
│  📤 Upload    │                                     │
│               │                                     │
│ AI &amp;amp; Proc.    │                                     │
│  ⚡ AI Proc.  │                                     │
│  🤖 AI Chat   │                                     │
│  🔍 Search    │                                     │
│  📋 History   │                                     │
│  📊 Analytics │                                     │
│  🗂️ Agent Dir │                                     │
│               │                                     │
│ Data Prot.    │  + Right Panel: AI Assistant        │
│  📸 Snapshots │  (appears on file selection)        │
│  🔒 Lock      │                                     │
│  🛡️ ARP/AI    │                                     │
│               │                                     │
│ Admin         │                                     │
│  🔧 Resources │                                     │
│  🔄 Version   │                                     │
│  🔍 Audit     │                                     │
└───────────────┴─────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 groups × 17 sections. The sidebar pattern is the one file management screens generally use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;File selection → AI processing trigger → results shown on screen (5-second polling)&lt;/li&gt;
&lt;li&gt;Bedrock Q&amp;amp;A, Rekognition image analysis, Athena SQL, Textract OCR&lt;/li&gt;
&lt;li&gt;FlexClone snapshot restore (selected and run from the UI)&lt;/li&gt;
&lt;li&gt;Job execution history (DynamoDB, per-user tracking)&lt;/li&gt;
&lt;li&gt;8-language i18n (JA/EN/KO/ZH-CN/ZH-TW/FR/DE/ES)&lt;/li&gt;
&lt;li&gt;Keyboard navigation + ARIA labels + Dark/Light mode&lt;/li&gt;
&lt;li&gt;Responsive (hamburger menu at 768px, tablet-ready)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Audit note&lt;/strong&gt;: All operations are recorded in CloudTrail. Audit log retention is controlled via CloudTrail settings (default 90 days, extendable to 7 years for compliance). Self-service Athena queries available from the portal's Audit section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: Blast radius if a Cognito token is compromised — tokens are scoped by Cognito Groups via AppSync resolver authorization; access is limited to resources within the user's group. Default token expiry: 1 hour. MFA-required setting mitigates leakage risk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AppSync APPSYNC_JS runtime: &lt;code&gt;new Date()&lt;/code&gt; not available → use &lt;code&gt;util.time.nowISO8601()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Data Source must be in the same CDK stack as AppSync API ("Data source not found" otherwise)&lt;/li&gt;
&lt;li&gt;Bedrock Nova Lite: requires &lt;code&gt;converse()&lt;/code&gt; API (&lt;code&gt;invoke_model&lt;/code&gt; + &lt;code&gt;inputText&lt;/code&gt; is Titan-only)&lt;/li&gt;
&lt;li&gt;Presigned URLs: need explicit SigV4 + regional endpoint&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Approach 2: Nextcloud — File Sharing UI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Nextcloud (Key Points for NAS Users)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextcloud.com/" rel="noopener noreferrer"&gt;Nextcloud&lt;/a&gt; is a self-hosted, open-source file sharing and collaboration platform. It provides browser-based file operations and sharing on infrastructure you control.&lt;/p&gt;

&lt;p&gt;What this means for NAS/file server users:&lt;/p&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;What Nextcloud Handles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data sovereignty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;File data stays on your servers. No need to entrust data to external SaaS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Direct NAS connection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External Storage feature mounts S3 AP / NFS / SMB / WebDAV as additional folders. No data copy needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Familiar experience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Desktop sync client (Windows/Mac/Linux), mobile apps, sharing links — SaaS-equivalent UX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No per-user licensing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AGPL-3.0, free to use. No per-seat pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auth integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supports LDAP, SAML, OIDC. Connects to existing Active Directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you configure FSx for ONTAP's S3 AP as Nextcloud's External Storage backend, ONTAP volume files appear in Nextcloud's folder listing. Files saved via NFS/SMB are immediately visible in the browser and syncable to desktop clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&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;# Nextcloud's External Storage App mounts S3 AP as S3-compatible storage&lt;/span&gt;
Nextcloud &lt;span class="o"&gt;(&lt;/span&gt;Docker&lt;span class="o"&gt;)&lt;/span&gt; → External Storage App &lt;span class="o"&gt;(&lt;/span&gt;S3 backend&lt;span class="o"&gt;)&lt;/span&gt; → S3 AP → FSx &lt;span class="k"&gt;for &lt;/span&gt;ONTAP
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvswszusgdiba6irozbz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbvswszusgdiba6irozbz.png" alt="Nextcloud file sharing UI architecture. A web browser reaches Nextcloud on EC2 with Docker through Elastic Load Balancing, with metadata in Amazon RDS for MariaDB. Nextcloud's External Storage App mounts the S3 Access Point over the S3 API to reach the FSx for ONTAP volume. EventBridge Scheduler and Step Functions trigger AI processing with Bedrock, Rekognition, Athena, and Comprehend" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/nextcloud-external-storage-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3: Nextcloud architecture — the External Storage App mounts the S3 Access Point as S3-compatible storage&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pass the S3 AP alias as the bucket name, and ONTAP volume contents appear in Nextcloud's file listing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup (~5 minutes)
&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;# Start Nextcloud container&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;solutions/nextcloud-test
make up

&lt;span class="c"&gt;# Set your S3 AP alias or bucket name (e.g., "my-volume-ap-s3alias")&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_S3AP_ALIAS&amp;gt;

&lt;span class="c"&gt;# Configure External Storage to mount ONTAP volume via S3 AP&lt;/span&gt;
make configure-s3

&lt;span class="c"&gt;# Verify connection (success = file listing returned)&lt;/span&gt;
make verify

&lt;span class="c"&gt;# Open in browser&lt;/span&gt;
open http://localhost:8080  &lt;span class="c"&gt;# admin / admin123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to expect&lt;/strong&gt;: Nextcloud file listing shows files from your ONTAP volume (or S3 bucket) via S3 AP.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Desktop sync client (Windows/Mac/Linux)&lt;/li&gt;
&lt;li&gt;Sharing links (password-protected, time-limited)&lt;/li&gt;
&lt;li&gt;Comments &amp;amp; annotations&lt;/li&gt;
&lt;li&gt;WebDAV access&lt;/li&gt;
&lt;li&gt;LDAP/SAML authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;th&gt;Considerations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mature NAS file sharing tool&lt;/td&gt;
&lt;td&gt;AI/ML integration needs Webhook development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop sync + mobile apps&lt;/td&gt;
&lt;td&gt;EC2/RDS operational cost (production config)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free under AGPL-3.0 (Docker as-is)&lt;/td&gt;
&lt;td&gt;Source disclosure obligation if server code is modified&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;use_path_style=true&lt;/code&gt; required (S3 AP alias only works with path-style addressing)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;occ files_external:config&lt;/code&gt; must be run once per parameter ("Too many arguments" otherwise)&lt;/li&gt;
&lt;li&gt;Empty credentials cause IMDS (169.254.169.254) fallback timeout in Docker&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Local Docker: $0. Production (EC2 + RDS + ALB): ~$70-110/month.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coexistence Pattern: Amplify + Nextcloud
&lt;/h2&gt;

&lt;p&gt;They are not mutually exclusive — both can &lt;strong&gt;access the same FSx for ONTAP volume simultaneously&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature Comparison
&lt;/h3&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;Nextcloud&lt;/th&gt;
&lt;th&gt;Amplify Gen2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File browsing&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File DL/UL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (Presigned URL, 50GB object limit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop sync&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;△ (Use NFS/SMB instead)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sharing links (time-limited)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (Presigned URL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI/ML processing workflows&lt;/td&gt;
&lt;td&gt;⚠️ Webhook needed&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File Q&amp;amp;A (Bedrock)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FlexClone restore&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP admin operations&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;h3&gt;
  
  
  Expected Usage Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Team uses Nextcloud daily for file browsing &amp;amp; sharing
   (same data visible as NFS/SMB users)

2. When AI processing is needed on a specific folder,
   launch it from the Amplify portal

3. Result files are written back to the same volume,
   instantly visible from Nextcloud and NFS
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffu5htftpz2bt0ou9dq6w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffu5htftpz2bt0ou9dq6w.png" alt="Amplify Gen2 and Nextcloud running side by side. The left AI processing and analytics block holds AWS Amplify, Cognito, AppSync, and Lambda calling Bedrock, Rekognition, Athena, Textract, and Comprehend. The right file management and sync block holds Nextcloud behind Elastic Load Balancing with RDS. Both blocks reach the same FSx for ONTAP volume through one shared S3 Access Point, coexisting with NFS and SMB clients" width="800" height="877"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Light theme shown. A &lt;a href="https://raw.githubusercontent.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/main/docs/images/png/coexistence-3path-en-dark@2x.png" rel="noopener noreferrer"&gt;dark theme version&lt;/a&gt; is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4: Running both — AI processing and file management share one S3 Access Point&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Authentication is independent (Nextcloud: LDAP/SAML, Amplify: Cognito). For gradual adoption, set up Nextcloud first, then add Amplify when processing needs arise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Consistency
&lt;/h3&gt;

&lt;p&gt;ONTAP keeps multi-protocol access consistent, but &lt;strong&gt;when a write becomes visible from the other protocol depends on the client's mount options.&lt;/strong&gt; These are measured, on one volume reached from both an S3 Access Point and NFS (2026-08-09, ap-northeast-1, SINGLE_AZ_1, 128 MBps, NFSv3, concurrency 1):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction / operation&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;S3 → NFS (64 B write)&lt;/td&gt;
&lt;td&gt;p50 7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS → S3&lt;/td&gt;
&lt;td&gt;An order of magnitude higher (around a second)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete reflected, &lt;code&gt;actimeo=0&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete reflected, default mount&lt;/td&gt;
&lt;td&gt;2,171 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completed multipart upload, default mount&lt;/td&gt;
&lt;td&gt;Still not visible after 3 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;If you are writing code that reads through the other protocol straight after a write, match your mount options to the measurement conditions.&lt;/strong&gt; Every direction and the full conditions are in the &lt;a href="https://github.com/Yoshiki0705/S3-Burst-on-ONTAP-Files/blob/main/docs/en/verification/s3ap-nfs-visibility.md" rel="noopener noreferrer"&gt;verification record&lt;/a&gt;. Reproducibility of this measurement is incomplete in one respect: the ONTAP version could not be identified.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Storage operations note&lt;/strong&gt;: Your existing procedures (volume creation, snapshot management, and so on) remain unchanged. This portal is an end-user-facing AI processing and file access layer; administrators carry on with the AWS Management Console / Amazon FSx API and the ONTAP CLI / REST API. Those three are the management interfaces reachable for FSx for ONTAP — ONTAP System Manager is not among them (&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/fsx-ontap-management-interfaces.md" rel="noopener noreferrer"&gt;management interfaces&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Comparison with Alternatives
&lt;/h2&gt;

&lt;p&gt;Multiple approaches exist for "accessing NAS data from a browser." Each has trade-offs:&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;Suited for&lt;/th&gt;
&lt;th&gt;Trade-off&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;This portal (S3 AP + Amplify/Nextcloud)&lt;/td&gt;
&lt;td&gt;AI processing + admin ops without data movement&lt;/td&gt;
&lt;td&gt;Custom code required, hours of initial setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DataSync + S3 + any portal&lt;/td&gt;
&lt;td&gt;Leveraging S3-native ecosystem&lt;/td&gt;
&lt;td&gt;Data copy, sync lag, storage cost duplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transfer Family SFTP&lt;/td&gt;
&lt;td&gt;Existing SFTP client compatibility&lt;/td&gt;
&lt;td&gt;No web UI, AI integration needs extra development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EFS + Lambda&lt;/td&gt;
&lt;td&gt;Serverless simplicity&lt;/td&gt;
&lt;td&gt;No ONTAP-specific features (SnapLock, FlexClone, ARP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A browser-native file sharing service&lt;/td&gt;
&lt;td&gt;No infrastructure to build&lt;/td&gt;
&lt;td&gt;Reaching data on the NAS requires a copy. ONTAP-side Snapshot / SnapLock / FlexClone / ARP are outside the path&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The S3 AP characteristic is "access data via S3 API without moving it." No data copy means no consistency issues or double storage costs. However, it's not "sign up and use immediately" like SaaS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How to choose&lt;/strong&gt;: DataSync + S3, Transfer Family SFTP, EFS + Lambda, and SaaS file sharing each suit a different context. The table above also carries the constraints on the FSx for ONTAP S3 AP side (custom code required, a few hours to stand up). Read it as material for choosing against your own requirements.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Try It (~30 Minutes)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Check Command&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;20.x or later&lt;/td&gt;
&lt;td&gt;&lt;code&gt;node --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0 min if installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npm&lt;/td&gt;
&lt;td&gt;10.x or later&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bundled with Node.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS CLI&lt;/td&gt;
&lt;td&gt;2.x&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~2 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Account&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws sts get-caller-identity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0 min if authenticated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;24.x or later&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nextcloud only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;FSx for ONTAP is &lt;strong&gt;NOT required&lt;/strong&gt; for DemoMode. You can verify portal behavior with a regular S3 bucket.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Fastest Path (~5 min, copy-paste 4 lines)
&lt;/h3&gt;

&lt;p&gt;If Node.js and AWS credentials are ready:&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;# Clone repository and navigate to portal directory&lt;/span&gt;
git clone https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/solutions/amplify-portal

&lt;span class="c"&gt;# Install dependencies (~2 min)&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy config (DemoMode default — no edits needed)&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;amplify/portal-config.example.ts amplify/portal-config.ts

&lt;span class="c"&gt;# Start sandbox (first run also creates Cognito users, ~3 min)&lt;/span&gt;
npx ampx sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to expect&lt;/strong&gt;: Terminal shows &lt;code&gt;✅ Deployment complete&lt;/code&gt;, then &lt;code&gt;http://localhost:5173&lt;/code&gt; opens the portal. You can browse files and access AI processing screens from the sidebar.&lt;/p&gt;

&lt;h3&gt;
  
  
  portal-config.ts Key Parameters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// portal-config.ts — parameter explanations&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;portalConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// S3 AP alias (empty string = DemoMode, uses regular S3 bucket)&lt;/span&gt;
  &lt;span class="c1"&gt;// Production example: "my-ontap-vol-ap-s3alias"&lt;/span&gt;
  &lt;span class="na"&gt;s3ApAlias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// VPC ID (empty = no VPC Lambda = DemoMode)&lt;/span&gt;
  &lt;span class="c1"&gt;// Production example: "vpc-0123456789abcdef0"&lt;/span&gt;
  &lt;span class="na"&gt;vpcId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// Cognito group-to-S3AP mapping (different access per group)&lt;/span&gt;
  &lt;span class="c1"&gt;// Example: analysts group gets analysis AP only, admins get full access&lt;/span&gt;
  &lt;span class="na"&gt;groupApMapping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// "analysts": "analysis-vol-ap-s3alias",&lt;/span&gt;
    &lt;span class="c1"&gt;// "admins": "admin-vol-ap-s3alias"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CI/CD note&lt;/strong&gt;: Amplify Gen2 auto-deploys on git push. &lt;code&gt;git push origin main&lt;/code&gt; triggers Amplify Hosting build/deploy. No separate CI/CD pipeline needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Nextcloud Setup (~5 min)
&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;# Start Nextcloud container&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;solutions/nextcloud-test
make up

&lt;span class="c"&gt;# Set your S3 AP alias or S3 bucket name&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_BUCKET_OR_AP_ALIAS&amp;gt;

&lt;span class="c"&gt;# Configure External Storage&lt;/span&gt;
make configure-s3

&lt;span class="c"&gt;# Verify connection&lt;/span&gt;
make verify

&lt;span class="c"&gt;# Open in browser&lt;/span&gt;
open http://localhost:8080  &lt;span class="c"&gt;# admin / admin123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to expect&lt;/strong&gt;: Nextcloud file listing shows files from the ONTAP volume (or S3 bucket) via S3 AP.&lt;/p&gt;

&lt;h3&gt;
  
  
  About DemoMode
&lt;/h3&gt;

&lt;p&gt;Leave VPC settings empty in &lt;code&gt;portal-config.ts&lt;/code&gt; to experience the portal UI and AI processing flow without FSx for ONTAP.&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;DemoMode&lt;/th&gt;
&lt;th&gt;Production&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sidebar UI / navigation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File browsing&lt;/td&gt;
&lt;td&gt;⚠️ (S3 bucket)&lt;/td&gt;
&lt;td&gt;✅ (S3 AP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI processing&lt;/td&gt;
&lt;td&gt;⚠️ (needs Step Functions)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP admin panels&lt;/td&gt;
&lt;td&gt;⚠️ (shows "connection required")&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For production connectivity, see &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Role-Based Documentation (8 Languages)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Audience&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-user-guide.md" rel="noopener noreferrer"&gt;User Guide&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;End users&lt;/td&gt;
&lt;td&gt;Sign in → file ops → AI → FAQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-compliance-guide.md" rel="noopener noreferrer"&gt;Compliance Guide&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Security/Compliance officers&lt;/td&gt;
&lt;td&gt;ARP verification → WORM checks → audit trail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-quick-reference.md" rel="noopener noreferrer"&gt;Quick Reference&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;All roles&lt;/td&gt;
&lt;td&gt;1-page operations cheat sheet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Demo note&lt;/strong&gt;: Minimum 30-minute demo setup is "4-line fastest path → DemoMode launch → file browsing → AI processing screen." No FSx for ONTAP pre-build required.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Operational Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  File-Level Access Control
&lt;/h3&gt;

&lt;p&gt;S3 AP access is governed by ONTAP's UNIX permissions (UID/GID) or Windows ACLs. The File System Identity specified when creating the S3 AP determines "who" the access runs as.&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;# Example: separate team access with multiple S3 APs&lt;/span&gt;
S3 AP &lt;span class="s2"&gt;"portal-readonly"&lt;/span&gt;  → UID 1001 &lt;span class="o"&gt;(&lt;/span&gt;read-only&lt;span class="o"&gt;)&lt;/span&gt;
S3 AP &lt;span class="s2"&gt;"portal-analyst"&lt;/span&gt;   → UID 1002 &lt;span class="o"&gt;(&lt;/span&gt;analysis team, specific &lt;span class="nb"&gt;dirs &lt;/span&gt;only&lt;span class="o"&gt;)&lt;/span&gt;
S3 AP &lt;span class="s2"&gt;"portal-admin"&lt;/span&gt;     → UID 0    &lt;span class="o"&gt;(&lt;/span&gt;admin, full access&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Throughput Sharing
&lt;/h3&gt;

&lt;p&gt;S3 AP access shares the same FSx for ONTAP throughput budget as NFS/SMB. Normal portal usage (directory listing + individual reads) is not an issue, but if heavy parallel reads coexist with NFS workloads, monitor CloudWatch &lt;code&gt;ThroughputUtilization&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Options if throughput is insufficient:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scale up throughput capacity (first-generation file systems offer 128 / 256 / 512 / 1,024 / 2,048 MBps, and 4,096 MBps in some Regions; second-generation uses a separate series from 384 MBps up)&lt;/li&gt;
&lt;li&gt;Set QoS policy to cap portal volume throughput&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Multi-Account Environments
&lt;/h3&gt;

&lt;p&gt;This portal is designed for single-account use. AWS Organizations considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S3 AP&lt;/strong&gt;: Cross-account access requires AP resource policy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito&lt;/strong&gt;: Multi-account user consolidation recommended via SAML federation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tenant isolation&lt;/strong&gt;: &lt;code&gt;groupApMapping&lt;/code&gt; assigns different S3 APs per group&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  About Presigned URLs
&lt;/h3&gt;

&lt;p&gt;AWS documentation lists Presign as "Not supported," but they work in practice (confirmed with AWS Support). Presigning is client-side SigV4 signature calculation, and since GetObject is Supported, there's no structural way to block it. However, production reliance is at your own risk since documentation says "Not supported." Set short expiry (5 minutes or less recommended).&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling Characteristics
&lt;/h3&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;Scaling&lt;/th&gt;
&lt;th&gt;Downtime&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda / AppSync / Cognito / DynamoDB&lt;/td&gt;
&lt;td&gt;Automatic (serverless)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP&lt;/td&gt;
&lt;td&gt;Manual throughput/storage expansion&lt;/td&gt;
&lt;td&gt;Minutes during throughput change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Portal components are all serverless — the only scaling bottleneck is FSx for ONTAP throughput.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Data engineering note&lt;/strong&gt;: File metadata is available via CloudTrail + S3 AP access logs. Queryable with Athena, but building a dedicated metadata catalog (Glue Data Catalog etc.) requires separate design.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Series Structure (7 Parts)
&lt;/h2&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;Theme&lt;/th&gt;
&lt;th&gt;Key Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 1 (this article)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Portal foundation&lt;/td&gt;
&lt;td&gt;Amplify Gen2 vs Nextcloud, coexistence patterns, DemoMode&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;Storage operations&lt;/td&gt;
&lt;td&gt;ARP/AI incident response, Tamperproof Snapshot, regulatory retention, Audit Log, FlexClone Restore&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;AI agent integration&lt;/td&gt;
&lt;td&gt;AgentChat, semantic search, multi-agent teams, HITL&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;Delegation and record&lt;/td&gt;
&lt;td&gt;182 actions, buttons that cannot be pressed, the blast radius of irreversible operations&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;What only the cluster told me&lt;/td&gt;
&lt;td&gt;FlexGroup creation, capacity rebalance, delete refusals, defaults that invalidate a measurement&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;What was left off the screen&lt;/td&gt;
&lt;td&gt;How far ONTAP features reach over the S3 Access Point path, and the work handed to a schedule&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;The tooling underneath&lt;/td&gt;
&lt;td&gt;Nx Plugin for AWS 1.0, AWS Blocks and Amplify Gen 2 compared: resource counts, fixed monthly cost, and what a stack deletion leaves behind&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: I'm not using FSx for ONTAP yet. Can I try this?&lt;/strong&gt;&lt;br&gt;
Yes. DemoMode uses a regular S3 bucket to demonstrate the portal's UI and AI processing flow. FSx for ONTAP can be added later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Do I need to replace the file sharing service we already use?&lt;/strong&gt;&lt;br&gt;
No. This portal is an additional layer for AI processing, data protection, and audit on NAS data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How much does it cost?&lt;/strong&gt;&lt;br&gt;
DemoMode is free (within AWS Free Tier). Production portal add-on: ~$5–50/month. Main cost is FSx for ONTAP infrastructure (~$194/month minimum).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What about security?&lt;/strong&gt;&lt;br&gt;
Cognito MFA + Groups role separation, HTTPS, Secrets Manager. Enterprise SSO (SAML/OIDC) supported via Cognito federation. Part 2 covers the security model in detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can it handle large files (multiple GB)?&lt;/strong&gt;&lt;br&gt;
S3 AP upload has a 50GB object size limit (a single PutObject is capped at 5GB, so Storage Browser switches to multipart above that). For files larger than 50GB, place them on the volume via NFS/SMB and use the portal for browsing/processing only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does this affect my existing operational procedures?&lt;/strong&gt;&lt;br&gt;
No. This portal is an end-user-facing additional layer. Operating through the ONTAP CLI / REST API and the AWS Management Console continues exactly as before.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verified Environment
&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;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;20.18.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;18.3.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amplify Gen2&lt;/td&gt;
&lt;td&gt;1.x (as of 2026-07)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python (Lambda)&lt;/td&gt;
&lt;td&gt;3.12 (ARM64)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSx for ONTAP&lt;/td&gt;
&lt;td&gt;ONTAP 9.17.1&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;Verified&lt;/td&gt;
&lt;td&gt;2026-07&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns" rel="noopener noreferrer"&gt;GitHub: FSx for ONTAP S3 AP Serverless Patterns&lt;/a&gt; — This repository (MIT License)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-user-guide.md" rel="noopener noreferrer"&gt;User Guide (8 languages)&lt;/a&gt; — End-user daily operations guide&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-compliance-guide.md" rel="noopener noreferrer"&gt;Compliance Guide (8 languages)&lt;/a&gt; — ARP/WORM/audit trail verification procedures&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt; — DemoMode to production connectivity migration&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-scaling-guide.md" rel="noopener noreferrer"&gt;Scaling Guide&lt;/a&gt; — Capacity planning and throughput design&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/nextcloud-external-storage-s3ap.md" rel="noopener noreferrer"&gt;Nextcloud External Storage Setup&lt;/a&gt; — Nextcloud + S3 AP detailed configuration&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/s3ap-compatibility-notes.md" rel="noopener noreferrer"&gt;S3 AP Compatibility Notes&lt;/a&gt; — API support status and known constraints&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/demo-mode-guide.md" rel="noopener noreferrer"&gt;DemoMode Guide&lt;/a&gt; — Running without FSx for ONTAP&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;AWS: FSx for ONTAP S3 Access Points&lt;/a&gt; — Official documentation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary and Next Steps
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Point&lt;/th&gt;
&lt;th&gt;Conclusion&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data migration&lt;/td&gt;
&lt;td&gt;Not needed — S3 AP reads the same volume NFS/SMB already uses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portal choice&lt;/td&gt;
&lt;td&gt;Nextcloud when file sharing is the center of gravity, Amplify Gen2 when AI processing and ONTAP admin operations are&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running both&lt;/td&gt;
&lt;td&gt;Both can attach to the same volume at once. Authentication stays independent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How to try it&lt;/td&gt;
&lt;td&gt;DemoMode takes about 30 minutes and needs no FSx for ONTAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Existing workflows&lt;/td&gt;
&lt;td&gt;NFS/SMB mounts and ONTAP CLI / REST API operations continue unchanged&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For next steps, start with the 4-line fastest path in DemoMode and spend a few minutes in the sidebar UI and the AI processing screen. Once you can see which approach fits, the &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/en/portal-poc-to-production.md" rel="noopener noreferrer"&gt;PoC → Production Guide&lt;/a&gt; covers moving to a production connection.&lt;/p&gt;

&lt;p&gt;Part 2 covers storage operations (ARP/AI incident response, Tamperproof Snapshot, regulatory retention, Audit Log, FlexClone Restore), and Part 3 covers AI agent integration (AgentChat, semantic search, multi-agent teams, HITL). Parts 4 to 6 continue into delegating operations, what the running cluster stopped, and what was left off the screen.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/embedding-storage-operations-into-a-file-portal-from-arpai-incident-response-to-regulatory-1oih"&gt;Part 2: Embedding Storage Operations into a File Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/embedding-ai-agents-into-a-file-portal-from-agentcore-mcp-to-multi-agent-teams-part-3-19m1"&gt;Part 3: Embedding AI Agents into a File Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/putting-fsx-for-ontap-operations-on-a-file-portal-on-aws-182-actions-and-the-design-of-425g"&gt;Part 4: Putting FSx for ONTAP operations on a file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/what-i-learned-driving-fsx-for-ontap-from-a-file-portal-on-aws-flexgroup-creation-capacity-3gkd"&gt;Part 5: What I learned driving FSx for ONTAP from a file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/what-i-left-off-the-file-portal-on-aws-how-far-fsx-for-ontap-features-reach-and-the-work-handed-hk8"&gt;Part 6: What I left off the file portal on AWS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this post helps someone out there.&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>amplify</category>
      <category>fsxforontap</category>
      <category>architecture</category>
    </item>
    <item>
      <title>KNFSD File Cache FSx for ONTAP: NFS Read Speedup + S3 Access Points Dual-Path</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Thu, 23 Jul 2026 12:59:50 +0000</pubDate>
      <link>https://dev.to/aws-builders/knfsd-file-cache-x-fsx-for-ontap-nfs-read-speedup-s3-access-points-dual-path-kn1</link>
      <guid>https://dev.to/aws-builders/knfsd-file-cache-x-fsx-for-ontap-nfs-read-speedup-s3-access-points-dual-path-kn1</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In July 2026, AWS released &lt;a href="https://github.com/awslabs/knfsd-file-cache" rel="noopener noreferrer"&gt;KNFSD File Cache&lt;/a&gt; as Preview — an open-source (Apache 2.0) NFS caching proxy built on the Linux kernel's NFS re-export and FS-Cache subsystems.&lt;/p&gt;

&lt;p&gt;This is the first time an NFS read-acceleration layer has shipped as an official AWS solution. Working daily with FSx for ONTAP NFS performance for EDA and VFX workloads, my reaction was immediate: spin up a verification environment and see what happens.&lt;/p&gt;

&lt;p&gt;What I tested was a &lt;strong&gt;Dual-Path architecture&lt;/strong&gt;: KNFSD caching NFS reads from the same FSx for ONTAP volume that S3 Access Points expose to serverless Lambda processing. The question: can compute fleets read at local-NVMe speed while Lambda runs AI/ML workloads on the same data, without bandwidth contention?&lt;/p&gt;

&lt;p&gt;Here's what I found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dual-Path E2E test passed&lt;/strong&gt;: A file written via S3 AP was immediately readable through the KNFSD NFS cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache effect&lt;/strong&gt;: first read 55ms → subsequent reads 2ms (28x improvement), proxy cache serves 422-619 MB/s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFSv4.1 is mandatory&lt;/strong&gt;: NFSv3 re-export causes Stale file handle on file creation (kernel-level filehandle size constraint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FSID_MODE=local + SQLite on FSx for ONTAP&lt;/strong&gt;: FSID persistence at zero additional cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post covers every pitfall I hit during deployment, and why this architecture still makes sense despite those issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Consider This — and When Not To
&lt;/h2&gt;

&lt;h3&gt;
  
  
  This architecture suits workloads that:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Need NFS read bandwidth beyond FSx for ONTAP's provisioned throughput (EDA DRC/LVS, VFX rendering, genomics)&lt;/li&gt;
&lt;li&gt;Want both NFS (high-speed reads) and S3 API (Lambda AI/ML post-processing) on the same data&lt;/li&gt;
&lt;li&gt;Use Spot instances for burst compute and need the cache to stay warm through reclamation&lt;/li&gt;
&lt;li&gt;Unify multiple NFS sources (FSx for ONTAP + on-premises NAS + other clouds) into one cache layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consider alternatives when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;FSx for ONTAP throughput scaling alone covers your bandwidth needs (steady-state workloads)&lt;/li&gt;
&lt;li&gt;Write caching is needed (FlexCache offers configurable write-back/write-around)&lt;/li&gt;
&lt;li&gt;You want fully managed operations with no EC2 to run (FlexCache or Amazon File Cache)&lt;/li&gt;
&lt;li&gt;Production SLA is non-negotiable (KNFSD is Preview — wait for GA)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On vendor neutrality&lt;/strong&gt;: This post evaluates KNFSD File Cache on technical merits only. FlexCache, Amazon File Cache, EFS, and other options each suit different contexts. The goal here is to document what actually works, not to rank solutions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What is KNFSD File Cache?
&lt;/h2&gt;

&lt;p&gt;KNFSD File Cache is an open-source (Apache 2.0) NFS caching proxy built on the Linux kernel's NFS re-export and FS-Cache subsystems. NFS re-export means mounting a remote NFS share and publishing it as a new NFS export to downstream clients — the proxy sits transparently between source and consumers. Wētā FX (Avatar: The Way of Water) and ILM used the predecessor project in production.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NFS Source (FSx for ONTAP)
     │ NFS mount (NFSv4.1, fsc)
     ▼
KNFSD Proxy (EC2, NVMe L2 cache)
     │ NFS re-export (NFSv4.1)
     ▼
Compute Fleet (Spot × hundreds)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;L1 cache: Linux Page Cache (RAM — 5-9 GB/s on 16 GB in this test)&lt;/li&gt;
&lt;li&gt;L2 cache: FS-Cache (local NVMe — survives reboot, 106 MB/s measured)&lt;/li&gt;
&lt;li&gt;Writes: write-through (immediately forwarded to source)&lt;/li&gt;
&lt;li&gt;Scaling: ASG + DNS round-robin or NLB&lt;/li&gt;
&lt;li&gt;Prerequisite: source must be an NFS v3/4.x server (parallel filesystems like Lustre/GPFS/WekaFS are not supported)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key difference from FlexCache: KNFSD is a protocol-level transparent proxy that doesn't require the source to be ONTAP. It can unify FSx for OpenZFS, on-premises NetApp/Pure/Dell/DDN (A3I), and other-cloud NFS servers (via Direct Connect / Cloud Interconnect) into a single cache layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test Environment
&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;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&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;Source&lt;/td&gt;
&lt;td&gt;FSx for ONTAP (128 MBps, Single-AZ, ONTAP 9.17.1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KNFSD Proxy&lt;/td&gt;
&lt;td&gt;m6gd.xlarge (arm64 Graviton, 16 GB RAM, 237 GB NVMe)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;7.1.3-knfsd (Packer custom build)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FSID Mode&lt;/td&gt;
&lt;td&gt;local (SQLite on FSx for ONTAP NFS mount)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS Version&lt;/td&gt;
&lt;td&gt;NFSv4.1 (source → proxy → client)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Client&lt;/td&gt;
&lt;td&gt;t4g.micro (AL2023, 916 MB RAM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP&lt;/td&gt;
&lt;td&gt;Internet-origin, UNIX identity (root)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cost note&lt;/strong&gt;: KNFSD-specific incremental resources only (Packer Spot build ~$0.30 + m6gd.xlarge 1hr ~$0.29). Prerequisites: a running FSx for ONTAP (~$194/mo minimum) + VPC infrastructure. If you already operate FSx for ONTAP, the incremental evaluation cost is effectively &amp;lt; $1.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Dual-Path E2E Test Results (Verified)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Lambda / CLI]                    [Compute Fleet]
     │                                 │
     │ S3 PutObject                    │ NFS read (NFSv4.1)
     ▼                                 ▼
┌──────────────────┐         ┌──────────────────────┐
│ S3 Access Point  │         │ KNFSD File Cache     │
│ (Internet-origin)│         │ (m6gd.xlarge, NVMe)  │
└────────┬─────────┘         └──────────┬───────────┘
         │                              │
         │  ← same volume →             │ NFS mount (NFSv4.1)
         ▼                              ▼
┌──────────────────────────────────────────────────┐
│ FSx for ONTAP Volume (/vol1, UNIX security style)│
└──────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Results
&lt;/h3&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;Result&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP write → KNFSD NFS read&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Immediate reflection, content match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS write → S3 AP read (MD5)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;c092ef65...&lt;/code&gt; match both directions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP batch 50 files → NFS bulk read&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;50 files in 107ms (2.1ms/file)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP multipart 50MB → NFS read&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MD5 verified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFSv4.1 write via KNFSD&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Write-through reflects immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fsidd + SQLite persistence&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQLite created on FSx for ONTAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proxy restart → client continues&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NFSv4.1 grace period, no remount needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Actual test log&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;test.txt s3://arn:aws:s3:ap-northeast-1:XXXX:accesspoint/knfsd-dualpath-test/s3ap-e2e-1784803253.txt
&lt;span class="c"&gt;# → upload: success&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /mnt/knfsd/vol1/s3ap-e2e-1784803253.txt  &lt;span class="c"&gt;# NFSv4.1 via KNFSD&lt;/span&gt;
dual-path-e2e-test-1784803253
&lt;span class="c"&gt;# → read success ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Performance Numbers
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Test environment note&lt;/strong&gt;: These numbers are from m6gd.xlarge (237 GB NVMe, single drive). Production-recommended instances (im4gn.16xlarge: 30 TB NVMe RAID, i3en.24xlarge: 60 TB) are expected to deliver significantly higher L2 NVMe throughput (estimated 2-8 GB/s sequential).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3-Layer Cache Hierarchy
&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;Throughput&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;L1: Proxy RAM (page cache)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.0-9.1 GB/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hot data, repeated reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L2: NVMe (FS-Cache)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;106 MB/s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Warm-up shortcut after proxy restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source: FSx for ONTAP (NFS fetch)&lt;/td&gt;
&lt;td&gt;18-19 MB/s&lt;/td&gt;
&lt;td&gt;Cache miss, initial fetch&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 note on source performance&lt;/strong&gt;: The 18-19 MB/s figure reflects test-time NFS mount constraints (NFSv3 single-stream, rsize=64K) — &lt;strong&gt;not&lt;/strong&gt; FSx for ONTAP's throughput limit (128 MBps). Switching the source mount to NFSv4.1 + rsize=1M is expected to significantly improve cache miss performance (proposed upstream in &lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/42" rel="noopener noreferrer"&gt;Issue #42&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;L2 NVMe is &lt;strong&gt;5.6x faster&lt;/strong&gt; than source fetch. After a proxy reboot with empty RAM, data is served from NVMe cache immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Throughput
&lt;/h3&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;Throughput&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sequential read (proxy L1 → client)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;422-619 MB/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large write (write-through)&lt;/td&gt;
&lt;td&gt;157-218 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP multipart upload (50 MB)&lt;/td&gt;
&lt;td&gt;36.6 MiB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Latency
&lt;/h3&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;Cold&lt;/th&gt;
&lt;th&gt;Cached&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10 MB file read&lt;/td&gt;
&lt;td&gt;55 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;28x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 KB small file&lt;/td&gt;
&lt;td&gt;1.75 ms&lt;/td&gt;
&lt;td&gt;1.5 ms&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP batch → NFS&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.1 ms/file&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;h3&gt;
  
  
  Working Set &amp;gt; Client RAM
&lt;/h3&gt;

&lt;p&gt;With client RAM at 916 MB and a 500 MB dataset, reading after &lt;code&gt;drop_caches&lt;/code&gt; still achieves &lt;strong&gt;422-428 MB/s&lt;/strong&gt; — served from the KNFSD proxy's 16 GB RAM cache. This is the core value proposition: &lt;strong&gt;small compute nodes can access datasets larger than their local memory at proxy-cache speed&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  nconnect Caveat
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nconnect&lt;/th&gt;
&lt;th&gt;100 MB cold read&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 (default)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;619 MB/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;184 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On t4g.micro (5 Gbps NIC), nconnect=16 is counterproductive. Only effective on 100 Gbps instances (c5n.18xlarge, hpc7g).&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Coherency
&lt;/h3&gt;

&lt;p&gt;S3 AP write visibility through KNFSD depends on NFS attribute cache timeouts:&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;Default&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;acregmin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3s&lt;/td&gt;
&lt;td&gt;Minimum file attribute cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;acdirmax&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;60s&lt;/td&gt;
&lt;td&gt;Maximum directory attribute cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In our tests, S3 AP-written files were immediately visible through KNFSD (new files bypass directory attribute cache). For frequently-overwritten files, set &lt;code&gt;acregmax=5-10&lt;/code&gt; to reduce stale-data windows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Every Pitfall We Hit (Documenting All of Them)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  NFSv3 Breaks Writes (Most Important Finding)
&lt;/h3&gt;

&lt;p&gt;This consumed the most debugging time. When mounting the KNFSD proxy via NFSv3, existing file reads succeed but new file creation returns &lt;code&gt;Stale file handle&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The cause is documented in the &lt;a href="https://docs.kernel.org/next/filesystems/nfs/reexport.html" rel="noopener noreferrer"&gt;Linux Kernel NFS re-export documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;reexport server's filehandle for the reexported object will be X+22 bytes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;FSx for ONTAP filehandles are ~40+ bytes. After +22 bytes = ~62+ bytes. New file creation (which adds subdirectory inode information) pushes past NFSv3's &lt;strong&gt;64-byte limit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;NFSv4.1 supports 128 bytes — ample headroom. Our Terraform &lt;strong&gt;validates against nfs_version="3"&lt;/strong&gt; with a clear error message linking to the upstream issue.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Filed upstream as &lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/40" rel="noopener noreferrer"&gt;Issue #40&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Other Pitfalls
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;proxy-startup.sh immediate failure&lt;/td&gt;
&lt;td&gt;No EC2 API access (no Public IP)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;assign_public_ip = true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM &lt;code&gt;GetParametersByPath&lt;/code&gt; denied&lt;/td&gt;
&lt;td&gt;IAM missing path-level ARN&lt;/td&gt;
&lt;td&gt;Include both &lt;code&gt;.../*&lt;/code&gt; and &lt;code&gt;...&lt;/code&gt; (&lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/41" rel="noopener noreferrer"&gt;Issue #41&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"No storage devices found"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CACHEFILESD_DISK_TYPE=local_nvme&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Correct value: &lt;code&gt;local-nvme&lt;/code&gt; (hyphen)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;FSID_MODE=static&lt;/code&gt; breaks all writes&lt;/td&gt;
&lt;td&gt;fsidd disabled, filehandle unresolvable&lt;/td&gt;
&lt;td&gt;Switch to &lt;code&gt;FSID_MODE=local&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 AP create BadRequest&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--type One_Zone&lt;/code&gt; is for OpenZFS&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;--type ONTAP&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exportfs: No fsid found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;fsidd first-time allocation warning&lt;/td&gt;
&lt;td&gt;No operational impact (ignore)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  FSID_MODE=local: SQLite on FSx for ONTAP
&lt;/h2&gt;

&lt;p&gt;KNFSD's NFS re-export requires stable FSID management. &lt;code&gt;FSID_MODE=static&lt;/code&gt; (fsidd disabled) is fundamentally broken — all write operations fail with Stale file handle.&lt;/p&gt;

&lt;p&gt;The fix: use the Linux kernel's fsidd daemon with SQLite backend, placing the database file on the FSx for ONTAP NFS mount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/srv/nfs/vol1/.knfsd/fsids.sqlite  ← on FSx for ONTAP (persistent, 99.99% SLA)
&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;Aspect&lt;/th&gt;
&lt;th&gt;local (SQLite on FSx)&lt;/th&gt;
&lt;th&gt;external (RDS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Additional cost&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$15-45/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;✅ (FSx for ONTAP SLA)&lt;/td&gt;
&lt;td&gt;✅ (RDS SLA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-node&lt;/td&gt;
&lt;td&gt;△ (single-node only)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium-High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;PoC / single-node&lt;/td&gt;
&lt;td&gt;Production multi-node&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  FlexCache and KNFSD: Different Tools for Different Contexts
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;FlexCache&lt;/th&gt;
&lt;th&gt;KNFSD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source must be ONTAP&lt;/td&gt;
&lt;td&gt;✅ Native integration&lt;/td&gt;
&lt;td&gt;○ Any NFS-compatible filer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple source unification&lt;/td&gt;
&lt;td&gt;Not possible&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write-back/write-around&lt;/td&gt;
&lt;td&gt;✅ (configurable)&lt;/td&gt;
&lt;td&gt;△ (write-through only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Burst read scaling&lt;/td&gt;
&lt;td&gt;△ (within FSx throughput)&lt;/td&gt;
&lt;td&gt;✅ (NVMe cache scales with ASG)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed operations&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;△ (EC2 operations)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;NFSv3/4.x&lt;/td&gt;
&lt;td&gt;NFSv4.1 (required for re-export)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data protection integration&lt;/td&gt;
&lt;td&gt;✅ (SnapMirror/SnapLock)&lt;/td&gt;
&lt;td&gt;None (pass-through)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node elasticity&lt;/td&gt;
&lt;td&gt;△ (manual)&lt;/td&gt;
&lt;td&gt;✅ (Auto Scaling)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FlexCache note&lt;/strong&gt;: FlexCache also doesn't consume origin bandwidth on cache hits (same structure as KNFSD). However, in FSx for ONTAP's managed environment, auto-scaling FlexCache node count is not possible. For burst read bandwidth exceeding FSx max throughput, KNFSD offers more flexibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon File Cache / FSx for Lustre&lt;/strong&gt;: Amazon File Cache is a managed Lustre-compatible cache requiring the Lustre kernel client module on every node. KNFSD uses standard NFS clients (built into Linux/macOS), requiring no additional modules. However, Lustre's parallel I/O bandwidth is inherently higher than NFS re-export — if raw bandwidth is the priority and you can deploy Lustre clients on all nodes, File Cache / FSx for Lustre is the better fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost break-even guideline&lt;/strong&gt;: KNFSD becomes economical when you need burst bandwidth beyond FSx for ONTAP's maximum throughput tier (4,096 MBps at ~$2,000/mo). For steady workloads where FSx throughput scaling suffices, increasing FSx throughput is simpler.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Target Workloads
&lt;/h2&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;Scenario&lt;/th&gt;
&lt;th&gt;KNFSD Role&lt;/th&gt;
&lt;th&gt;S3 AP Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Semiconductor EDA&lt;/td&gt;
&lt;td&gt;DRC/LVS burst&lt;/td&gt;
&lt;td&gt;Tech file repeated reads&lt;/td&gt;
&lt;td&gt;Verification result AI classification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VFX&lt;/td&gt;
&lt;td&gt;Rendering&lt;/td&gt;
&lt;td&gt;Texture/asset Fanout&lt;/td&gt;
&lt;td&gt;Render quality AI check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Genomics&lt;/td&gt;
&lt;td&gt;Reference genome + dbSNP&lt;/td&gt;
&lt;td&gt;Hundreds of nodes on same reference&lt;/td&gt;
&lt;td&gt;Variant AI classification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Financial Risk&lt;/td&gt;
&lt;td&gt;Monte Carlo VaR&lt;/td&gt;
&lt;td&gt;Market data sub-ms delivery&lt;/td&gt;
&lt;td&gt;Anomaly scenario AI detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automotive CAE&lt;/td&gt;
&lt;td&gt;Structural sim&lt;/td&gt;
&lt;td&gt;Shared mesh data reads&lt;/td&gt;
&lt;td&gt;Result comparison AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weather&lt;/td&gt;
&lt;td&gt;NWP ensemble&lt;/td&gt;
&lt;td&gt;GFS initial condition cache&lt;/td&gt;
&lt;td&gt;Extreme weather AI detection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step-by-Step Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Build AMI (~25 min)
&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;# Find your subnet (same AZ as FSx for ONTAP recommended)&lt;/span&gt;
aws ec2 describe-subnets &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=vpc-id,Values=vpc-YOUR-VPC"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Subnets[].{Id:SubnetId,AZ:AvailabilityZone}'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; table

git clone https://github.com/awslabs/knfsd-file-cache.git /tmp/knfsd-file-cache
&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/knfsd-file-cache/image
packer build &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s1"&gt;'REGION=ap-northeast-1'&lt;/span&gt; &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s1"&gt;'ARCH=["arm64"]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s1"&gt;'ASSOCIATE_PUBLIC_IP_ADDRESS=true'&lt;/span&gt; &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="s1"&gt;'SUBNET=subnet-YOUR-SUBNET'&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# → Note the AMI ID (ami-0xxxxxxxxxx)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The AMI is reusable across deploy/destroy cycles. Rebuilds are only needed for kernel updates (~monthly). Normal deploys just pass the existing AMI ID to Terraform.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Terraform Deploy (~3 min)
&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;infrastructure/knfsd-file-cache/terraform
&lt;span class="nb"&gt;cp &lt;/span&gt;terraform.tfvars.example terraform.tfvars
&lt;span class="c"&gt;# Edit: vpc_id, subnet_ids, knfsd_ami_id, source_mounts&lt;/span&gt;

./scripts/preflight-check.sh   &lt;span class="c"&gt;# Validates environment before deploy&lt;/span&gt;
terraform init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Mount from Client
&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;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/knfsd
&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;vers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4.1 &amp;lt;KNFSD_IP&amp;gt;:/vol1 /mnt/knfsd
&lt;span class="nb"&gt;echo test&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /mnt/knfsd/hello.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; /mnt/knfsd/hello.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Always specify &lt;code&gt;vers=4.1&lt;/code&gt;. Using &lt;code&gt;vers=3&lt;/code&gt; will cause Stale file handle on file creation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Verify Cache Effect
&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;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/knfsd/bench.dat &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sync
sync&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo 3 &amp;gt; /proc/sys/vm/drop_caches'&lt;/span&gt;
&lt;span class="nb"&gt;time cat&lt;/span&gt; /mnt/knfsd/bench.dat &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null   &lt;span class="c"&gt;# ~55ms (first read — fetched from source)&lt;/span&gt;
&lt;span class="nb"&gt;time cat&lt;/span&gt; /mnt/knfsd/bench.dat &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null   &lt;span class="c"&gt;# ~2ms  (subsequent — served from cache, 28x)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Dual-Path Test (if you have an S3 AP)
&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;# Write via S3 AP&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;test.txt &lt;span class="s2"&gt;"s3://arn:aws:s3:REGION:ACCOUNT:accesspoint/AP-NAME/file.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Read via KNFSD NFS (immediate reflection)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /mnt/knfsd/file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: The above test uses &lt;code&gt;root&lt;/code&gt; (UID 0) as the S3 AP FileSystemIdentity. This is for testing only. In production, use a dedicated non-root user following least-privilege principles.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  6. Cleanup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
aws ec2 deregister-image &lt;span class="nt"&gt;--image-id&lt;/span&gt; ami-xxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Upstream Feedback
&lt;/h2&gt;

&lt;p&gt;Five issues filed during this verification:&lt;/p&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;Summary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/38" rel="noopener noreferrer"&gt;#38&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Verification report + minimal example for existing source filers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/39" rel="noopener noreferrer"&gt;#39&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Private subnet support (graceful degradation without EC2 API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/40" rel="noopener noreferrer"&gt;#40&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;NFSv3 re-export filehandle overflow documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/41" rel="noopener noreferrer"&gt;#41&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;IAM policy missing path-level ARN (bug fix)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/awslabs/knfsd-file-cache/issues/42" rel="noopener noreferrer"&gt;#42&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;NFSv4.1 source mount support (end-to-end v4.1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Filing upstream issues for things you find broken (or under-documented) is one of the most impactful things you can do with a Preview-stage OSS project. It ensures the maintainers have real-world data to prioritize GA fixes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary and Next Steps
&lt;/h2&gt;

&lt;p&gt;Dual-Path E2E test passed — S3 AP write → KNFSD NFS read works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three key findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;NFSv4.1 mandatory (NFSv3 filehandle size limit breaks writes)&lt;/li&gt;
&lt;li&gt;FSID_MODE=local + SQLite on FSx for ONTAP provides FSID persistence at $0&lt;/li&gt;
&lt;li&gt;S3 AP writes reflect immediately through KNFSD NFS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wait for GA (SLA terms, support scope confirmation)&lt;/li&gt;
&lt;li&gt;Multi-node (ASG + NLB) verification&lt;/li&gt;
&lt;li&gt;EDA/VFX-specific file access pattern benchmarks&lt;/li&gt;
&lt;li&gt;im4gn.16xlarge (30 TB NVMe) L2 bandwidth at production scale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;KNFSD File Cache is Preview, but it clearly functions as a practical read-acceleration layer for burst compute workloads. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small-scale starting point&lt;/strong&gt;: m6gd.xlarge × 1 running 24/7 = ~$215/mo. Daytime-only (10h) = ~$65/mo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/awslabs/knfsd-file-cache" rel="noopener noreferrer"&gt;KNFSD File Cache GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/media/introducing-knfsd-file-cache-extending-your-nfs-storage-into-the-cloud/" rel="noopener noreferrer"&gt;AWS Launch Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/docs/knfsd-s3ap-dual-path-architecture.md" rel="noopener noreferrer"&gt;Dual-Path Architecture Guide (JA)&lt;/a&gt; — 7 industry deep dives + cost estimates + observability&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/tree/main/infrastructure/knfsd-file-cache" rel="noopener noreferrer"&gt;Terraform + Demo Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/blob/main/infrastructure/knfsd-file-cache/docs/fsid-backend-options.md" rel="noopener noreferrer"&gt;FSID Backend Options&lt;/a&gt; — SQLite/RDS/Aurora comparison&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.kernel.org/next/filesystems/nfs/reexport.html" rel="noopener noreferrer"&gt;Linux Kernel NFS Re-export Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/awslabs/knfsd-file-cache/issues?q=author%3AYoshiki0705" rel="noopener noreferrer"&gt;Verification Issues #38-#42&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Preview notice&lt;/strong&gt;: KNFSD File Cache is in Preview as of July 2026. The code is OSS (Apache 2.0) but AWS Preview Service Terms apply. Evaluate for non-production or supplementary use until GA. AWS Support coverage may not extend to KNFSD-specific issues — GitHub Issues is the primary channel. NVMe instance store cache data is protected by AES-256 hardware encryption and auto-erased on instance termination.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>nfs</category>
      <category>hpc</category>
      <category>storage</category>
    </item>
    <item>
      <title>Mackerel's Log Feature Just Opened in Beta — Here's What It Takes to Wire It Into an OTLP Pipeline</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Mon, 20 Jul 2026 08:05:24 +0000</pubDate>
      <link>https://dev.to/aws-builders/mackerels-log-feature-just-opened-in-beta-heres-what-it-takes-to-wire-it-into-an-otlp-pipeline-888</link>
      <guid>https://dev.to/aws-builders/mackerels-log-feature-just-opened-in-beta-heres-what-it-takes-to-wire-it-into-an-otlp-pipeline-888</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mackerel.io/" rel="noopener noreferrer"&gt;Mackerel&lt;/a&gt; — Hatena's Japan-origin observability platform — opened its log feature as &lt;a href="https://mackerel.io/ja/blog/entry/announcement/log-beta-release" rel="noopener noreferrer"&gt;public beta on July 16, 2026&lt;/a&gt;. In response, this repository added Mackerel integration for shipping FSx for ONTAP file access audit logs, EMS events, and FPolicy events via the existing Lambda/OTel Collector pipeline.&lt;/p&gt;

&lt;p&gt;The OTel Collector path worked immediately — just add an exporter config entry. But the direct-send path (Lambda → vendor OTLP endpoint, no Collector) exposed two compatibility gaps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mackerel requires a custom &lt;code&gt;Mackerel-Api-Key&lt;/code&gt; header, not &lt;code&gt;Bearer&lt;/code&gt;/&lt;code&gt;Basic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mackerel's OTLP endpoint only accepts Protobuf, rejecting OTLP/JSON outright&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article covers the generic auth header primitive and limited Protobuf encoder added to close those gaps. Both delivery paths are confirmed E2E against a live Mackerel organization, but since the log feature is still in beta, production deployments should use the Collector path and should not treat Mackerel as the sole audit trail destination.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;: This article covers shipping FSx for ONTAP file access audit logs, EMS events, and FPolicy events to Mackerel via the existing Lambda/OTel Collector integration. It does not cover Collector high availability, Lambda retry/DLQ design, NAT Gateway routing, or log-loss recovery — see the &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;base article (Part 5)&lt;/a&gt; and repository operations docs for those topics.&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;FSx for ONTAP
├─ File access audit (EVTX/XML)
├─ EMS events
└─ FPolicy events
         │
         ▼
Lambda: builds OTLP Logs Data Model
         │
         ├──▶ OTel Collector ──▶ Mackerel [recommended]
         │
         └──▶ Direct to Mackerel [verification path]
              - Mackerel-Api-Key header
              - Accept: */*
              - OTLP/Protobuf required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&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;Yoshiki0705/fsxn-observability-integrations&lt;/a&gt;&lt;br&gt;
New files: &lt;code&gt;integrations/mackerel/&lt;/code&gt;, &lt;code&gt;integrations/otel-collector/otel-collector-config-mackerel.yaml&lt;/code&gt;&lt;br&gt;
Modified: &lt;code&gt;integrations/otel-collector/lambda/{handler,ems_handler,fpolicy_handler}.py&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a standalone entry in the &lt;a href="https://dev.to/aws-builders/why-your-fsx-for-ontap-audit-logs-deserve-better-than-ec2-kod"&gt;Serverless Observability for FSx for ONTAP&lt;/a&gt; series — it's not a continuation of the incident-response article, and doesn't assume you've read it. It builds on &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;Part 5&lt;/a&gt; (the vendor-neutral OTel Collector pattern), since that's exactly the mechanism a new backend plugs into.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Mackerel, and Why Now
&lt;/h2&gt;

&lt;p&gt;This series has already shipped FSx for ONTAP audit logs to 9 observability platforms — Datadog, Splunk, Grafana Cloud, Honeycomb, and others — all confirmed E2E against real accounts. A few readers, mostly from Japan, have asked why Mackerel wasn't on that list. Fair question, and one I'd been wanting to answer for a while.&lt;/p&gt;

&lt;p&gt;The short answer: it wasn't this repository's gap to close. Mackerel simply didn't have a log-sending feature yet. It's offered OpenTelemetry-based tracing (APM) for a while, but logs were a gap in Mackerel's own product.&lt;/p&gt;

&lt;p&gt;There's another direct trigger behind this article. At &lt;a href="https://sre-next.dev/2026/" rel="noopener noreferrer"&gt;SRE NEXT 2026&lt;/a&gt;, I had a chance to casually catch up with some longtime acquaintances on the Mackerel team and trade notes on Observability for Amazon FSx for NetApp ONTAP. That particular conversation didn't lead anywhere specific on its own, but the conference as a whole gave me useful ideas and motivation for infrastructure-focused development, which fed directly into picking this integration up.&lt;/p&gt;

&lt;p&gt;That changed on &lt;strong&gt;July 16, 2026&lt;/strong&gt;, when Mackerel &lt;a href="https://mackerel.io/ja/blog/entry/announcement/log-beta-release" rel="noopener noreferrer"&gt;opened its log feature as a public beta&lt;/a&gt;. A feature I'd been waiting on finally showed up, which is the actual reason this article exists.&lt;/p&gt;

&lt;p&gt;That said, "just opened in beta" is worth taking seriously. The feature itself is new and explicitly non-GA — a genuinely different situation from adding an eighth or ninth already-mature vendor without much thought. This article walks through what actually broke while wiring it up, and why it still sits in a separate section instead of being quietly folded into the main vendor table.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Vendor-neutral note&lt;/strong&gt;: this article evaluates Mackerel's OTLP log ingestion on its technical merits — the same way this repository's &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/vendor-comparison.md" rel="noopener noreferrer"&gt;vendor-comparison.md&lt;/a&gt; treats Datadog, Grafana, Splunk, and the other 9. No platform in this comparison is framed as superior to another; each fits a different context, and the goal here is documenting what's actually supported, not ranking who's "better."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;When this integration is a good fit:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You already use Mackerel for monitoring/APM&lt;/li&gt;
&lt;li&gt;You want to integrate FSx for ONTAP events into your existing incident investigation workflow&lt;/li&gt;
&lt;li&gt;You use an OTel Collector as a shared telemetry router&lt;/li&gt;
&lt;li&gt;You can evaluate a beta feature in a non-production or supplementary capacity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to consider other options for now:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regulatory/audit requirements demand guaranteed data retention&lt;/li&gt;
&lt;li&gt;This would be your sole security log destination&lt;/li&gt;
&lt;li&gt;Log loss is unacceptable and formal support terms are required&lt;/li&gt;
&lt;li&gt;Strict data residency or contract-path conditions apply&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Mackerel's Beta Actually Supports
&lt;/h2&gt;

&lt;p&gt;Before writing any integration code, the first step was reading Mackerel's own documentation rather than assuming logs would work like traces. As of July 2026, the log-sending method documented by Mackerel's official help is OpenTelemetry-based. This article uses OTLP/HTTP accordingly. Some things turned out to be shared, some didn't:&lt;/p&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;Mackerel Tracing (APM)&lt;/th&gt;
&lt;th&gt;Mackerel Logs (beta, 2026-07-16)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;OTLP/HTTP&lt;/td&gt;
&lt;td&gt;OTLP/HTTP (same)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://otlp-vaxila.mackerelio.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Mackerel-Api-Key&lt;/code&gt; header (Write scope)&lt;/td&gt;
&lt;td&gt;Same header, same scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Required extra header&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Accept: */*&lt;/code&gt; (documented as required by Mackerel; the reason isn't publicly documented)&lt;/td&gt;
&lt;td&gt;Same requirement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grouping key&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;service.namespace&lt;/code&gt; + &lt;code&gt;service.name&lt;/code&gt; resource attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retention&lt;/td&gt;
&lt;td&gt;(APM's own terms)&lt;/td&gt;
&lt;td&gt;30 days planned at GA; beta operates under the same window but &lt;strong&gt;with no guarantee&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collector batch config&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;~3.5MB (&lt;code&gt;sending_queue.batch.max_size: 3500000&lt;/code&gt; bytes). This is a Collector-side setting matching Mackerel's official config example, not an absolute API max request size&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The practical upshot: if you already have an OTel Collector sending traces to Mackerel, adding logs is a second exporter pipeline entry, not a new integration pattern.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwotmejtys8w2tj8s16gb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwotmejtys8w2tj8s16gb.png" alt="Mackerel Logs UI — beta notice and search history" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Mackerel's Logs feature top page. The beta notice (no data retention guarantee, unscheduled maintenance possible) is explicitly displayed.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Architecture Question: Collector or Direct-Send?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Collector path is recommended for most deployments.&lt;/strong&gt; Direct-send is an option for minimal setups without a Collector, or for verifying protocol compatibility. Mackerel's own documentation also &lt;a href="https://mackerel.io/ja/docs/entry/log/sending" rel="noopener noreferrer"&gt;recommends placing a Collector in between&lt;/a&gt; for filtering and masking.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criteria&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;Production use, masking, multiple destinations, retry control&lt;/td&gt;
&lt;td&gt;Collector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimize components in a small environment&lt;/td&gt;
&lt;td&gt;Direct-send&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Just want to try Mackerel integration first&lt;/td&gt;
&lt;td&gt;Local Collector test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handling security audit logs&lt;/td&gt;
&lt;td&gt;Collector (filter sensitive attributes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This repository's &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/tree/main/integrations/otel-collector" rel="noopener noreferrer"&gt;OTel Collector integration&lt;/a&gt; already ships FSx for ONTAP audit/EMS/FPolicy logs to Datadog, Grafana Cloud, and Honeycomb simultaneously from one Lambda codebase — the Lambda builds a backend-neutral OTLP payload, and the Collector's &lt;code&gt;exporters&lt;/code&gt; config decides where it goes. Adding Mackerel as a fourth backend to that pattern needed &lt;strong&gt;zero Lambda changes&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="c1"&gt;# otel-collector-config-mackerel.yaml&lt;/span&gt;
&lt;span class="c1"&gt;# NOTE: This repo's verified Collector version (0.152.0) uses otlp_http. Older&lt;/span&gt;
&lt;span class="c1"&gt;# versions may require otlphttp (no underscore). Check your version's docs.&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/mackerel&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-vaxila.mackerelio.com&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;Accept&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*/*"&lt;/span&gt;
      &lt;span class="na"&gt;Mackerel-Api-Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:MACKEREL_APIKEY}&lt;/span&gt;
    &lt;span class="na"&gt;sending_queue&lt;/span&gt;&lt;span class="pi"&gt;:&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;max_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3500000&lt;/span&gt;
        &lt;span class="na"&gt;sizer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bytes&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;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/mackerel&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 the whole integration for the Collector-mediated path. But this repository also supports a &lt;strong&gt;direct-send&lt;/strong&gt; path — Lambda posts straight to a vendor's OTLP endpoint, skipping the Collector entirely — used today for Grafana Cloud via &lt;code&gt;AUTH_MODE=basic&lt;/code&gt;. Checking whether that path also worked for Mackerel is where things got more interesting.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Gap: A Header Mackerel Needs That the Direct-Send Path Couldn't Send
&lt;/h2&gt;

&lt;p&gt;"Add an exporter and you're done" turned out not to cover every path. The direct-send auth logic in &lt;code&gt;handler.py&lt;/code&gt;, &lt;code&gt;ems_handler.py&lt;/code&gt;, and &lt;code&gt;fpolicy_handler.py&lt;/code&gt; only supported two modes:&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;if&lt;/span&gt; &lt;span class="n"&gt;AUTH_MODE&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;basic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;encoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&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;token&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="nf"&gt;decode&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="n"&gt;auth_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;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&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;Basic &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;encoded&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="k"&gt;else&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&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;Bearer &lt;/span&gt;&lt;span class="si"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mackerel's auth is neither. It's a bare custom header, &lt;code&gt;Mackerel-Api-Key: &amp;lt;token&amp;gt;&lt;/code&gt;, with no &lt;code&gt;Bearer&lt;/code&gt; or &lt;code&gt;Basic&lt;/code&gt; wrapping. At that point, direct-send to Mackerel without a Collector simply wasn't possible.&lt;/p&gt;

&lt;p&gt;The fix is a generic primitive rather than a Mackerel-specific branch:&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;AUTH_MODE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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;AUTH_MODE&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;bearer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# "bearer", "basic", or "header"
&lt;/span&gt;&lt;span class="n"&gt;AUTH_HEADER_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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;AUTH_HEADER_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;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;EXTRA_HEADERS_JSON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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;EXTRA_HEADERS_JSON&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="c1"&gt;# ...
&lt;/span&gt;&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;AUTH_MODE&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;header&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_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AUTH_HEADER_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;EXTRA_HEADERS_JSON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;extra_headers&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;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EXTRA_HEADERS_JSON&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_headers&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;extra_headers&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AUTH_MODE=header&lt;/code&gt; sends the secret verbatim under any header name you specify. Mackerel happens to be the first consumer, but nothing about the option itself references Mackerel. &lt;code&gt;EXTRA_HEADERS_JSON&lt;/code&gt; covers Mackerel's required &lt;code&gt;Accept: */*&lt;/code&gt;, which isn't authentication at all — just a static header their backend depends on.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;EXTRA_HEADERS_JSON&lt;/code&gt; constraint&lt;/strong&gt;: This is for static, non-secret headers like &lt;code&gt;Accept&lt;/code&gt; only. Do not specify auth headers (&lt;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;Mackerel-Api-Key&lt;/code&gt;, etc.), &lt;code&gt;Content-Type&lt;/code&gt;, &lt;code&gt;Content-Length&lt;/code&gt;, or &lt;code&gt;Host&lt;/code&gt; — these are controlled by &lt;code&gt;AUTH_MODE&lt;/code&gt;/&lt;code&gt;OTLP_CONTENT_TYPE&lt;/code&gt; settings or set automatically by the HTTP library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same fix went into &lt;code&gt;ems_handler.py&lt;/code&gt; and &lt;code&gt;fpolicy_handler.py&lt;/code&gt; too. The CloudFormation template passes these env vars to all three Lambda functions, so patching only one would have left the other two silently ignoring &lt;code&gt;AUTH_MODE=header&lt;/code&gt; if someone set it.&lt;/p&gt;

&lt;p&gt;12 new unit tests cover this across the three handlers (4 each in &lt;code&gt;handler.py&lt;/code&gt;, &lt;code&gt;ems_handler.py&lt;/code&gt;, and &lt;code&gt;fpolicy_handler.py&lt;/code&gt;): custom-header auth, &lt;code&gt;EXTRA_HEADERS_JSON&lt;/code&gt; merging, the case where no API key secret is configured but extra headers still apply, and invalid &lt;code&gt;EXTRA_HEADERS_JSON&lt;/code&gt; producing a startup warning with extra headers disabled. &lt;code&gt;cfn-lint&lt;/code&gt; and &lt;code&gt;gitleaks&lt;/code&gt; run clean on the modified template and new files.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Second Gap: Auth Was Fixed, But the Payload Format Wasn't
&lt;/h2&gt;

&lt;p&gt;Fixing auth wasn't the finish line, though. The unit tests mock the HTTP layer entirely, so they passed without catching what a real API key call turned up next:&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="err"&gt;OTLP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="err"&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"json is not supported yet"&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;&lt;code&gt;_send_otlp_payload&lt;/code&gt; always sends OTLP/JSON with &lt;code&gt;Content-Type: application/json&lt;/code&gt;, but Mackerel's OTLP endpoint accepts Protobuf only and rejects JSON outright. The Collector-mediated path never surfaced this: the Collector decodes received OTLP data into its internal data model, then re-serializes via the &lt;code&gt;otlphttp&lt;/code&gt; exporter whose default encoding is Protobuf (&lt;code&gt;proto&lt;/code&gt;). So even though the Lambda sends OTLP/JSON to the Collector, the Collector sends OTLP/Protobuf to Mackerel. The direct-send Lambda code simply never had a Protobuf encoder.&lt;/p&gt;

&lt;p&gt;This project's policy is to include no additional Python dependency packages in the Lambda deployment package. Only &lt;code&gt;boto3&lt;/code&gt; and &lt;code&gt;urllib3&lt;/code&gt; — both available in the Lambda Python runtime environment — are used; &lt;code&gt;protobuf&lt;/code&gt;/&lt;code&gt;opentelemetry-proto&lt;/code&gt; are not bundled. Adding those packages for one vendor felt disproportionate, so the fix is a small hand-rolled encoder (&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/lambda/otlp_protobuf.py" rel="noopener noreferrer"&gt;&lt;code&gt;otlp_protobuf.py&lt;/code&gt;&lt;/a&gt;). This encoder is not a general-purpose OTLP implementation. It covers only the limited data structures this repository's FSx for ONTAP log builders produce: &lt;code&gt;timeUnixNano&lt;/code&gt;, &lt;code&gt;severityNumber&lt;/code&gt;, &lt;code&gt;severityText&lt;/code&gt;, &lt;code&gt;body.stringValue&lt;/code&gt;, &lt;code&gt;attributes&lt;/code&gt; (stringValue only), resource attributes (stringValue only), and &lt;code&gt;InstrumentationScope&lt;/code&gt;. It does not support the full OTLP Logs field set or all &lt;code&gt;AnyValue&lt;/code&gt; types, and is not intended for reuse outside this project. Field numbers came straight from the &lt;a href="https://github.com/open-telemetry/opentelemetry-proto" rel="noopener noreferrer"&gt;official OTLP proto definitions&lt;/a&gt; (Apache License 2.0); the encoder's byte output was cross-checked against the official &lt;code&gt;opentelemetry-proto&lt;/code&gt; generated Python classes in a throwaway virtualenv before it touched a real account. We monitor official proto definition updates and maintain compatibility tests against the generated classes.&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;OTLP_CONTENT_TYPE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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;OTLP_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;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# "json" or "protobuf"
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;protobuf&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;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/x-protobuf&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;encode_logs_data&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="k"&gt;else&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;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="n"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding &lt;code&gt;OTLP_CONTENT_TYPE=protobuf&lt;/code&gt;, calling &lt;code&gt;handler.py&lt;/code&gt;'s actual &lt;code&gt;build_otlp_payload&lt;/code&gt; and &lt;code&gt;_send_otlp_payload&lt;/code&gt; functions — the same code the Lambda runs, not a reimplementation — against the real Mackerel API key succeeded. For the two sample records sent (&lt;code&gt;ReadData&lt;/code&gt;/&lt;code&gt;Success&lt;/code&gt; and &lt;code&gt;Delete&lt;/code&gt;/&lt;code&gt;Access Denied&lt;/code&gt;), this repository's generated audit attributes — operation type, result, file path, SVM name, user information, event timestamp — were confirmed searchable on Mackerel's log search UI.&lt;/p&gt;

&lt;p&gt;Unit tests verify auth headers, Content-Type branching, and encoding output. However, they cannot detect vendor-side acceptance behavior, rate limits, network path issues, or search-indexing delays — those require E2E tests against the real endpoint, which we run separately. 20 more unit tests were added: 3 per handler for the new content-type path, plus 11 in a dedicated &lt;code&gt;test_otlp_protobuf.py&lt;/code&gt;, bringing the OTel Collector integration's test suite to 110 passing tests.&lt;/p&gt;

&lt;p&gt;Verifying one delivery path for a vendor doesn't verify another. Auth headers, payload wire format, and network reachability can each fail independently, and a unit-test suite that mocks the HTTP layer won't catch a payload-format mismatch that only a real vendor endpoint enforces.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Still Isn't in the "9 Vendors, All Verified" List
&lt;/h2&gt;

&lt;p&gt;Every other vendor in this repository has a checkmark for a reason: an actual payload was sent to a live account and confirmed to arrive. Mackerel now has that checkmark too. Sample FSx audit log payloads showed up in Mackerel's log search UI with this repository's generated audit attributes confirmed searchable, through both the Collector-mediated path and the direct-send path once &lt;code&gt;OTLP_CONTENT_TYPE=protobuf&lt;/code&gt; was in place. This repo's &lt;code&gt;README.md&lt;/code&gt; reflects that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;E2E verified (open beta)&lt;/strong&gt; — Confirmed end-to-end against a live account, but the backend platform's own feature is itself in open beta (no data retention guarantee, unscheduled maintenance possible) — see the linked integration README for beta constraints.&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;Verification Detail&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;E2E verified&lt;/td&gt;
&lt;td&gt;2026-07-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collector version&lt;/td&gt;
&lt;td&gt;0.152.0 (OpenTelemetry Collector Contrib)&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;Repository commit&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;main&lt;/code&gt; branch HEAD at time of publication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc58hebgq6qmunv99tauw.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc58hebgq6qmunv99tauw.png" alt="Mackerel Log Search Results — FSx for ONTAP audit logs" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;E2E evidence: Searching by the fsxn-audit service shows 4 records — ReadData/Success, WriteData/Success, Open/Failure, Delete/Access Denied at WARN/INFO severity levels.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpym74aase3lrwmateeee.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpym74aase3lrwmateeee.png" alt="Mackerel Log Detail — Delete/Access Denied audit attributes" width="800" height="426"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Log detail: Operation type, result, SVM name, file path, user info, client IP, and timestamp are all retained as searchable attributes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the code-side gate is cleared. There's a second reason the integration still doesn't sit in the main table, though, and it's not about this repository's code at all — it's about what Mackerel itself has published about its own feature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No data-retention guarantee during the beta period&lt;/li&gt;
&lt;li&gt;Unscheduled maintenance is possible&lt;/li&gt;
&lt;li&gt;GA is planned for fall 2026, but the exact date isn't fixed yet&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cost note&lt;/strong&gt;: As of July 2026, the beta period is free. GA pricing is planned as ingest-volume-based billing. Pricing details, measurement units, and free-tier terms may change after this article's publication — check &lt;a href="https://mackerel.io/ja/pricing/" rel="noopener noreferrer"&gt;Mackerel's official pricing page&lt;/a&gt; for current terms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Folding this integration quietly into the "supported vendors" table with the same checkmark as the other 9 would let someone deploying it for security alerting (this repository's automated incident-response module, for instance) reasonably assume the same confidence level as Datadog or Splunk. That assumption would be wrong, not because of anything this repo's code does, but because the platform underneath it is explicitly pre-GA. So the integration stays in a separate "Emerging / Beta Vendors Under Evaluation" section of &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/vendor-comparison.md" rel="noopener noreferrer"&gt;vendor-comparison.md&lt;/a&gt;, marked as E2E-verified-but-beta, until Mackerel's own log feature reaches GA.&lt;/p&gt;

&lt;p&gt;One thing worth flagging for anyone reproducing this kind of verification: during investigation we also inspected GraphQL requests used internally by the browser UI, but we have not confirmed these are public/stable APIs. This repository's permanent E2E verification does not depend on them — final confirmation uses the official UI's log search screen combined with the Collector's own send metrics (&lt;code&gt;otelcol_exporter_sent_log_records&lt;/code&gt;). Internal implementation details on Mackerel's side may change without notice.&lt;/p&gt;


&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;p&gt;FSx for ONTAP audit logs may contain usernames, file paths, share names, and client IP addresses — content that may constitute sensitive or personally-identifiable information depending on your organization's policies. Before sending production data to any external observability service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm your organization's data classification, storage location, access control, and retention requirements&lt;/li&gt;
&lt;li&gt;Use OTel Collector &lt;code&gt;filter&lt;/code&gt;/&lt;code&gt;transform&lt;/code&gt; processors to remove or mask sensitive attributes as needed (see this repository's &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/integrations/otel-collector/docs/en/pii-redaction-cookbook.md" rel="noopener noreferrer"&gt;PII redaction cookbook&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Do not use beta-period Mackerel as the sole storage destination for audit trails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On &lt;code&gt;EXTRA_HEADERS_JSON&lt;/code&gt; specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never put secret values in it — API keys belong in Secrets Manager&lt;/li&gt;
&lt;li&gt;Never put API keys directly in CloudFormation parameters or Lambda environment variables&lt;/li&gt;
&lt;li&gt;Never log full header contents at runtime&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Troubleshooting: 4-Layer Isolation
&lt;/h2&gt;

&lt;p&gt;When issues occur, isolate across these four layers:&lt;/p&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;Target&lt;/th&gt;
&lt;th&gt;What to Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Generation&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;OTLP record count, required attributes, timestamp, severity, resource attributes. If enabling debug logging, avoid outputting sensitive info like file paths or usernames&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Transmission&lt;/td&gt;
&lt;td&gt;Lambda / Collector exporter&lt;/td&gt;
&lt;td&gt;HTTP response code, timeouts, retry results (400, 401, 403, 429, 5xx)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Acceptance&lt;/td&gt;
&lt;td&gt;Collector metrics / OTLP response&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sent&lt;/code&gt;/&lt;code&gt;failed&lt;/code&gt; log records, partial success, drops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Consumption&lt;/td&gt;
&lt;td&gt;Mackerel UI&lt;/td&gt;
&lt;td&gt;service attribute match, search time range, filter conditions, indexing delay&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Distinguishing Collector/HTTP transmission failures (layers 2-3) from post-acceptance search/display issues (layer 4) is critical. If Mackerel rejects with HTTP 400, that's a layer 2-3 problem, not layer 4. See the troubleshooting table in &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/tree/main/integrations/mackerel/docs" rel="noopener noreferrer"&gt;&lt;code&gt;integrations/mackerel/docs/&lt;/code&gt;&lt;/a&gt; for detailed guidance.&lt;/p&gt;


&lt;h2&gt;
  
  
  Recommended Adoption Steps
&lt;/h2&gt;

&lt;p&gt;For evaluating this beta integration in stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;E2E verification with sample logs using a local Collector (&lt;code&gt;test-local-mackerel.sh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Send non-production FSx for ONTAP audit logs in limited scope&lt;/li&gt;
&lt;li&gt;Evaluate send volume, data loss, searchability, and sensitive-information handling&lt;/li&gt;
&lt;li&gt;In production, implement dual-send to your existing log platform (CloudWatch Logs, S3, existing vendor)&lt;/li&gt;
&lt;li&gt;During the beta period, do not use Mackerel as your sole audit trail destination&lt;/li&gt;
&lt;li&gt;Re-evaluate for primary-path promotion once data retention terms, support conditions, and pricing are finalized at GA&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  What's Actually Usable Today
&lt;/h2&gt;

&lt;p&gt;Both paths below are now confirmed working end-to-end:&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;# Collector-mediated (recommended, zero Lambda changes)&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;integrations/otel-collector/.env.mackerel.example .env.mackerel
&lt;span class="c"&gt;# edit .env.mackerel with a Write-scoped Mackerel API key&lt;/span&gt;
bash integrations/otel-collector/scripts/test-local-mackerel.sh
&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;&lt;span class="c"&gt;# Direct-send (skips the Collector; needs AUTH_MODE=header AND OtlpContentType=protobuf)&lt;/span&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;OtlpEndpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://otlp-vaxila.mackerelio.com &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;header &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;AuthHeaderName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Mackerel-Api-Key &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;ExtraHeadersJson&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{"Accept":"*/*"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;OtlpContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;protobuf &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;&amp;lt;secret-arn&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    ... &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_NAMED_IAM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;OtlpContentType=protobuf&lt;/code&gt; is not optional for Mackerel — without it, the send fails with the JSON-rejection error described above, even with correct auth.&lt;/p&gt;

&lt;p&gt;Full setup guides (bilingual) are in &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/tree/main/integrations/mackerel/docs" rel="noopener noreferrer"&gt;&lt;code&gt;integrations/mackerel/docs/&lt;/code&gt;&lt;/a&gt;, including a troubleshooting table that specifically tells you to isolate Collector-side failures from Mackerel-side rejections before debugging the wrong layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up, and What's Next
&lt;/h2&gt;

&lt;p&gt;So that's where Mackerel's log beta stands today. Getting there took two fixes that aren't visible when only the Collector-mediated path is tested — a custom auth header format, and a JSON-vs-Protobuf mismatch — and neither one surfaced until real credentials hit the direct-send path. A green unit-test suite doesn't tell you that.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;AUTH_MODE=header&lt;/code&gt;, &lt;code&gt;EXTRA_HEADERS_JSON&lt;/code&gt;, and &lt;code&gt;OTLP_CONTENT_TYPE=protobuf&lt;/code&gt; options added here are not Mackerel-specific code — they're reusable primitives for any future OTLP/HTTP backend that requires a custom auth header or Protobuf-only ingestion.&lt;/p&gt;

&lt;p&gt;A few things to watch going forward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Watch for Mackerel's GA announcement for its log feature (data retention terms, pricing) — this article and the linked docs will be updated, not silently left stale, once that happens.&lt;/li&gt;
&lt;li&gt;Once GA lands with confirmed data retention conditions, support terms, and pricing, move Mackerel from "Emerging / Beta Vendors" into the main "Supported Vendors" comparison table.&lt;/li&gt;
&lt;li&gt;If you're running Mackerel already, the setup guide's sample OTLP payload is a good starting point to validate your own organization's setup independently.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mackerel's log feature still has some growing to do before GA, and I'm looking forward to seeing where it lands. Hope this was useful to someone wiring up the same thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/tree/main/integrations/mackerel" rel="noopener noreferrer"&gt;GitHub: &lt;code&gt;integrations/mackerel/&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/tree/main/integrations/otel-collector" rel="noopener noreferrer"&gt;GitHub: OTel Collector integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mackerel.io/ja/blog/entry/announcement/log-beta-release" rel="noopener noreferrer"&gt;Mackerel: Log feature open beta announcement (JA)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mackerel.io/ja/docs/entry/log/sending" rel="noopener noreferrer"&gt;Mackerel: Sending logs to Mackerel (JA help docs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mackerel.io/ja/pricing/" rel="noopener noreferrer"&gt;Mackerel: Pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/vendor-comparison.md" rel="noopener noreferrer"&gt;GitHub: Vendor Comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlphttpexporter" rel="noopener noreferrer"&gt;OpenTelemetry Collector: OTLP HTTP Exporter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>observability</category>
      <category>opentelemetry</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
