<?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>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;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;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, but &lt;strong&gt;the public documentation still reads "5 GB" and "50 GB" and is being corrected.&lt;/strong&gt; 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; 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, but its&lt;br&gt;
published Region pairs are eight for Google Cloud and one for OCI (us-east-1 to us-ashburn-1), and&lt;br&gt;
&lt;strong&gt;neither includes a Japanese Region.&lt;/strong&gt; Azure is stated as planned, which is neither GA nor Preview.&lt;br&gt;
Starting from Japan therefore means joining Direct Connect to the other cloud's circuit inside an&lt;br&gt;
interconnection provider's fabric, and whether that is possible is decided not by the Region-pair&lt;br&gt;
table but by whether Direct Connect locations, the other cloud's connection locations and the&lt;br&gt;
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;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>Embedding AI Agents into a File Portal — From AgentCore MCP to Multi-Agent Teams (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 3)&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://hakobiya.hatenablog.com/entry/fsxn-file-portal-1-browser-access" rel="noopener noreferrer"&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;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;
  
  
  If You're Already Using ChatGPT / Copilot / Claude for Document Analysis
&lt;/h2&gt;

&lt;p&gt;When using general-purpose AI assistants (ChatGPT, GitHub Copilot, Claude, etc.) for file analysis, the typical flow is "upload file → AI analyzes → returns results." The portal agent takes a different approach.&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;ChatGPT / Copilot (upload-based)&lt;/th&gt;
&lt;th&gt;Portal Agent (NAS-connected)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File access&lt;/td&gt;
&lt;td&gt;Manual upload (one file at a time)&lt;/td&gt;
&lt;td&gt;Direct access to files on NAS (via S3 AP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Only uploaded files&lt;/td&gt;
&lt;td&gt;Cross-volume search across entire storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage operations&lt;/td&gt;
&lt;td&gt;Not possible&lt;/td&gt;
&lt;td&gt;Proposes SnapLock enable, user block, etc. (with HITL approval)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP-specific features&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;ARP/AI status checks, Snapshot operations, export-policy changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data movement&lt;/td&gt;
&lt;td&gt;File content sent to external service&lt;/td&gt;
&lt;td&gt;File content processed within same-region Bedrock (VPC-contained possible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Destructive operation safety&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;HITL modal for human approve/reject&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;This portal does not attempt to replace general-purpose AI assistants.&lt;/strong&gt; It adds NAS-connected file intelligence — file discovery, content understanding, and storage operation proposals. For general Q&amp;amp;A, ChatGPT and Copilot remain well-suited tools for those tasks.&lt;/p&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;KB sync is asynchronous (minutes to tens of minutes lag after file changes)&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 in &lt;code&gt;portal-config.ts&lt;/code&gt;, semantic mode shows a "KB not configured" error&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;Setting&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Changeable&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;Model&lt;/td&gt;
&lt;td&gt;&lt;code&gt;amazon.nova-lite-v1:0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;Lowest cost. Use Claude 3.5 Haiku for quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max tokens&lt;/td&gt;
&lt;td&gt;4096 (output)&lt;/td&gt;
&lt;td&gt;○&lt;/td&gt;
&lt;td&gt;Increase for long analyses&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;○&lt;/td&gt;
&lt;td&gt;Balance with token consumption&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;—&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;Model is configurable via &lt;code&gt;bedrockModelId&lt;/code&gt; in &lt;code&gt;portal-config.ts&lt;/code&gt;. Recommended: Nova Lite for testing, Claude 3.5 Haiku for production.&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. To control context window size, adjust &lt;code&gt;maxHistoryMessages&lt;/code&gt; in &lt;code&gt;portal-config.ts&lt;/code&gt; (default: 10). Reducing history lowers cost but loses conversation context in longer sessions.&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;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 substantially. 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). Adjust &lt;code&gt;maxHistoryMessages&lt;/code&gt; 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://hakobiya.hatenablog.com/entry/fsxn-file-portal-1-browser-access" rel="noopener noreferrer"&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://hakobiya.hatenablog.com/entry/fsxn-file-portal-2-ransomware-worm" rel="noopener noreferrer"&gt;Part 2: From ARP/AI Incident Response to Regulatory Retention Management&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 three-part 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;/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>Embedding Storage Operations into a File Portal — From ARP/AI Incident Response to Regulatory Retention Management (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 3)&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://hakobiya.hatenablog.com/entry/fsxn-file-portal-1-browser-access" rel="noopener noreferrer"&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;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;: structurally blocks AI processing for paths such as &lt;code&gt;/dicom/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EMS Events&lt;/strong&gt;: real-time ONTAP alert display&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;: one-click recovery from snapshots&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 / 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 instantly check ransomware detection state 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;Dashboard detection → one-click containment&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 — Structurally Blocking AI Processing
&lt;/h2&gt;

&lt;p&gt;Prevents the "accidentally sent PHI data to an external AI" scenario at the UI level in HIPAA environments.&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;. The PHI guardrail is an additional UI-level defense — both the BAA and technical controls are 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;. 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.&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, immediate&lt;/td&gt;
&lt;td&gt;Requires folder naming convention compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic Dispatch (10 endpoints)&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;One-click 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:&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/solutions/amplify-portal/docs/SECURITY-REVIEW.md" rel="noopener noreferrer"&gt;Security Review&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://hakobiya.hatenablog.com/entry/fsxn-file-portal-1-browser-access" rel="noopener noreferrer"&gt;Part 1: Adding a File Portal to FSx for ONTAP S3 AP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hakobiya.hatenablog.com/entry/fsxn-file-portal-3-ai-agent-mcp" rel="noopener noreferrer"&gt;Part 3: Embedding AI Agents into a File Portal&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;Structurally prevents AI processing of regulated data&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.&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>Adding a File Portal to FSx for ONTAP S3 Access Points — Choosing Between Amplify Gen2 and Nextcloud (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 3)&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;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;Enable S3 AP on existing volumes — all portal features become available immediately&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;Connect via SnapMirror or DataSync to FSx for ONTAP, then add capabilities incrementally&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)&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 SaaS file sharing&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No real-time co-authoring (like Google Docs simultaneous editing)&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Authentication is independent via Cognito (SAML/OIDC federation supported). No impact on existing SaaS contracts or licenses.&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;Current Setup&lt;/th&gt;
&lt;th&gt;What this portal adds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Box for contracts&lt;/td&gt;
&lt;td&gt;Tamperproof Snapshot on NAS originals (FISC 7-year retention)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SharePoint for design docs &amp;amp; reports&lt;/td&gt;
&lt;td&gt;AI anomaly detection and classification on NAS files (one-click trigger)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Drive for team files&lt;/td&gt;
&lt;td&gt;"Who accessed what when" audit trail (CloudTrail + Athena)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dropbox for project file sync&lt;/td&gt;
&lt;td&gt;Ransomware detection + automated containment (ARP/AI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS/SMB direct access only&lt;/td&gt;
&lt;td&gt;Browser-based file access for non-technical users + AI analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Industry-specific examples&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Financial services&lt;/strong&gt;: "Hundreds of TB of trading logs and risk calculation results on NFS. Daily documents in SharePoint, but we need anomaly detection on logs and 7-year audit trail retention"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manufacturing&lt;/strong&gt;: "500TB of CAD files on NFS, daily document sharing via SharePoint. Add only AI quality inspection on CAD files through the portal"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare&lt;/strong&gt;: "DICOM images stored on NAS, clinical data in a dedicated system. Need AI diagnostic support access to images with HIPAA-compliant retention management"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Media &amp;amp; entertainment&lt;/strong&gt;: "Multiple PB of video assets (ProRes/MXF). Daily editing via NFS mount from Avid/Premiere, but need AI auto-tagging of content management metadata"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal&lt;/strong&gt;: "Tens of thousands of contract PDFs accumulated on NAS. Need AI-powered content search, automated classification, and deadline visibility"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research&lt;/strong&gt;: "Hundreds of TB of genomics analysis and simulation results. Researchers want to search and preview results from a browser without using CLI"&lt;/li&gt;
&lt;/ul&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)&lt;/td&gt;
&lt;td&gt;VPN + mount configuration&lt;/td&gt;
&lt;td&gt;S3 AP + Cognito auth (Internet-origin)&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 UI + FlexClone one-click restore&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 one-click trigger&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 in real time&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;/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;: To add new operations, simply add an &lt;code&gt;action&lt;/code&gt; entry to the Lambda handler's dispatch map. No AppSync schema or CloudFormation changes needed. 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. Same sidebar pattern as Google Drive and Box.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;File selection → AI processing trigger → real-time result display (5s 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 (one-click from 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 Google Drive/Dropbox-like user experience 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%2Fraw.githubusercontent.com%2FYoshiki0705%2FFSx-for-ONTAP-S3AccessPoints-Serverless-Patterns%2Fmain%2Fdocs%2Fimages%2Fpng%2Fcoexistence-3path-en%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FYoshiki0705%2FFSx-for-ONTAP-S3AccessPoints-Serverless-Patterns%2Fmain%2Fdocs%2Fimages%2Fpng%2Fcoexistence-3path-en%402x.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="520"&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 guarantees &lt;strong&gt;strong consistency&lt;/strong&gt; across multi-protocol access. A PutObject via S3 AP is immediately visible from NFS/SMB after write completion. The reverse is also true. Whichever portal writes, all see consistent data.&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 / 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;Full SaaS (Box/SharePoint etc.)&lt;/td&gt;
&lt;td&gt;Zero infrastructure, immediate availability&lt;/td&gt;
&lt;td&gt;NAS data copy required, limited data protection features&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 (128→256/512/1024/2048 MBps)&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 (3 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;/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 my existing Box/SharePoint?&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).&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>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>
    <item>
      <title>Automated Access Blocking for FSx for ONTAP — From Ransomware Detection to Storage-Layer Deny</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Sun, 12 Jul 2026 12:11:54 +0000</pubDate>
      <link>https://dev.to/aws-builders/automated-access-blocking-for-fsx-for-ontap-from-ransomware-detection-to-storage-layer-deny-4l2g</link>
      <guid>https://dev.to/aws-builders/automated-access-blocking-for-fsx-for-ontap-from-ransomware-detection-to-storage-layer-deny-4l2g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem&lt;/strong&gt;: On Amazon FSx for NetApp ONTAP — a file system running on AWS — how do you &lt;em&gt;automatically stop ransomware from spreading&lt;/em&gt; after detection (via ARP/AI, SIEM, or CloudWatch alarm)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: A Lambda function calls ONTAP REST API to immediately block the compromised user/IP at the storage layer and create a protective snapshot — the same containment-phase actions DII Storage Workload Security performs, but triggerable from any detection source, not just DII's own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope&lt;/strong&gt;: This is storage-layer access blocking plus evidence preservation. Host isolation, malware removal, credential rotation, and blocking lateral movement to other systems (eradication/recovery phases) are out of scope and still require human judgment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mechanism&lt;/strong&gt;: SMB user blocking via name-mapping + session disconnect (immediate), NFS IP blocking via export-policy rules + NACL deny rules (immediate, network-layer) — all through REST API and AWS VPC APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger&lt;/strong&gt;: Any source that can publish to SNS (CloudWatch Alarm, Datadog Monitor, Elastic SIEM, manual CLI).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt;: One CloudFormation stack. Tested with 52 unit tests (36 core + 16 network-layer). CLI helper for manual operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: ~$0.51/month (Lambda + SNS + Secrets Manager). Add ~$14/month if VPC Endpoints are needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timing&lt;/strong&gt;: Under 2 minutes end-to-end (verified). Worst-case with Lambda cold start + VPC ENI attach: under 3 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where this fits&lt;/strong&gt;: In NIST CSF 2.0 terms, this module is a Respond-function tool (RS.MI mitigation + RS.AN analysis groundwork). It doesn't touch Govern, or the ML side of Detect — see the dedicated section below. (Companion modules in this repo now cover parts of Recover and Identify too — also covered below.)&lt;/li&gt;
&lt;/ul&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;
Template: &lt;code&gt;shared/templates/automated-response.yaml&lt;/code&gt;&lt;br&gt;
Module: &lt;code&gt;shared/python/ontap_response.py&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is Part 18 of 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 builds on the detection capabilities from &lt;a href="https://dev.to/aws-builders/alerting-on-fsx-for-ontap-audit-logs-no-metric-filter-required-with-cloudwatch-log-alarms-15eg"&gt;Part 17&lt;/a&gt; (CloudWatch Log Alarm) and &lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Part 3&lt;/a&gt; (ARP + Datadog).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Gap Between Detection and Response
&lt;/h2&gt;

&lt;p&gt;The previous articles in this series covered &lt;em&gt;detection&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 3: ARP detects ransomware → EMS event → alert in ~30 seconds&lt;/li&gt;
&lt;li&gt;Part 17: CloudWatch Log Alarm detects suspicious admin operations → alert in ~90 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But an alert is just information. The real question is: &lt;strong&gt;between when detection fires and when a human completes their response, what should the system do automatically?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a ransomware scenario, every second counts. If ARP detects file encryption and you get a Slack notification, but the SOC analyst is in a meeting, the attacker keeps encrypting files for 15-30 minutes before anyone reacts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Idea Came From
&lt;/h2&gt;

&lt;p&gt;We have previously recommended dedicated storage security products — such as &lt;a href="https://docs.netapp.com/us-en/cloudinsights/cs_restrict_user_access.html" rel="noopener noreferrer"&gt;NetApp DII (Data Infrastructure Insights, formerly Cloud Insights) Storage Workload Security&lt;/a&gt; — to close this gap. DII's built-in per-user ML baselines detect anomalies and automatically block users or IPs at the storage layer, using the same ONTAP mechanisms (name-mapping, export-policy rules) this module uses. If you already run DII in your on-premises NetApp ONTAP environment, it remains a valid choice on AWS as well.&lt;/p&gt;

&lt;p&gt;The friction shows up in a specific situation this series keeps running into: &lt;strong&gt;detection that originates outside DII&lt;/strong&gt;. Part 3's ARP → Datadog pipeline, Part 17's CloudWatch Log Alarm, or any third-party SIEM monitor produces an alert. Based on DII's publicly available documentation, its response actions are designed around DII's own detection rather than an arbitrary SNS message from your existing observability stack — we didn't find a documented path to trigger a DII block from an external alert, and building one was outside the scope of this AWS-native series. There's also a practical entry-cost question: DII is a capacity-based licensed product with its own SaaS data plane, which is a different investment than "add a Lambda to a stack you already have."&lt;/p&gt;

&lt;p&gt;So the question became: "Can the same &lt;em&gt;containment-phase&lt;/em&gt; actions — blocking storage access, taking a snapshot, disconnecting sessions — be triggered by any event that can publish to an SNS topic, using only ONTAP REST API and AWS-native services?" That's what &lt;code&gt;shared/python/ontap_response.py&lt;/code&gt; and this article implement. It's not a replacement for DII's ML detection — see the comparison table further down this article, and the fuller comparison plus FAQ ("Does this replace DII Storage Workload Security entirely?") in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/automated-response-guide.md#faq" rel="noopener noreferrer"&gt;Automated Response Guide on GitHub&lt;/a&gt; — it's a way to get the same storage-layer response when your detection already lives somewhere else.&lt;/p&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------------------------------------------------------------+
| Detection (existing - any source)                            |
|                                                              |
|  CloudWatch Log Alarm --+                                    |
|  ARP EMS -&amp;gt; Monitor ----+                                    |
|  FPolicy -&amp;gt; SIEM -------+-- SNS Trigger Topic                |
|  Manual CLI ------------+                                    |
|                                                              |
+--------------------------------------------------------------+
| Response (NEW - this article)                                |
|                                                              |
|  SNS -&amp;gt; Lambda (VPC) -&amp;gt; ONTAP REST API                       |
|                           |                                  |
|                           +- Block SMB user (name-mapping)   |
|                           +- Block NFS IP (export-policy)    |
|                           +- Block NFS IP (NACL — immediate) |
|                           +- Create snapshot (evidence)      |
|                           +- Disconnect CIFS sessions        |
|                           +- Notify (SNS -&amp;gt; email/PagerDuty) |
|                                                              |
+--------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key design choice: &lt;strong&gt;detection and response are decoupled via SNS&lt;/strong&gt;. Any system that can publish a JSON message to an SNS topic can trigger a storage-layer block. This means you're not locked into a single detection product.&lt;/p&gt;




&lt;h2&gt;
  
  
  How ONTAP User Blocking Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SMB: Name-Mapping Deny
&lt;/h3&gt;

&lt;p&gt;ONTAP resolves Windows users to UNIX users via name-mapping rules. By mapping a Windows user to an empty UNIX identity (&lt;code&gt;" "&lt;/code&gt;), the SID-to-UNIX translation fails and all file operations are denied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: CORP\jdoe → maps to → jdoe (UNIX) → access granted
After:  CORP\jdoe → maps to → " " (empty)  → access DENIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an SVM-wide block — it affects every volume and share within the SVM.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Operational safety note&lt;/strong&gt;: The deny mapping is inserted at position 1 by default, meaning it's evaluated &lt;em&gt;before&lt;/em&gt; any existing name-mappings. If your SVM has existing name-mappings for service accounts or automation, use a higher index (the module uses index 1; customize via &lt;code&gt;ontap_response.py&lt;/code&gt; if needed). Always test with &lt;code&gt;health_check&lt;/code&gt; and a non-production user first. To immediately undo an accidental block: &lt;code&gt;./automated-response-cli.sh unblock-smb --domain CORP --user jdoe&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;ONTAP REST API call:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/api/name-services/name-mappings&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;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"win_unix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CORP&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;jdoe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"replacement"&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;"svm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"svm-prod-01"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NFS: Export-Policy Deny Rule
&lt;/h3&gt;

&lt;p&gt;For NFS clients, blocking is done via export-policy rules with &lt;code&gt;ro_rule: never&lt;/code&gt; and &lt;code&gt;rw_rule: never&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/api/protocols/nfs/export-policies/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;policy_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;/rules&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"clients"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fsxn_auto_response,10.0.5.99"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ro_rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"never"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rw_rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"never"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"superuser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"never"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"protocols"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;999&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;fsxn_auto_response&lt;/code&gt; marker in the client match makes these rules identifiable and easy to clean up. Use a high index (e.g., 999) to avoid conflicts with existing rules.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope note&lt;/strong&gt;: Both SMB and NFS blocks are &lt;strong&gt;SVM-wide&lt;/strong&gt; — they affect ALL volumes and shares within the target SVM, not just the volume specified in the &lt;code&gt;contain_*&lt;/code&gt; action. In multi-tenant SVMs (multiple applications sharing one SVM), this is the intended blast radius for containment. If you need volume-level granularity instead, use &lt;code&gt;create_snapshot&lt;/code&gt; (per-volume) and coordinate with your network team for host-level isolation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  NFS: Why Export-Policy Alone Isn't Instant (and How We Fixed It)
&lt;/h3&gt;

&lt;p&gt;The export-policy rule above takes effect on the server side immediately — but Linux NFS clients cache access decisions for up to 60 seconds (&lt;code&gt;actimeo&lt;/code&gt; default). During that window, the attacker's existing mount can still read/write despite the ONTAP-layer deny.&lt;/p&gt;

&lt;p&gt;Unlike SMB (where &lt;code&gt;disconnect_smb_sessions&lt;/code&gt; forces re-authentication), NFS has no equivalent "kill this client's connection" API. (NFSv4 has lease management internally, but ONTAP does not expose a REST API endpoint for forced lease revocation of a specific client.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution: Network-layer blocking via VPC NACL deny rules.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;FsxSubnetId&lt;/code&gt; is configured, &lt;code&gt;contain_nfs_threat&lt;/code&gt; automatically applies &lt;em&gt;both&lt;/em&gt; 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;Mechanism&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;th&gt;Bypass?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ONTAP&lt;/td&gt;
&lt;td&gt;Export-policy deny rule&lt;/td&gt;
&lt;td&gt;Persistent, survives NACL removal&lt;/td&gt;
&lt;td&gt;Client cache (up to 60s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;NACL deny rule (rule 50-99)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Immediate&lt;/strong&gt; (packet-level)&lt;/td&gt;
&lt;td&gt;Cannot be bypassed by client&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;# Deploy with network-layer NFS blocking enabled:&lt;/span&gt;
aws cloudformation deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; shared/templates/automated-response.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; fsxn-automated-response &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="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;FsxSubnetId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;subnet-where-fsx-enis-reside&amp;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;The NACL rule blocks ALL traffic from the attacker IP at the VPC level — no client-side cache, no retry window. The export-policy rule persists as the long-term block after the NACL is eventually removed during investigation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Blast radius note&lt;/strong&gt;: By default, the NACL rule blocks ALL protocols from the IP (not just NFS). If the attacker IP is also used for monitoring or management, set &lt;code&gt;"block_all_ports": false&lt;/code&gt; in the SNS message to block only NFS port 2049.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why NACL and not Security Group?&lt;/strong&gt; Security Groups are allow-only — you cannot add a deny rule for a specific IP. NACLs support explicit deny and are evaluated before Security Groups, making them the correct mechanism for emergency IP blocking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VPC Endpoint requirement&lt;/strong&gt;: The Lambda calls the EC2 API (&lt;code&gt;CreateNetworkAclEntry&lt;/code&gt;) to manage NACL rules. If your VPC has a NAT Gateway, this works automatically. If not, add an EC2 Interface VPC Endpoint (&lt;code&gt;com.amazonaws.&amp;lt;region&amp;gt;.ec2&lt;/code&gt;) or set &lt;code&gt;CreateVpcEndpoints=true&lt;/code&gt; and add EC2 manually. Without network access to the EC2 API, the NACL block will timeout while the ONTAP export-policy block still succeeds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Session Disconnect
&lt;/h3&gt;

&lt;p&gt;Blocking prevents &lt;em&gt;new&lt;/em&gt; operations, but existing CIFS sessions can continue. The composite action also disconnects active sessions:&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;DELETE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/api/protocols/cifs/sessions/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;svm_uuid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;connection_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Safety Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Input Validation
&lt;/h3&gt;

&lt;p&gt;The module validates all inputs before calling ONTAP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Username injection prevention (blocks &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;`&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, newlines)&lt;/li&gt;
&lt;li&gt;Protected account denylist (&lt;code&gt;fsxadmin&lt;/code&gt;, &lt;code&gt;administrator&lt;/code&gt;, &lt;code&gt;vsadmin&lt;/code&gt;, &lt;code&gt;system&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;IP address format validation (valid IPv4 octets 0-255)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Snapshot Storm Prevention
&lt;/h3&gt;

&lt;p&gt;A configurable cooldown (default: 15 minutes) prevents creating multiple snapshots during a sustained attack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# If a snapshot with our prefix was created &amp;lt;15 min ago → skip
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;svm_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;svm-prod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;volume_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vol_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cooldown_minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Prevents storm
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# result["status"] == "skipped" if cooldown active
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Marker-Based Cleanup
&lt;/h3&gt;

&lt;p&gt;All response rules include a consistent marker (&lt;code&gt;fsxn_auto_response&lt;/code&gt;), making it straightforward to find and remove all automated blocks:&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;# Find all our rules&lt;/span&gt;
export-policy rule show &lt;span class="nt"&gt;-clientmatch&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;fsxn_auto_response&lt;span class="k"&gt;*&lt;/span&gt;
vserver name-mapping show &lt;span class="nt"&gt;-direction&lt;/span&gt; win-unix &lt;span class="nt"&gt;-replacement&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Evidence-handling note&lt;/strong&gt;: The protective snapshot this module creates is evidence, and evidence needs a chain of custody, not just a timestamp. Trigger source, exact UTC time, and post-action API confirmation are already captured in CloudWatch Logs — but pre-action state (what the name-mapping or export-policy looked like &lt;em&gt;before&lt;/em&gt; the block) isn't currently queried and logged, and the SNS trigger message itself isn't hashed. If this snapshot might ever need to hold up in an investigation, not just an ops postmortem, review the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/automated-response-security-addendum.md#chain-of-custody-requirements-dfir" rel="noopener noreferrer"&gt;chain-of-custody gap table&lt;/a&gt; in the Security Addendum before treating "snapshot created" as "evidence preserved."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Time-Limited Blocks
&lt;/h3&gt;

&lt;p&gt;Persistent blocks are a real operational risk — a false positive can lock out a legitimate user indefinitely if nobody notices. The companion &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/shared/templates/automated-response-ttl.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;automated-response-ttl.yaml&lt;/code&gt;&lt;/a&gt; stack deploys an EventBridge Scheduler that periodically checks for and auto-removes blocks older than a configurable TTL, so a missed alert doesn't turn into a standing lockout. Deploy it alongside the main stack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  One Stack Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; shared/templates/automated-response.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; fsxn-automated-response &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;OntapMgmtIp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;management-ip&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;OntapCredentialsSecretArn&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="nv"&gt;VpcId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;vpc-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;SubnetIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;subnet-1&amp;gt;,&amp;lt;subnet-2&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;SecurityGroupId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;sg-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;DefaultSvmName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;svm-prod-01 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;SharedPythonLayerArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;layer-arn&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;NotificationEmail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;security-team@example.com &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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;New parameter (from verification)&lt;/strong&gt;: &lt;code&gt;SharedPythonLayerArn&lt;/code&gt; — provide the ARN of the &lt;code&gt;fsxn-shared-python&lt;/code&gt; Lambda Layer containing &lt;code&gt;ontap_response.py&lt;/code&gt;. Without this, the handler fails with &lt;code&gt;ModuleNotFoundError&lt;/code&gt;. Build and publish the layer first:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;bash shared/python/build-layer.sh
aws lambda publish-layer-version &lt;span class="nt"&gt;--layer-name&lt;/span&gt; fsxn-shared-python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zip-file&lt;/span&gt; fileb://shared/python/dist/fsxn-shared-python-layer.zip &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--compatible-runtimes&lt;/span&gt; python3.12 &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'LayerVersionArn'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;The stack creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SNS Trigger Topic (entry point)&lt;/li&gt;
&lt;li&gt;SNS Notification Topic (results)&lt;/li&gt;
&lt;li&gt;Lambda function (VPC, Python 3.12)&lt;/li&gt;
&lt;li&gt;DLQ with encryption&lt;/li&gt;
&lt;li&gt;CloudWatch Log Group (365-day retention)&lt;/li&gt;
&lt;li&gt;DLQ depth alarm&lt;/li&gt;
&lt;li&gt;VPC Endpoints for Secrets Manager + SNS (if &lt;code&gt;CreateVpcEndpoints=true&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DLQ alarm = urgent&lt;/strong&gt;: A message in the DLQ means a containment action &lt;em&gt;failed&lt;/em&gt;. Unlike most DLQs where retry-later is fine, here it means &lt;strong&gt;the attacker may still have access&lt;/strong&gt;. Treat DLQ depth &amp;gt; 0 as a P1 incident requiring immediate human investigation. See the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/runbooks/dlq-replay.md" rel="noopener noreferrer"&gt;DLQ Replay Runbook&lt;/a&gt; for recovery steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical: VPC Endpoints Required.&lt;/strong&gt; Lambda in a VPC cannot reach AWS APIs (Secrets Manager, SNS) without Interface VPC Endpoints or a NAT Gateway. The stack creates these by default (&lt;code&gt;CreateVpcEndpoints=true&lt;/code&gt;). If your VPC already has them, set it to &lt;code&gt;false&lt;/code&gt;. Without this, Lambda will timeout on every invocation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost note&lt;/strong&gt;: If your VPC already has a NAT Gateway, set &lt;code&gt;CreateVpcEndpoints=false&lt;/code&gt; — the Lambda can reach AWS APIs through NAT without the additional ~$14/month VPC Endpoint cost. The $0.51/month figure is infrastructure cost only; operational cost (tuning detection rules, investigating false positives, running quarterly drills) is the larger ongoing investment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The CloudFormation template embeds the Lambda code inline for a quick first deployment. For a GitOps-friendly setup, package &lt;code&gt;ontap_response.py&lt;/code&gt; as a Lambda Layer instead using &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/shared/python/build-layer.sh" rel="noopener noreferrer"&gt;&lt;code&gt;build-layer.sh&lt;/code&gt;&lt;/a&gt; and version it independently of the stack.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Comprehensive deployment guidance&lt;/strong&gt;: For VPC Endpoint conflict avoidance, parameter file templates, and pre-deployment environment validation, see the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/deployment-guide.md" rel="noopener noreferrer"&gt;Deployment Guide&lt;/a&gt;. It includes a pre-flight check script that detects existing VPC Endpoints before you deploy — the single most common source of deployment rollbacks in this project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Connect Detection Sources
&lt;/h3&gt;

&lt;p&gt;Subscribe the trigger topic to your detection:&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;How to Connect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Log Alarm&lt;/td&gt;
&lt;td&gt;Set alarm action to trigger topic ARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datadog Monitor&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;@sns-&amp;lt;topic&amp;gt;&lt;/code&gt; in notification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elastic SIEM&lt;/td&gt;
&lt;td&gt;Use SNS action in Kibana alert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws sns publish --topic-arn &amp;lt;arn&amp;gt; --message '&amp;lt;json&amp;gt;'&lt;/code&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;Alert-tuning note&lt;/strong&gt;: Whatever you wire to the trigger topic will fire on every match, and every fire generates a notification. A detection rule tuned for "catch everything" rather than "catch what matters" turns this pipeline into another source of alert fatigue — the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/automated-response-guide.md" rel="noopener noreferrer"&gt;Reliability note in the Automated Response Guide&lt;/a&gt; already flags the self-inflicted-DoS risk of an over-triggering rule; the analyst-facing version of that same risk is that a noisy rule buries the one block that actually mattered in a pile of routine ones. Tune the upstream detection rule's specificity before wiring it here, not after the first pager rotation complains.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  CLI Helper
&lt;/h2&gt;

&lt;p&gt;For manual operations and testing, use the included CLI wrapper:&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;export &lt;/span&gt;&lt;span class="nv"&gt;RESPONSE_TOPIC_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:sns:ap-northeast-1:123456789012:fsxn-automated-response-trigger"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DEFAULT_SVM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"svm-prod-01"&lt;/span&gt;

&lt;span class="c"&gt;# Full containment (snapshot + block + disconnect)&lt;/span&gt;
./shared/scripts/automated-response-cli.sh contain-smb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--domain&lt;/span&gt; CORP &lt;span class="nt"&gt;--user&lt;/span&gt; jdoe &lt;span class="nt"&gt;--volume&lt;/span&gt; vol_data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--reason&lt;/span&gt; &lt;span class="s2"&gt;"ARP detection - arw.volume.state alert"&lt;/span&gt;

&lt;span class="c"&gt;# Block a single IP&lt;/span&gt;
./shared/scripts/automated-response-cli.sh block-nfs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ip&lt;/span&gt; 10.0.5.99 &lt;span class="nt"&gt;--reason&lt;/span&gt; &lt;span class="s2"&gt;"Mass deletion from FPolicy"&lt;/span&gt;

&lt;span class="c"&gt;# Unblock after investigation&lt;/span&gt;
./shared/scripts/automated-response-cli.sh unblock-smb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--domain&lt;/span&gt; CORP &lt;span class="nt"&gt;--user&lt;/span&gt; jdoe

&lt;span class="c"&gt;# Dry-run test (no publish)&lt;/span&gt;
./shared/scripts/automated-response-cli.sh &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the same compromised identity or IP has access across multiple SVMs (e.g., a domain user with shares on both production and DR), use &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/shared/scripts/automated-response-multi-svm-cli.sh" rel="noopener noreferrer"&gt;&lt;code&gt;automated-response-multi-svm-cli.sh&lt;/code&gt;&lt;/a&gt; instead — it wraps the single-SVM CLI, fans out the same action across a comma-separated SVM list, and reports per-SVM success/failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./shared/scripts/automated-response-multi-svm-cli.sh contain-smb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--svms&lt;/span&gt; &lt;span class="s2"&gt;"svm-prod-01,svm-prod-02,svm-dr-01"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--domain&lt;/span&gt; CORP &lt;span class="nt"&gt;--user&lt;/span&gt; jdoe &lt;span class="nt"&gt;--volume&lt;/span&gt; vol_data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--reason&lt;/span&gt; &lt;span class="s2"&gt;"ARP detection - multi-SVM block"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Composite Actions
&lt;/h2&gt;

&lt;p&gt;Individual actions (&lt;code&gt;block_smb_user&lt;/code&gt;, &lt;code&gt;block_nfs_ip&lt;/code&gt;, &lt;code&gt;create_snapshot&lt;/code&gt;, disconnect) can be combined into a single message:&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;Steps&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contain_smb_threat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Snapshot → Block SMB user → Disconnect sessions&lt;/td&gt;
&lt;td&gt;Compromised AD user detected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contain_nfs_threat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Snapshot → Block NFS IP&lt;/td&gt;
&lt;td&gt;Suspicious NFS client activity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contain_multiprotocol_threat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Snapshot → Block SMB user → Block NFS IP → Disconnect sessions&lt;/td&gt;
&lt;td&gt;Volume accessed via both SMB and NFS — a single-protocol block lets the attacker switch protocols and continue&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;contain_*&lt;/code&gt; naming maps to the Containment phase in NIST SP 800-61's incident handling model. This module only covers storage-layer access blocking and evidence preservation, though. Isolating the compromised host, removing malware, rotating credentials, and blocking lateral movement to other systems (eradication/recovery) remain out of scope and still require a human (or another IR tool) to handle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison with DII Storage Workload Security
&lt;/h2&gt;

&lt;p&gt;Both approaches use the same underlying ONTAP mechanisms. The difference is where detection intelligence lives:&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;DII Storage Workload Security&lt;/th&gt;
&lt;th&gt;This Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blocking mechanism&lt;/td&gt;
&lt;td&gt;ONTAP REST API&lt;/td&gt;
&lt;td&gt;ONTAP REST API (identical)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detection&lt;/td&gt;
&lt;td&gt;Built-in ML (per-user baselines)&lt;/td&gt;
&lt;td&gt;Your choice of SIEM/observability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context scope&lt;/td&gt;
&lt;td&gt;Storage only&lt;/td&gt;
&lt;td&gt;Storage + network + application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data residency&lt;/td&gt;
&lt;td&gt;SaaS (vendor cloud)&lt;/td&gt;
&lt;td&gt;Your AWS VPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration&lt;/td&gt;
&lt;td&gt;Limited export&lt;/td&gt;
&lt;td&gt;Native (detection originates from your tools)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost model&lt;/td&gt;
&lt;td&gt;Capacity-based license&lt;/td&gt;
&lt;td&gt;~$0.51/month (pay-per-use)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Days (agent + collector + AD config)&lt;/td&gt;
&lt;td&gt;30 minutes (CloudFormation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither approach is "better" — they suit different contexts. If you already have a SIEM with anomaly detection (Datadog ML, Elastic ML Jobs, Splunk MLTK), or if your alert originates outside DII, this approach extends your existing investment to cover storage-layer access blocking. If you already run DII and your detection lives there too, its built-in ML and turnkey containment remain a solid choice — see the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/automated-response-guide.md#faq" rel="noopener noreferrer"&gt;Automated Response Guide&lt;/a&gt; for the fuller writeup.&lt;/p&gt;

&lt;p&gt;The table above compares &lt;em&gt;mechanisms&lt;/em&gt;. The next section places both mechanisms inside a wider risk-management frame, because "which tool blocks the user" and "is your organization's ransomware posture actually sound" are different questions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Fits in Cyber Resilience: NIST CSF 2.0
&lt;/h2&gt;

&lt;p&gt;It's tempting to read a 65-second block-and-snapshot flow as "ransomware handled." It isn't — it's one function, in one framework, among six. &lt;a href="https://www.nist.gov/cyberframework" rel="noopener noreferrer"&gt;NIST CSF 2.0&lt;/a&gt; organizes an organization's &lt;em&gt;entire&lt;/em&gt; cybersecurity risk-management program into &lt;strong&gt;Govern, Identify, Protect, Detect, Respond, Recover&lt;/strong&gt;, and NIST's dedicated ransomware profile (&lt;a href="https://csrc.nist.gov/pubs/ir/8374/r1/final" rel="noopener noreferrer"&gt;NIST IR 8374r1&lt;/a&gt;) maps ransomware-specific outcomes onto those six functions. Both AWS (&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/ransomware-risk-management-on-aws-using-nist-csf/technical-capabilities.html" rel="noopener noreferrer"&gt;Ransomware Risk Management on AWS Using the NIST CSF&lt;/a&gt;) and NetApp (&lt;a href="https://www.netapp.com/it/blog/fortify-cybersecurity-nist-framework/" rel="noopener noreferrer"&gt;Fortify your cybersecurity defenses with NIST framework&lt;/a&gt;) publish their own mappings for the same reason this section exists: to be explicit about which function a given tool covers, and which functions remain the organization's job regardless of tooling.&lt;/p&gt;

&lt;p&gt;Placed on that map, this module and DII Storage Workload Security occupy the same narrow slice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CSF 2.0 Function&lt;/th&gt;
&lt;th&gt;What It Covers&lt;/th&gt;
&lt;th&gt;Where This Module (or DII) Sits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Govern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Risk strategy, roles, policy, board oversight&lt;/td&gt;
&lt;td&gt;Out of scope for both — this is an organizational responsibility no storage-layer tool automates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identify&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Asset/data inventory, classification&lt;/td&gt;
&lt;td&gt;Out of scope for &lt;em&gt;this&lt;/em&gt; module, but a companion module in the same repo — a &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/content-classification-scanner.md" rel="noopener noreferrer"&gt;Content-Level PII Classification Scanner&lt;/a&gt; using Amazon Comprehend — covers text/structured-data content discovery. Office/PDF content extraction is not implemented; DII's parent product line offers broader data classification separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Safeguards that reduce likelihood/impact&lt;/td&gt;
&lt;td&gt;Partially covered — ONTAP Snapshot/SnapLock, export-policy, name-mapping are the underlying safeguards both approaches call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Detect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Finding anomalies via monitoring&lt;/td&gt;
&lt;td&gt;Not this module's job — it &lt;em&gt;responds&lt;/em&gt; to a detection, it doesn't produce one (see &lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Part 3&lt;/a&gt; and &lt;a href="https://dev.to/aws-builders/alerting-on-fsx-for-ontap-audit-logs-no-metric-filter-required-with-cloudwatch-log-alarms-15eg"&gt;Part 17&lt;/a&gt; for the Detect side)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Respond&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mitigation, analysis, reporting on a detected incident&lt;/td&gt;
&lt;td&gt;✅ This is what this module does — block, snapshot, disconnect, notify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recover&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Restoring systems and coordinating with stakeholders&lt;/td&gt;
&lt;td&gt;The snapshot this module creates is Respond-phase evidence, not a verified recovery point on its own. A companion &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/verified-recovery-point-guide.md" rel="noopener noreferrer"&gt;Verified-Clean Recovery Point Guide&lt;/a&gt; closes part of that gap — see below — though the restore itself and stakeholder-level coordination remain manual&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Governance-reporting note&lt;/strong&gt;: If you're reporting this capability up to a risk committee or board, resist framing it as "ransomware is covered." It's more accurate — and more useful for risk-register purposes — to say "we have automated Respond-phase containment; Govern-phase maturity, and the ML side of Detect, are tracked separately." CloudFormation-as-code and CloudWatch Logs give you the audit evidence (who deployed what, when a block fired and why) that a Govern program consumes as input, but they aren't a substitute for having the program itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy note&lt;/strong&gt;: The Content-Level PII Classification Scanner referenced in the Identify row above is worth a second look if your org is tracking data-minimization obligations. Its findings deliberately record only entity &lt;em&gt;type&lt;/em&gt;, count, and confidence per file (&lt;code&gt;EMAIL&lt;/code&gt;, &lt;code&gt;SSN&lt;/code&gt;, &lt;code&gt;CREDIT_DEBIT_NUMBER&lt;/code&gt;, and so on via Amazon Comprehend) — never the matched text itself — so a "this file has 3 SSN-pattern matches" report doesn't itself become a new PII exposure. That's useful for prioritizing where to apply DLP controls, but it isn't a substitute for a DPO's formal data classification review, and it currently can't see inside Office documents or PDFs (no text extraction yet) — plan around that blind spot if your regulated data lives mostly in those formats.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A point worth being precise about on the Recover side, since it's easy to overstate: a snapshot existing is a &lt;strong&gt;Protect&lt;/strong&gt;-phase artifact, not proof that recovery works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Recovery-sufficiency note&lt;/strong&gt;: "We took a snapshot" and "we have a verified-clean recovery point" are two different maturity levels. &lt;a href="https://elastio.com/blog/mapping-ransomware-recovery-to-nist-csf-20" rel="noopener noreferrer"&gt;Elastio's mapping of ransomware recovery to CSF 2.0&lt;/a&gt; makes the same point industry-wide: RC.RP (Incident Recovery Plan Execution) is only credible once someone has actually restored from the recovery point and confirmed it's free of compromise — not merely confirmed the snapshot job succeeded. This module alone only delivers the first maturity level (the snapshot). A companion &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/verified-recovery-point-guide.md" rel="noopener noreferrer"&gt;Verified-Clean Recovery Point Guide&lt;/a&gt; in this repo now automates a version of the second: it clones the candidate snapshot (FlexClone), scans the clone through an isolated S3 Access Point for ransomware-associated file extensions, and records a pass/fail verdict — without ever touching the production volume.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What "Verified Recovery" Looks Like in Practice
&lt;/h3&gt;

&lt;p&gt;After the containment above creates its protective snapshot, you can verify it before restoring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Candidate Snapshot
     │
     ▼ FlexClone (copy-on-write, instant, never modifies source)
     │
     ▼ Isolated S3 Access Point (VPC-scoped)
     │
     ▼ Scan for ransomware-associated extensions (.encrypted, .locked, etc.)
     │
     ▼ Verdict: clean / suspicious / error
     │
     ▼ Auto-cleanup (clone + access point deleted)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a single Step Functions workflow (5 Lambda functions). Deploy it and run a verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy the verification workflow&lt;/span&gt;
aws cloudformation deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--template-file&lt;/span&gt; shared/templates/restore-verification.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stack-name&lt;/span&gt; fsxn-restore-verification &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;VpcId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;vpc-id&amp;gt; &lt;span class="nv"&gt;SubnetIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;subnet-1&amp;gt;,&amp;lt;subnet-2&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;SecurityGroupId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;sg-id&amp;gt; &lt;span class="nv"&gt;OntapMgmtIp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;mgmt-ip&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;OntapCredentialsSecretArn&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="nt"&gt;--capabilities&lt;/span&gt; CAPABILITY_NAMED_IAM

&lt;span class="c"&gt;# Verify a specific snapshot before restoring&lt;/span&gt;
aws stepfunctions start-execution &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--state-machine-arn&lt;/span&gt; &amp;lt;from stack outputs&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--input&lt;/span&gt; &lt;span class="s1"&gt;'{"svm_name":"svm-prod","volume_name":"vol_data","snapshot_name":"incident_response_20260712_005307"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow completes in 2-5 minutes (depending on clone creation and access point attachment time). The verdict appears in the execution output and is logged to CloudWatch for audit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What it doesn't do&lt;/strong&gt;: This is a fast extension-pattern pre-filter — it flags a volume dominated by renamed/encrypted files. It is not a deep content inspection, and it does not exercise an actual end-to-end restore. Treat a "clean" verdict as a necessary first gate before committing to a restore, and still run periodic full restore drills into an isolated environment separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restore-testing note&lt;/strong&gt;: Be precise about what that verdict actually certifies, because it's easy to over-claim from the runbook name alone. The scan is a fast extension-pattern pre-filter — it flags a volume dominated by &lt;code&gt;.encrypted&lt;/code&gt;/&lt;code&gt;.locked&lt;/code&gt;-style renamed files — not a deep content inspection, and it does not exercise an actual restore end-to-end. Treat a "clean" verdict as a necessary first gate before a human commits to a restore, and still run periodic full restore-and-verify drills into an isolated environment separately. Don't let an automated pre-filter quietly become your only recovery test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This module's protective snapshot, in other words, is Respond-phase evidence preservation, and the verification workflow above is a fast pre-filter toward Recover-phase confidence — neither is a substitute for a tested, human-verified restore. A fuller function-by-function breakdown — including where this repository's &lt;em&gt;other&lt;/em&gt; components (ARP, EMS detection, Forensics dashboards, the recovery verification and PII scanning modules) land on Identify/Protect/Detect/Recover — lives in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cyber-resilience-capability-map.md" rel="noopener noreferrer"&gt;Cyber Resilience Capability Map&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  End-to-End Flow: ARP Detection → Auto-Block
&lt;/h2&gt;

&lt;p&gt;Here's the full flow from ransomware detection to an automated storage-layer block. &lt;strong&gt;This flow has been verified end-to-end&lt;/strong&gt; on ONTAP 9.17.1P7D1 (FSx for ONTAP, ap-northeast-1). Key timings from actual execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ARP/AI detection: triggered within minutes of ransomware-like file operations (no learning period — ARP/AI in ONTAP 9.16.1+ is immediately active)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;contain_smb_threat&lt;/code&gt; Lambda execution: &lt;strong&gt;1.8 seconds&lt;/strong&gt; (snapshot + block + session disconnect in a single invocation)&lt;/li&gt;
&lt;li&gt;SMB access denial confirmed after re-authentication
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. User "jdoe" starts encrypting files on vol_data
   (zip with password → delete original → new extension)
         │
2. ONTAP ARP/AI detects anomaly (entropy + extension changes)
   No learning period required (ONTAP 9.16.1+ / ARP/AI)
         │  Detection within minutes
         ▼
3. EMS event: callhome.arw.activity.seen (severity: alert)
   ONTAP auto-creates Anti_ransomware_attack_backup snapshot
         │
4. EMS Webhook → API Gateway → Lambda → Observability Platform
         │  ~30 seconds
         ▼
5. Observability Monitor fires (ARP alert detected)
         │
6. Monitor → SNS publish (contain_smb_threat)
         │  ~5 seconds
         ▼
7. Response Lambda executes (1.8s measured):
   a) Creates incident_response_20260712_005307 snapshot
   b) Blocks CORP\jdoe via name-mapping (deny on re-authentication)
   c) Attempts CIFS session disconnect
   d) Sends notification to security team
         │
8. Total time: detection + routing (~35s) + response (1.8s) ≈ under 2 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SOC correlation tip&lt;/strong&gt;: Always include &lt;code&gt;incident_id&lt;/code&gt; and &lt;code&gt;detection_source&lt;/code&gt; in your SNS message so that the storage-layer block can be correlated with the upstream detection in your SIEM timeline:&lt;/p&gt;


&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"contain_smb_threat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"svm_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"svm-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"CORP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"jdoe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"volume_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"vol_data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ARP alert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"incident_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"INC-2026-0712-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"detection_source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"datadog-monitor-arp"&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;p&gt;&lt;strong&gt;How immediate blocking actually works&lt;/strong&gt;: The &lt;code&gt;contain_smb_threat&lt;/code&gt; composite action achieves &lt;strong&gt;effective immediate cutoff&lt;/strong&gt; through a two-step mechanism:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;name-mapping deny&lt;/strong&gt; — blocks all &lt;em&gt;future&lt;/em&gt; authentication attempts (evaluated at session setup time, not per-I/O)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;disconnect_smb_sessions&lt;/code&gt;&lt;/strong&gt; — forcefully terminates the user's &lt;em&gt;existing&lt;/em&gt; active sessions via &lt;code&gt;DELETE /protocols/cifs/sessions/{svm}/{id}/{conn}&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the existing session is terminated, the Windows SMB client automatically attempts to re-establish the connection — at which point the name-mapping deny takes effect and access is refused. The net result: &lt;strong&gt;the user is cut off within seconds of the Lambda executing&lt;/strong&gt;, not at the next natural session expiry.&lt;/p&gt;

&lt;p&gt;If the disconnect call returns HTTP 404 (session already gone — e.g., the encrypting process finished before the Lambda fired), that's expected and logged but not treated as an error. The name-mapping deny still prevents any future reconnection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ARP/AI verification note (ONTAP 9.16.1+)&lt;/strong&gt;: The CLI command &lt;code&gt;security anti-ransomware volume attack simulate&lt;/code&gt; referenced in some ONTAP documentation does &lt;strong&gt;NOT exist&lt;/strong&gt; in ONTAP 9.17.1. To trigger ARP/AI detection for testing, perform actual ransomware-like file operations: encrypt files with a password, delete originals, and add a new file extension. ARP/AI detects the high-entropy + deletion + never-seen-extension pattern. Detection threshold: 5+ distinct new extensions within 48 hours. See the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/ontap-rest-api-reference.md" rel="noopener noreferrer"&gt;ONTAP REST API Quick Reference&lt;/a&gt; for details.&lt;/p&gt;
&lt;/blockquote&gt;







&lt;h2&gt;
  
  
  What Blocking Looks Like: Visual Evidence
&lt;/h2&gt;

&lt;p&gt;The following was captured during E2E verification on ONTAP 9.17.1P7D1 (July 2026).&lt;/p&gt;

&lt;h3&gt;
  
  
  NFS: Before and After
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; — user can list and read files normally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /mnt/fsxn/
&lt;span class="go"&gt;hr-salary.txt  project-spec.txt  reports/

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /mnt/fsxn/hr-salary.txt
&lt;span class="go"&gt;Confidential HR Record - Employee Salary Data
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt; — export-policy deny rule applied (immediate, same-subnet):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /mnt/fsxn/
&lt;span class="go"&gt;ls: cannot access '/mnt/fsxn/': Permission denied

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /mnt/fsxn/hr-salary.txt
&lt;span class="go"&gt;cat: /mnt/fsxn/hr-salary.txt: Permission denied
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SMB: Before and After (AD-joined SVM)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; — domain user DEMO\testuser can access CIFS share:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X:\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Mode&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;------&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;d-----&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;reports&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;hr-salary.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;43&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;project-spec.txt&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X:\hr-salary.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Confidential&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Employee&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Salary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Data&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;After&lt;/strong&gt; — block_smb_user executed (name-mapping → nobody, volume perms 750):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;X:\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# ACCESS DENIED — drive not accessible after block&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step Functions: Recovery Point Verification Workflow
&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%2Fylt6hf8alz7c8h1132c9.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%2Fylt6hf8alz7c8h1132c9.png" alt="Step Functions Graph View — execution in progress" width="800" height="828"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Step Functions console: All states succeeded (green). Verified-clean recovery point workflow completed end-to-end — waiting for FSx API to discover the ONTAP-created FlexClone. This is the ONTAP→FSx sync delay documented by AWS ("may take up to several minutes").&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Detection &amp;amp; Forensics: Datadog Dashboard
&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%2F63j7mjuqcmlig8vph1yl.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%2F63j7mjuqcmlig8vph1yl.png" alt="Datadog Forensics Dashboard" width="800" height="679"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Datadog Forensics Dashboard: Incident timeline (Detection → Response → Verification), user activity tracking, affected volumes, automated response actions log, ARP alert feed, and recovery verification results — all from a single pane.&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%2Ffgzniwak3r55kpel3hot.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%2Ffgzniwak3r55kpel3hot.png" alt="Datadog Log Explorer — Live ARP Detection" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Datadog Log Explorer: Real-time ARP detection events (&lt;code&gt;callhome.arw.activity.seen&lt;/code&gt;, &lt;code&gt;callhome.arw.volume.state&lt;/code&gt;) with structured attributes — entropy score, affected file count, new extensions detected.&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%2Fb0946ot0sdf2l0p2gmvj.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%2Fb0946ot0sdf2l0p2gmvj.png" alt="Datadog Audit Trail — User + File Path" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;File Access Audit Trail: Who (CORP\jdoe) accessed what (/vol_data/finance/budget-2026.docx) with what operation (WriteData|Delete) — the same forensic visibility DII Storage Workload Security provides.&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%2Fpzlkgb1zumpusunw8q5o.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%2Fpzlkgb1zumpusunw8q5o.png" alt="Datadog Audit Trail — Client IP" width="799" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Scrolled right: Client IP (198.51.100.99) visible alongside the structured JSON content — enabling "who accessed what from where" forensic queries.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Vendor-neutral note&lt;/strong&gt;: The screenshots above use Datadog as a representative example, but this repository delivers the same logs to all 9 supported observability vendors (Datadog, Grafana, Splunk, Elastic, New Relic, Dynatrace, Sumo Logic, Honeycomb, OTel Collector). Deployable forensics dashboard definitions (user timeline, file access audit trail, affected volumes, client IP analysis) are provided for Datadog (JSON), Grafana (JSON), Splunk (SPL), and Elastic (KQL Saved Searches). See the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/native-alternative-matrix.md#forensics-dashboard--per-vendor-reference" rel="noopener noreferrer"&gt;per-vendor reference&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;The module has 52 unit tests covering:&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;Tests&lt;/th&gt;
&lt;th&gt;What's Verified&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SMB blocking&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Success, SVM not found, API error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB unblocking&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Success, not found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS blocking&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Success, policy not found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFS unblocking&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Success, not found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshots&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Success, cooldown active/expired/disabled, volume not found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session disconnect&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;By user, no sessions, missing params&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composite actions&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Full sequence, partial failure, NFS containment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input validation&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Protected accounts, injection prevention, IP validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configurable protected accounts&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PROTECTED_ACCOUNTS_EXTRA&lt;/code&gt; env var&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List blocks&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;With results, empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health check&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Success, SVM not found, unreachable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error handling&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Error attributes, timeout, malformed response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NACL blocking&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Block IP, unblock, list active, find NACL, rule number allocation, marker validation&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;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pytest shared/python/tests/test_ontap_response.py shared/python/tests/test_network_block.py &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# 52 passed in 0.06s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Your Deployment
&lt;/h3&gt;

&lt;p&gt;After deploying to your environment, run these checks within the first 30 days:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Day 1&lt;/strong&gt;: &lt;code&gt;health_check&lt;/code&gt; action succeeds (ONTAP reachable, credentials valid)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 1&lt;/strong&gt;: Test &lt;code&gt;create_snapshot&lt;/code&gt; on a non-production volume (confirm snapshot appears)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 1&lt;/strong&gt;: Run a full &lt;code&gt;contain_smb_threat&lt;/code&gt; drill against a test user on a test SVM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 1&lt;/strong&gt;: Verify TTL cleanup removes the test block after configured minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day 30&lt;/strong&gt;: Confirm DLQ is empty (no silent failures), CloudWatch dashboard shows expected invocation count&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any of these fail, check the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/prerequisites.md#troubleshooting" rel="noopener noreferrer"&gt;Troubleshooting section in Prerequisites&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Posts in This Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/why-your-fsx-for-ontap-audit-logs-deserve-better-than-ec2-kod"&gt;Part 1: Why Your FSx for ONTAP Audit Logs Deserve Better Than EC2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Part 3: Event-Driven Ransomware Detection with ONTAP ARP + Datadog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/alerting-on-fsx-for-ontap-audit-logs-no-metric-filter-required-with-cloudwatch-log-alarms-15eg"&gt;Part 17: CloudWatch Log Alarms — No Metric Filter Required&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  EMS Webhook Integration: Payload Format
&lt;/h2&gt;

&lt;p&gt;When connecting ARP EMS events to your detection pipeline, note the &lt;strong&gt;actual&lt;/strong&gt; ONTAP EMS webhook payload format (verified on ONTAP 9.17.1P7D1):&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;"message-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"callhome.arw.activity.seen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message-severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message-timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-12T00:42:06+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"vserver-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"svm-prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"volume-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vol_data"&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution&lt;/strong&gt;: Field names use &lt;strong&gt;hyphens&lt;/strong&gt; (&lt;code&gt;message-name&lt;/code&gt;), not camelCase (&lt;code&gt;messageName&lt;/code&gt;) or snake_case (&lt;code&gt;message_name&lt;/code&gt;). This matters for SIEM parsing rules and Lambda handler field validation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The EMS event for ARP attack detection is &lt;code&gt;callhome.arw.activity.seen&lt;/code&gt; (severity: alert), not &lt;code&gt;arw.volume.state&lt;/code&gt; (which is a state-change notification for ARP enable/disable). The &lt;code&gt;callhome.*&lt;/code&gt; prefix indicates an ONTAP AutoSupport-class event (internal telemetry classification, not external data transmission). Configure your detection rules to match the correct event name.&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/blob/main/shared/python/ontap_response.py" rel="noopener noreferrer"&gt;GitHub: ontap_response.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/shared/templates/automated-response.yaml" rel="noopener noreferrer"&gt;GitHub: automated-response.yaml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/automated-response-guide.md" rel="noopener noreferrer"&gt;GitHub: Automated Response Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/ontap-rest-api-reference.md" rel="noopener noreferrer"&gt;GitHub: ONTAP REST API Quick Reference&lt;/a&gt; — Common pitfalls (svm.uuid duplicate, allow_privileged_access, ARP/AI behaviors), verified on ONTAP 9.17.1P7D1&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/ems-detection-capabilities.md" rel="noopener noreferrer"&gt;GitHub: EMS Detection Capabilities&lt;/a&gt; — 30+ events, Push vs Pull delivery, latency comparison&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cyber-resilience-capability-map.md" rel="noopener noreferrer"&gt;GitHub: Cyber Resilience Capability Map&lt;/a&gt; — full NIST CSF 2.0 function mapping with FSx for ONTAP implementation and alternative implementation paths&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/verified-recovery-point-guide.md" rel="noopener noreferrer"&gt;GitHub: Verified-Clean Recovery Point Guide&lt;/a&gt; — FlexClone + isolated scan to verify a snapshot before restoring (CSF 2.0 RC.RP)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/content-classification-scanner.md" rel="noopener noreferrer"&gt;GitHub: Content-Level PII Classification Scanner&lt;/a&gt; — Amazon Comprehend-based PII discovery for file contents (CSF 2.0 Identify)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.netapp.com/us-en/ontap-restapi/" rel="noopener noreferrer"&gt;NetApp: ONTAP REST API Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/fsx/latest/ONTAPGuide/ARP.html" rel="noopener noreferrer"&gt;AWS: FSx for ONTAP ARP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nist.gov/cyberframework" rel="noopener noreferrer"&gt;NIST Cybersecurity Framework (CSF) 2.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://csrc.nist.gov/pubs/ir/8374/r1/final" rel="noopener noreferrer"&gt;NIST IR 8374r1 — Ransomware Risk Management: A CSF 2.0 Community Profile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>observability</category>
      <category>aws</category>
      <category>serverless</category>
      <category>security</category>
    </item>
    <item>
      <title>Alerting on FSx for ONTAP Audit Logs — No Metric Filter Required, with CloudWatch Log Alarms</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Fri, 03 Jul 2026 16:15:43 +0000</pubDate>
      <link>https://dev.to/aws-builders/alerting-on-fsx-for-ontap-audit-logs-no-metric-filter-required-with-cloudwatch-log-alarms-15eg</link>
      <guid>https://dev.to/aws-builders/alerting-on-fsx-for-ontap-audit-logs-no-metric-filter-required-with-cloudwatch-log-alarms-15eg</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Once FSx for ONTAP audit logs land in CloudWatch Logs, a common next question is: &lt;em&gt;"How do I get alerted when someone touches a specific file?"&lt;/em&gt; Until recently you had to build a metric filter first. As of &lt;strong&gt;July 1, 2026&lt;/strong&gt;, &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/07/amazon-cloudwatch-log-alarms/" rel="noopener noreferrer"&gt;CloudWatch Log Alarms&lt;/a&gt; let you create an alarm &lt;strong&gt;directly from a Logs Insights query&lt;/strong&gt; — no metric filter step.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Log Alarm&lt;/strong&gt; (GA 2026-07-01): alarm straight from a log query. No metric filter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it works&lt;/strong&gt;: a Logs Insights query matches events, &lt;code&gt;count(*)&lt;/code&gt; turns them into a number, and the alarm fires when the count crosses a threshold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5 detection presets&lt;/strong&gt;: sensitive-path access / failed access / bulk delete / privileged-user activity / custom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-command deploy&lt;/strong&gt;: &lt;code&gt;bash shared/scripts/deploy-log-alarm.sh&lt;/code&gt; (working in ~15 minutes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: ~$6.6/month (100 MB/day). Slightly higher than the metric-filter approach, but you get log lines in the notification and retroactive queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E verified&lt;/strong&gt;: CloudFormation deploy through state transition, in the Tokyo region.&lt;/li&gt;
&lt;/ul&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;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is Part 17 of 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 builds directly on the admin-audit pipeline from &lt;a href="https://dev.to/aws-builders/can-you-use-system-manager-with-fsx-for-ontap-a-reality-check-with-screenshots"&gt;Part 14&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two AWS Launches That Make This Work
&lt;/h2&gt;

&lt;p&gt;This article sits on top of &lt;strong&gt;two recent CloudWatch launches&lt;/strong&gt;. If you're arriving from outside this series, read these first — together they remove the EC2 syslog server &lt;em&gt;and&lt;/em&gt; the metric-filter step that used to stand between ONTAP logs and an alert.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Managed syslog ingestion (June 2026) — how the logs get in
&lt;/h3&gt;

&lt;p&gt;ONTAP emits its admin audit trail (and EMS events) as &lt;strong&gt;syslog&lt;/strong&gt;. Historically, capturing that meant running an EC2 syslog server. In June 2026, CloudWatch Logs added &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/06/amazon-cloudwatch-syslog-ingestion/" rel="noopener noreferrer"&gt;managed syslog ingestion&lt;/a&gt;: you send syslog (RFC 5424 / RFC 3164 / Cisco FTD/ASA) over a &lt;strong&gt;VPC endpoint&lt;/strong&gt; straight into a log group — no agent, no EC2.&lt;/p&gt;

&lt;p&gt;For FSx for ONTAP, that means pointing ONTAP &lt;code&gt;log-forwarding&lt;/code&gt; at the syslog VPC endpoint and having the admin audit log arrive in CloudWatch Logs directly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's New: &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/06/amazon-cloudwatch-syslog-ingestion/" rel="noopener noreferrer"&gt;Amazon CloudWatch Logs supports managed syslog ingestion&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Setup guide: &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_Syslog_Setup.html" rel="noopener noreferrer"&gt;Setting up syslog ingestion&lt;/a&gt; — VPC endpoint, log group, resource policy, and the three transport options (TCP+TLS 6514 encrypted, TCP 1514 plaintext, UDP 514 best-effort)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the piece the previous article (&lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/syslog-vpce-setup-guide.md" rel="noopener noreferrer"&gt;Part 14 / Syslog VPCE setup&lt;/a&gt;) already wired up. If you followed it, your admin audit log is in CloudWatch Logs and this article's alarm has something to query.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. CloudWatch Log Alarms (July 2026) — how you alert on them
&lt;/h3&gt;

&lt;p&gt;The second launch is the subject of this article: alarms defined by a Logs Insights query rather than a metric.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's New: &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/07/amazon-cloudwatch-log-alarms/" rel="noopener noreferrer"&gt;Amazon CloudWatch supports creating alarms from log queries&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Alarm-On-Logs.html" rel="noopener noreferrer"&gt;Alarming on logs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Put together: &lt;strong&gt;ONTAP → managed syslog ingestion → CloudWatch Logs → Log Alarm → SNS&lt;/strong&gt;. No EC2, no metric filter, no forwarding Lambda.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changed
&lt;/h2&gt;

&lt;p&gt;Alerting on log &lt;em&gt;content&lt;/em&gt; is a basic monitoring need. On CloudWatch it used to take a detour: create a metric filter, generate a custom metric from it, then attach an alarm to that metric — three steps. From "I want to detect this" to an alert actually firing was 15–30 minutes of setup.&lt;/p&gt;

&lt;p&gt;Log Alarms collapse the middle steps. You give a Logs Insights query a threshold, and the alarm reads straight from the logs.&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;Before (metric filter)&lt;/th&gt;
&lt;th&gt;Log Alarm (NEW)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup steps&lt;/td&gt;
&lt;td&gt;3 (filter → metric → alarm)&lt;/td&gt;
&lt;td&gt;1 (Log Alarm only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query flexibility&lt;/td&gt;
&lt;td&gt;Filter pattern syntax only&lt;/td&gt;
&lt;td&gt;Full Logs Insights syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log lines in notification&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (up to 50)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retroactive to existing logs&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IaC&lt;/td&gt;
&lt;td&gt;2 resources&lt;/td&gt;
&lt;td&gt;1 resource (&lt;code&gt;AWS::CloudWatch::LogAlarm&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The part I appreciate most: the notification can include the matched log lines. When the alert lands, you already see &lt;em&gt;who&lt;/em&gt; touched &lt;em&gt;which&lt;/em&gt; file and &lt;em&gt;what&lt;/em&gt; they did — you can form a first read before opening the console.&lt;/p&gt;




&lt;h2&gt;
  
  
  FSx for ONTAP Logs × Log Alarm
&lt;/h2&gt;

&lt;p&gt;There are three FSx for ONTAP log/event families, and it's worth separating them because only some are a natural fit for Log Alarms.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Log type&lt;/th&gt;
&lt;th&gt;What it records&lt;/th&gt;
&lt;th&gt;Delivery path&lt;/th&gt;
&lt;th&gt;Log Alarm target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Admin audit log&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ONTAP management ops (CLI / REST API)&lt;/td&gt;
&lt;td&gt;Managed syslog ingestion → CloudWatch Logs&lt;/td&gt;
&lt;td&gt;✅ this article&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File access audit log&lt;/td&gt;
&lt;td&gt;NAS file/folder ops (NFS / SMB)&lt;/td&gt;
&lt;td&gt;FSx for ONTAP S3 AP → Lambda&lt;/td&gt;
&lt;td&gt;Separate pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMS events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ONTAP system events (capacity / HA / ARP)&lt;/td&gt;
&lt;td&gt;Managed syslog ingestion → CloudWatch Logs, or EMS Webhook → Lambda&lt;/td&gt;
&lt;td&gt;✅ when syslog-delivered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Admin audit log&lt;/strong&gt; is the "who did what as an administrator" record — logins (success/failure), &lt;code&gt;security login&lt;/code&gt; account and role changes, &lt;code&gt;volume create/offline/delete&lt;/code&gt;, &lt;code&gt;vserver&lt;/code&gt; config changes, and privileged operations like &lt;code&gt;system node systemshell&lt;/code&gt; or &lt;code&gt;set -privilege diagnostic&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Two ONTAP settings, don't conflate them&lt;/strong&gt;: &lt;code&gt;security audit&lt;/code&gt; decides &lt;em&gt;what&lt;/em&gt; gets recorded (e.g., &lt;code&gt;security audit modify -cliget on -httpget on -ontapiget on&lt;/code&gt; to capture read/GET operations — off by default, so without it your &lt;code&gt;sensitive-file-access&lt;/code&gt; query on admin ops comes up empty), while &lt;code&gt;cluster log-forwarding&lt;/code&gt; decides &lt;em&gt;where&lt;/em&gt; it's sent. The exact commands are in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/syslog-vpce-setup-guide.md" rel="noopener noreferrer"&gt;Syslog VPCE setup guide&lt;/a&gt;. Note &lt;code&gt;cluster log-forwarding&lt;/code&gt; supports &lt;strong&gt;multiple destinations&lt;/strong&gt;, so you can add CloudWatch alongside an existing on-prem SIEM without cutting over.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;File access audit log&lt;/strong&gt; is the "which user did what to which file" record, enabled via &lt;code&gt;vserver audit create&lt;/code&gt; and emitted in Windows Security Event format (EVTX / XML). It's the right source for sensitive-folder access and mass-delete detection — but to use it with a Log Alarm you'd need a separate pipeline to land EVTX/XML into CloudWatch Logs. In this project, that content flows via the FSx for ONTAP S3 Access Point → Lambda path to each vendor instead.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Critical scoping (read before you trust a preset name)&lt;/strong&gt;: the presets in this article run against the &lt;strong&gt;admin audit log&lt;/strong&gt; (&lt;code&gt;/syslog/fsxn-admin-audit&lt;/code&gt;). That log contains &lt;strong&gt;management-plane&lt;/strong&gt; operations — it does &lt;strong&gt;not&lt;/strong&gt; see end-user file I/O over NFS/SMB. So on this log group:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;bulk-delete-operations&lt;/code&gt; detects &lt;strong&gt;admin-plane&lt;/strong&gt; destructive ops (Snapshot delete, &lt;code&gt;volume offline/delete&lt;/code&gt;) — &lt;strong&gt;not&lt;/strong&gt; ransomware encrypting user files over SMB.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sensitive-file-access&lt;/code&gt; matches a path only when an &lt;strong&gt;admin command&lt;/strong&gt; references it — &lt;strong&gt;not&lt;/strong&gt; when a user opens that file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;strong&gt;user-file&lt;/strong&gt; ransomware/mass-delete/sensitive-access detection, use ONTAP &lt;strong&gt;ARP&lt;/strong&gt; (&lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Part 3&lt;/a&gt;) and &lt;strong&gt;FPolicy / file-access audit&lt;/strong&gt; (&lt;a href="https://dev.to/aws-builders/fpolicy-file-activity-pipeline-ontap-to-datadog-via-ecs-fargate-2ing"&gt;Part 4&lt;/a&gt;). The same preset works against a file-access-audit log group &lt;em&gt;if&lt;/em&gt; you land that data in CloudWatch Logs — but on &lt;code&gt;/syslog/fsxn-admin-audit&lt;/code&gt; it only sees the admin plane.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  EMS events (system events)
&lt;/h3&gt;

&lt;p&gt;The third source is &lt;strong&gt;EMS (Event Management System)&lt;/strong&gt; — ONTAP's internal system-event notifications. Where audit logs say "who did it", EMS says "what happened to the system", across seven severities (emergency → debug). Representative events this project normalizes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;arw.volume.state&lt;/code&gt; / &lt;code&gt;arw.vserver.state&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;ARP (Autonomous Ransomware Protection) state transition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;monitor.volume.full&lt;/code&gt; / &lt;code&gt;wafl.vol.full&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;Volume space exhaustion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wafl.quota.hardlimit.exceeded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;error&lt;/td&gt;
&lt;td&gt;Quota hard-limit exceeded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cf.fsm.takeoverStarted&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;HA takeover started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;net.linkDown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;Network link down&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;EMS has two routes. This project's EMS Webhook path (HTTPS → API Gateway → Lambda) normalizes and ships events to any vendor/OTLP. Alternatively, ONTAP 9.x can forward EMS over syslog (&lt;code&gt;event notification destination create -syslog ...&lt;/code&gt;) to the &lt;strong&gt;same&lt;/strong&gt; CloudWatch Logs syslog VPC endpoint you built for the admin audit log — no separate EC2 syslog server — and then EMS becomes a &lt;strong&gt;Log Alarm target&lt;/strong&gt; directly. If your need is "alert immediately on volume-full or an ARP state change", syslog-to-CloudWatch + a Log Alarm is the shortest path.&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;FSx for ONTAP (ONTAP log-forwarding)
    │ Syslog TCP (TLS 6514)
    ▼
CloudWatch Logs managed syslog ingestion (VPC endpoint)
    │  → /syslog/fsxn-admin-audit
    │ Scheduled Query (5 min)
    ▼
CloudWatch Log Alarm
    │ count(*) &amp;gt; threshold → ALARM
    ▼
SNS → Email / Slack / PagerDuty (with log lines)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The actual log format
&lt;/h3&gt;

&lt;p&gt;Writing a Log Alarm query is easier if you know what the line looks like. A real admin-audit line in CloudWatch Logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;190&amp;gt;Jul  2 03:17:37 FsxId...-02: [kern_audit:info:6392]
...:: FsxId...:ssh :: &amp;lt;source-ip&amp;gt;:unknown ::
FsxId...:fsx-control-plane:admin ::
system node systemshell -node * -command "top -d 1 -s 1"
:: Success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads like an incantation, but everything is there: when, which protocol (ssh/http), from where (source IP), who (user), what (command), and the outcome (Success/Failure). A query usually just needs one of those.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why "Alert on a String" Actually Means "Count Then Compare"
&lt;/h2&gt;

&lt;p&gt;Worth recalling: a CloudWatch alarm compares a &lt;strong&gt;number&lt;/strong&gt; against a threshold. It is not natively a "fire when this string appears" trigger.&lt;/p&gt;

&lt;p&gt;So how does a Log Alarm alert on content? It turns the string into a number first. A Logs Insights query narrows to matching events, &lt;code&gt;count(*)&lt;/code&gt; converts them to a count, and the alarm fires when that count crosses the threshold. Read it as &lt;strong&gt;string → count → threshold&lt;/strong&gt; and it clicks.&lt;/p&gt;

&lt;p&gt;For example, to detect access to a confidential folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;vol&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;confidential&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the aggregation to &lt;code&gt;count(*)&lt;/code&gt; and the threshold to &lt;code&gt;&amp;gt; 0&lt;/code&gt;, and a single access within the 5-minute window flips the alarm to ALARM. "Tell me the moment anyone touches the confidential folder" — done, with just that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;The repo ships a deploy script, so a few environment variables bring up the whole set of resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy script (recommended)
&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;# Sensitive-file access detection (auto-creates the SNS topic)&lt;/span&gt;
&lt;span class="nv"&gt;DETECTION_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sensitive-file-access &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TARGET_PATTERN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/vol/data/confidential"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;CREATE_SNS_TOPIC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SNS_TOPIC_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fsxn-security-alerts &lt;span class="se"&gt;\&lt;/span&gt;
  bash shared/scripts/deploy-log-alarm.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script creates the SNS topic, deploys the CloudFormation stack, and prints the alarm name and console URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  CloudFormation template
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# shared/templates/cloudwatch-log-alarm.yaml (excerpt)&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;SensitiveFileAccessAlarm&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::CloudWatch::LogAlarm&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;AlarmName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fsxn-sensitive-file-access&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanThreshold&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="na"&gt;QueryResultsToEvaluate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;QueryResultsToAlarm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;TreatMissingData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notBreaching&lt;/span&gt;
      &lt;span class="na"&gt;ScheduledQueryConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;QueryString&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;fields @timestamp, @message&lt;/span&gt;
          &lt;span class="s"&gt;| filter @message like /\/vol\/data\/confidential/&lt;/span&gt;
        &lt;span class="na"&gt;LogGroupIdentifiers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/syslog/fsxn-admin-audit&lt;/span&gt;
        &lt;span class="na"&gt;ScheduledQueryRoleARN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;ScheduledQueryRole.Arn&lt;/span&gt;
        &lt;span class="na"&gt;AggregationExpression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count(*)"&lt;/span&gt;
        &lt;span class="na"&gt;ScheduleConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ScheduleExpression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate(5&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;minutes)"&lt;/span&gt;
          &lt;span class="na"&gt;StartTimeOffset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
      &lt;span class="na"&gt;AlarmActions&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="s"&gt;AlarmSNSTopic&lt;/span&gt;
      &lt;span class="na"&gt;ActionLogLineCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;ActionLogLineRoleArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;LogLineRole.Arn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On &lt;code&gt;QueryResultsToEvaluate&lt;/code&gt; / &lt;code&gt;QueryResultsToAlarm&lt;/code&gt; (M-of-N)&lt;/strong&gt;: this is the flapping control — &lt;code&gt;3 / 1&lt;/code&gt; fires fast but can flap on a single spike; &lt;code&gt;3 / 2&lt;/code&gt; smooths transient blips at ~one extra interval of latency. Tune this &lt;em&gt;before&lt;/em&gt; the threshold. Per-use-case recommendations are in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#m-out-of-n-evaluation" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detection presets
&lt;/h3&gt;

&lt;p&gt;The template ships five presets for common patterns. Switch &lt;code&gt;DetectionType&lt;/code&gt; and each gets an appropriate query and threshold.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DetectionType&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Default threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sensitive-file-access&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Access to a specific path&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;failed-access-attempts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authentication/authorization failures&lt;/td&gt;
&lt;td&gt;&amp;gt; 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bulk-delete-operations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mass deletion (ransomware signal)&lt;/td&gt;
&lt;td&gt;&amp;gt; 50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;specific-user-activity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Privileged-user monitoring&lt;/td&gt;
&lt;td&gt;&amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;custom&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any Logs Insights query&lt;/td&gt;
&lt;td&gt;your choice&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Regulated environments — one thing to get right first&lt;/strong&gt;: &lt;code&gt;ActionLogLineCount&lt;/code&gt; puts the matched log lines (usernames, file paths, client IPs — potentially &lt;strong&gt;PHI&lt;/strong&gt;) into the SNS notification, which leaves the CloudWatch boundary. For healthcare/finance/public-sector data, default to &lt;code&gt;ActionLogLineCount: 0&lt;/code&gt; and let responders pivot into Logs Insights for the detail. This is a detection &lt;em&gt;mechanism&lt;/em&gt;, not a compliance attestation — classify your fields and confirm APPI/FISC/ISMAP/HIPAA scope with your compliance team first. Full guidance, the regulated default, the alert-audit-trail requirements, and multi-account (StackSets) rollout are in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#regulated-environments-healthcare--finance--public-sector-default-to-actionloglinecount0" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Admin-plane destructive-op detection (defense in depth)
&lt;/h3&gt;

&lt;p&gt;To be precise about scope: on the admin audit log this preset catches &lt;strong&gt;management-plane&lt;/strong&gt; destructive operations — a burst of Snapshot deletes, &lt;code&gt;volume offline&lt;/code&gt;, &lt;code&gt;volume delete&lt;/code&gt; — the kind of action an attacker with stolen admin credentials (or a mistaken operator) would take to remove recovery points. It does &lt;strong&gt;not&lt;/strong&gt; see user-file encryption over SMB; that's ONTAP ARP's job at the storage layer (&lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Part 3&lt;/a&gt;), with FPolicy (&lt;a href="https://dev.to/aws-builders/fpolicy-file-activity-pipeline-ontap-to-datadog-via-ecs-fargate-2ing"&gt;Part 4&lt;/a&gt;) for per-file operations. Layer all three: ARP for encryption, FPolicy for file ops, and this Log Alarm for admin-plane tampering (e.g., someone deleting the Snapshots you'd restore from).&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;DETECTION_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bulk-delete-operations &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;ALARM_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;50 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;QUERY_RESULTS_TO_ALARM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SNS_TOPIC_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_SNS_ARN&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  bash shared/scripts/deploy-log-alarm.sh
&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;Detection layer&lt;/th&gt;
&lt;th&gt;Catches&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage layer (ARP)&lt;/td&gt;
&lt;td&gt;User-file encryption&lt;/td&gt;
&lt;td&gt;ML-based entropy analysis&lt;/td&gt;
&lt;td&gt;Real-time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File ops (FPolicy)&lt;/td&gt;
&lt;td&gt;Per-file create/write/delete/rename over NFS/SMB&lt;/td&gt;
&lt;td&gt;Protocol-level intercept&lt;/td&gt;
&lt;td&gt;~6 s (validated, Part 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admin plane (Log Alarm)&lt;/td&gt;
&lt;td&gt;Snapshot/volume destructive ops by admins&lt;/td&gt;
&lt;td&gt;Count-based threshold on admin audit log&lt;/td&gt;
&lt;td&gt;~5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three different vantage points on the same attack: ARP sees the encryption, FPolicy sees the file operations, and this Log Alarm sees an admin deleting the Snapshots you'd recover from. Layering them means whatever slips past one is more likely caught by another.&lt;/p&gt;

&lt;p&gt;In ATT&amp;amp;CK terms, that admin-plane view is &lt;strong&gt;T1490 Inhibit System Recovery&lt;/strong&gt; — an attacker deleting your restore points so you &lt;em&gt;can't&lt;/em&gt; roll back the encryption ARP detects (T1486). Two techniques, two controls: detect the Snapshot deletion here, and &lt;strong&gt;prevent&lt;/strong&gt; it with &lt;strong&gt;SnapLock&lt;/strong&gt; (WORM Snapshots that can't be deleted before expiry) so your recovery points survive the attempt. The full ATT&amp;amp;CK mapping, tamper-resistance guidance (who can delete the alarm and how to guard it), and a one-slide coverage map are in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#mitre-attck-mapping" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ops note (baseline first)&lt;/strong&gt;: Scheduled bulk operations — nightly backups, batch ETL, archive cleanups — can legitimately exceed a "50 deletes / 5 min" threshold and page on-call for nothing. Baseline your normal delete volume for a few days &lt;em&gt;without&lt;/em&gt; an alarm action, then set the threshold above your routine peak (the exact baseline query is in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#baselining-a-threshold-before-enabling-bulk-delete-etc" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt;). This mirrors the ARP learning-period caveat from &lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Part 3&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Compliance: notify on regulated-data access
&lt;/h3&gt;

&lt;p&gt;For finance or healthcare, where "any touch of this data must be recorded and notified", a simple per-path detection works.&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;DETECTION_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sensitive-file-access &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TARGET_PATTERN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/vol/finance/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;ALARM_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SNS_TOPIC_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_SNS_ARN&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  bash shared/scripts/deploy-log-alarm.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Alert-fatigue note&lt;/strong&gt;: A &lt;code&gt;&amp;gt; 0&lt;/code&gt; threshold on an actively-used path pages on every access and quickly becomes noise. Reserve &lt;code&gt;&amp;gt; 0&lt;/code&gt; for genuinely restricted paths (break-glass directories, quarantined data). For paths with legitimate regular access, prefer a rate threshold (e.g., access from an unexpected principal, or volume above a baseline) and route to a ticket/Slack channel rather than a pager.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Privileged-user monitoring
&lt;/h3&gt;

&lt;p&gt;Keeping a record of every admin-account action uses the same mechanism.&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;DETECTION_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;specific-user-activity &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TARGET_PATTERN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"fsxadmin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;ALARM_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;SNS_TOPIC_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_SNS_ARN&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  bash shared/scripts/deploy-log-alarm.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  E2E Validation (Tokyo Region)
&lt;/h2&gt;

&lt;p&gt;Because it's a brand-new feature, I was skeptical it would behave as documented. So I deployed the template in a real Tokyo-region environment and drove the alarm through a state transition.&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;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CloudFormation deploy&lt;/td&gt;
&lt;td&gt;✅ Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM role auto-creation&lt;/td&gt;
&lt;td&gt;✅ ScheduledQueryRole + LogLineRole&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduled Query execution&lt;/td&gt;
&lt;td&gt;✅ INSUFFICIENT_DATA → OK transition confirmed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Console display&lt;/td&gt;
&lt;td&gt;✅ Shown as "Log alarm" type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs Insights query&lt;/td&gt;
&lt;td&gt;✅ Matched (12 hits / 3,482 records scanned for &lt;code&gt;/volume/&lt;/code&gt;; 472 hits for &lt;code&gt;ssh&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the console it shows up as a new &lt;strong&gt;"Log alarm"&lt;/strong&gt; type, distinct from the metric alarms you already have. In the screenshot below, look at the &lt;strong&gt;Type&lt;/strong&gt; column — the new alarm is labeled "Log alarm" rather than "Metric alarm".&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%2F2m5jq72v62fr1esptgbk.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%2F2m5jq72v62fr1esptgbk.png" alt="CloudWatch Alarms list showing the new "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opening the alarm shows the Log Alarm detail page. Note the &lt;strong&gt;query configuration&lt;/strong&gt; (the Logs Insights query string, the target log group &lt;code&gt;/syslog/fsxn-admin-audit&lt;/code&gt;, and the 5-minute schedule) and the &lt;strong&gt;two IAM roles&lt;/strong&gt; CloudFormation created automatically — &lt;code&gt;ScheduledQueryRole&lt;/code&gt; (runs the query) and &lt;code&gt;LogLineRole&lt;/code&gt; (attaches matched log lines to the notification).&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%2F4nigc4xt75up2s6n7ci2.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%2F4nigc4xt75up2s6n7ci2.png" alt="Log alarm detail page showing the Logs Insights query configuration, target log group, 5-minute schedule, and the auto-created ScheduledQueryRole and LogLineRole"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the query in Logs Insights hits real audit data. The bar chart at the top shows the match count per interval, and the results table below lists the matched log lines — here the &lt;code&gt;/volume/&lt;/code&gt; filter returned &lt;strong&gt;12 matches across 3,482 records scanned&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%2Fycyh5ms6ic4gxm4ffm9i.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%2Fycyh5ms6ic4gxm4ffm9i.png" alt="Logs Insights query result for the /volume/ filter — 12 matches over 3,482 records scanned, with the match-count bar chart above the results table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The alarm itself stays &lt;strong&gt;OK&lt;/strong&gt; while there's no matching access — the screenshot shows the state after the initial &lt;code&gt;INSUFFICIENT_DATA → OK&lt;/code&gt; transition, with an empty history graph because nothing has crossed the threshold yet. With threshold &lt;code&gt;&amp;gt; 0&lt;/code&gt;, it flips to &lt;strong&gt;ALARM&lt;/strong&gt; the moment a single access to the sensitive path appears.&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%2F0qt1ccmb072yhteghkbj.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%2F0qt1ccmb072yhteghkbj.png" alt="Log alarm detail page in state OK, showing the INSUFFICIENT_DATA to OK transition and a flat history graph below the threshold"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Audit trail of the alert itself&lt;/strong&gt;: for compliance you also need evidence that the alarm fired and who was notified. Alarm state history is retained &lt;strong&gt;90 days&lt;/strong&gt; (fixed); for multi-year evidence, route CloudWatch Alarm state-change events (EventBridge) to S3, and rely on CloudTrail for the "who configured this detection" record. Details and retention table: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#audit-trail-of-the-alert-itself" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gotcha&lt;/th&gt;
&lt;th&gt;Workaround&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AWS CLI not yet supported (no &lt;code&gt;put-log-alarm&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Use CloudFormation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cfn-lint E3006 (resource type not yet in the spec)&lt;/td&gt;
&lt;td&gt;Suppress per-resource (not a blanket disable); &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#caveats-discovered-during-validation" rel="noopener noreferrer"&gt;exact &lt;code&gt;Metadata&lt;/code&gt; snippet in the setup guide&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First evaluation takes 5–10 min&lt;/td&gt;
&lt;td&gt;Just wait&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notification includes log lines (PII/PHI risk)&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;ActionLogLineCount=0&lt;/code&gt; in regulated environments (see the callout above)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-node log streams (&lt;code&gt;FsxId...-01/-02&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Query the whole &lt;strong&gt;log group&lt;/strong&gt;, don't pin &lt;code&gt;@logStream&lt;/code&gt;, or you miss half the traffic on HA takeover&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The "AWS CLI doesn't have &lt;code&gt;put-log-alarm&lt;/code&gt; yet" is a just-after-GA reality — for now CloudFormation or the console are the only creation paths. Platform/CoE note: drift detection won't cover a resource type the CLI can't yet describe, so treat CloudFormation as the single source of truth until the API surface completes.&lt;/p&gt;




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

&lt;p&gt;Rough estimate for &lt;strong&gt;one&lt;/strong&gt; alarm at a 5-minute cadence.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Logs/day&lt;/th&gt;
&lt;th&gt;Log Alarm&lt;/th&gt;
&lt;th&gt;Metric filter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100 MB&lt;/td&gt;
&lt;td&gt;~$6.6/month&lt;/td&gt;
&lt;td&gt;~$3/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500 MB&lt;/td&gt;
&lt;td&gt;~$33/month&lt;/td&gt;
&lt;td&gt;~$3/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;~$66/month&lt;/td&gt;
&lt;td&gt;~$3/month&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 the cost scales&lt;/strong&gt;: the table above is &lt;strong&gt;per alarm&lt;/strong&gt;. Each alarm runs its own Scheduled Query over the same log group, so cost is &lt;em&gt;alarms × cadence × scan size&lt;/em&gt; — ten alarms on one log group is ~10×, not a flat add-on. Narrow queries with &lt;code&gt;filter&lt;/code&gt;/&lt;code&gt;limit&lt;/code&gt; and consolidate related detections to keep it bounded. Full breakdown in the &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#how-cost-scales-read-before-fanning-out" rel="noopener noreferrer"&gt;setup guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Honestly, Log Alarms cost more than the metric-filter approach — the Scheduled Query scans logs on each run. But the difference is recoverable elsewhere: log lines in the notification cut investigation time, it applies retroactively to existing logs, and setup is a single step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the premium is worth it&lt;/strong&gt; (a line you can put in a proposal): choose Log Alarms when the alert &lt;em&gt;content&lt;/em&gt; matters for triage (you want the matched lines in the page), when the detection query needs full Logs Insights syntax a metric filter can't express, or when you need to apply it retroactively to existing logs. Stick with metric filters for high-volume, well-understood, purely numeric signals where a few dollars × many alarms adds up. For most teams the crossover is engineer time: if the log-line context saves even one 15-minute console dig per incident, the premium pays for itself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If volume makes cost a concern, dropping the cadence to 15 minutes cuts it to a third, and adding &lt;code&gt;limit&lt;/code&gt; to the query bounds the scan.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  When to Use Which
&lt;/h2&gt;

&lt;p&gt;Log Alarms aren't meant to replace every kind of monitoring. Depending on the need, metric filters or the OTel Collector fit better. Among this project's three delivery paths, the Log Alarm is the simplest entry point.&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;When&lt;/th&gt;
&lt;th&gt;Extra infra&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Log Alarm&lt;/strong&gt; (this article)&lt;/td&gt;
&lt;td&gt;Simple threshold alerts&lt;/td&gt;
&lt;td&gt;None (self-contained in CloudWatch)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda → vendor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dashboards, SIEM&lt;/td&gt;
&lt;td&gt;Forwarding Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OTel Collector&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-backend, PII redaction&lt;/td&gt;
&lt;td&gt;Forwarding Lambda + Collector (ECS Fargate)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Concretely, the OTel Collector path's "extra infra" is two layers: three forwarding Lambdas (audit-log shipper, EMS handler, FPolicy handler) sending OTLP/HTTP, plus a resident &lt;code&gt;otel/opentelemetry-collector-contrib&lt;/code&gt; task on ECS Fargate (with NAT Gateway for egress, Cloud Map for task IP resolution, ALB/autoscaling under load). Fan-out to Grafana/Honeycomb/Datadog and PII redaction are then a single change in the Collector's config YAML. The Log Alarm, by contrast, is the "everything stays inside CloudWatch" minimal option.&lt;/p&gt;

&lt;p&gt;A Log Alarm is a first alert, not a full investigation tool. Let it handle "notice it first", and hand off the deep dive to vendor tooling like Datadog or Splunk. That division of labor is the realistic one.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For ONTAP operators&lt;/strong&gt;, the setup guide covers the storage-side specifics: what &lt;code&gt;security audit&lt;/code&gt; captures vs what &lt;code&gt;log-forwarding&lt;/code&gt; sends, keeping an existing SIEM alongside CloudWatch (multiple destinations), ONTAP EMS &lt;em&gt;native&lt;/em&gt; email/SNMP as an alternative to pushing to AWS, a dead-man's-switch heartbeat alarm for when syslog delivery stops, and DR-region deployment — &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md#security-audit--what-gets-captured" rel="noopener noreferrer"&gt;ONTAP operational notes&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Delete all Log Alarm stacks&lt;/span&gt;
bash shared/scripts/cleanup-log-alarm.sh &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;CloudWatch Log Alarms aren't a flashy feature. But "turn what you noticed in the logs straight into an alert" lowers the bar for setting up monitoring — you're done with one query before you'd have finished building a metric filter, staring at a metric, and wiring an alarm. It pairs well with FSx for ONTAP audit logs, answering the "I just want to notice it first" needs of storage security without extra infrastructure. There's some just-after-GA roughness (the AWS CLI hasn't caught up), but CloudFormation works today, verified in a real environment.&lt;/p&gt;

&lt;p&gt;Upcoming in the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 4&lt;/strong&gt;: Terraform module equivalents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 4&lt;/strong&gt;: CDK construct library&lt;/li&gt;
&lt;li&gt;PagerDuty escalation for CloudWatch alarms — see &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/pagerduty-escalation-guide.md" rel="noopener noreferrer"&gt;pagerduty-escalation-guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the full &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/ROADMAP.md" rel="noopener noreferrer"&gt;ROADMAP&lt;/a&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations" rel="noopener noreferrer"&gt;github.com/Yoshiki0705/fsxn-observability-integrations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/shared/templates/cloudwatch-log-alarm.yaml" rel="noopener noreferrer"&gt;cloudwatch-log-alarm.yaml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy script&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/shared/scripts/deploy-log-alarm.sh" rel="noopener noreferrer"&gt;deploy-log-alarm.sh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setup guide (EN)&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/cloudwatch-log-alarm.md" rel="noopener noreferrer"&gt;cloudwatch-log-alarm.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runbook&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/runbooks/log-alarm-triggered.md" rel="noopener noreferrer"&gt;log-alarm-triggered.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Syslog VPCE setup&lt;/strong&gt;: &lt;a href="https://github.com/Yoshiki0705/fsxn-observability-integrations/blob/main/docs/en/syslog-vpce-setup-guide.md" rel="noopener noreferrer"&gt;syslog-vpce-setup-guide.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;What's New — &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/07/amazon-cloudwatch-log-alarms/" rel="noopener noreferrer"&gt;CloudWatch supports creating alarms from log queries&lt;/a&gt; (2026-07)&lt;/li&gt;
&lt;li&gt;What's New — &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/06/amazon-cloudwatch-syslog-ingestion/" rel="noopener noreferrer"&gt;CloudWatch Logs supports managed syslog ingestion&lt;/a&gt; (2026-06)&lt;/li&gt;
&lt;li&gt;Docs — &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Alarm-On-Logs.html" rel="noopener noreferrer"&gt;Alarming on logs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs — &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_Syslog_Setup.html" rel="noopener noreferrer"&gt;Setting up syslog ingestion&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Series Navigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/why-your-fsx-for-ontap-audit-logs-deserve-better-than-ec2-kod"&gt;Why Your FSx for ONTAP Logs Deserve Better&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/event-driven-ransomware-detection-with-ontap-arp-datadog-4cda"&gt;Event-Driven Ransomware Detection with ONTAP ARP + Datadog&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/escape-vendor-lock-in-multi-backend-log-delivery-with-otel-collector-for-fsx-for-ontap-2inb"&gt;Escape Vendor Lock-in with OTel Collector&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 13&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/9-services-one-architecture-what-we-learned-shipping-fsx-for-ontap-logs-to-every-major-19ig"&gt;9 Vendors, One Architecture&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 14&lt;/strong&gt;: &lt;a href="https://dev.to/aws-builders/can-you-use-system-manager-with-fsx-for-ontap-a-reality-check-with-screenshots"&gt;Can You Use System Manager with FSx for ONTAP?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 17&lt;/strong&gt;: Alerting on Audit Logs with CloudWatch Log Alarms (this post)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you deploy this, I'd love to hear how it went — drop a comment or open a GitHub issue.&lt;/em&gt;&lt;/p&gt;

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

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>security</category>
      <category>observability</category>
    </item>
    <item>
      <title>42 Patterns, Category Architecture, and HA LifeKeeper Monitoring — FSx for ONTAP S3 Access Points, Phase 18</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Sun, 21 Jun 2026 16:54:22 +0000</pubDate>
      <link>https://dev.to/aws-builders/42-patterns-category-architecture-and-ha-lifekeeper-monitoring-fsx-for-ontap-s3-access-points-4fh</link>
      <guid>https://dev.to/aws-builders/42-patterns-category-architecture-and-ha-lifekeeper-monitoring-fsx-for-ontap-s3-access-points-4fh</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Phase 18 restructures the entire repository from 41 flat directories into a categorized &lt;code&gt;solutions/&lt;/code&gt; hierarchy, adds &lt;strong&gt;HA LifeKeeper Monitoring&lt;/strong&gt; as a new pattern category, introduces &lt;strong&gt;5 category-specific architecture diagrams&lt;/strong&gt;, and establishes modern Python project infrastructure. The repository now contains &lt;strong&gt;42 deployable patterns&lt;/strong&gt; organized for discoverability at scale.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns" rel="noopener noreferrer"&gt;github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Restructure?
&lt;/h2&gt;

&lt;p&gt;With 41 pattern directories at the repository root, navigating the project had become unwieldy. New contributors could not quickly find patterns by domain, the README required extensive scrolling, and adding new categories (HA, GenAI) had no clear placement convention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (flat)&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;legal-compliance/  financial-idp/  semiconductor-eda/  sap-erp-adjacent/
flexcache-anycast-dr/  genai-kb-selfservice-curation/  event-driven-fpolicy/
... (41 directories at root)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (categorized)&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;solutions/
├── industry/           # 28 UC patterns (UC1-UC28)
├── flexcache/          # 7 FlexCache/FlexClone patterns
├── genai/              # 2 GenAI patterns (UC29-UC30)
├── sap/                # SAP/ERP pattern
├── ha/                 # HA monitoring (new)
├── event-driven/       # 2 FPolicy event-driven patterns
└── edge/               # CDN/edge delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Move: git mv with History Preservation
&lt;/h2&gt;

&lt;p&gt;All 41 directories were moved using &lt;code&gt;git mv&lt;/code&gt;, preserving full commit history. Key renames include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Old Path&lt;/th&gt;
&lt;th&gt;New Path&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;&lt;code&gt;sap-erp-adjacent/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;solutions/sap/erp-adjacent/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Category grouping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dynamic-flexcache-render-workflow/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;solutions/flexcache/dynamic-render-workflow/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shorter name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;genai-kb-selfservice-curation/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;solutions/genai/kb-selfservice-curation/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;devops-flexclone-cicd/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;solutions/flexcache/devops-cicd/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;content-edge-delivery/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;solutions/edge/content-delivery/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Category grouping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use &lt;code&gt;git log --follow &amp;lt;file&amp;gt;&lt;/code&gt; to trace history across the move.&lt;/p&gt;




&lt;h2&gt;
  
  
  HA LifeKeeper Monitoring — New Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SIOS LifeKeeper&lt;/strong&gt; is a Linux/Windows HA clustering solution that can be used on Amazon EC2 for application-aware failover scenarios. With FSx for ONTAP Multi-AZ as shared storage (NFS/iSCSI, depending on OS and configuration), this pattern focuses on observing LifeKeeper logs without putting monitoring agents on the HA nodes.&lt;/p&gt;

&lt;p&gt;The new HA pattern (&lt;code&gt;solutions/ha/lifekeeper-monitoring/&lt;/code&gt;) provides &lt;strong&gt;non-intrusive&lt;/strong&gt; log analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TB
    subgraph "HA Cluster"
        LK1[LifeKeeper Node 1&amp;lt;br/&amp;gt;Active]
        LK2[LifeKeeper Node 2&amp;lt;br/&amp;gt;Standby]
    end

    subgraph "Shared Storage"
        FSXN[FSx for ONTAP Multi-AZ]
        S3AP[S3 Access Point&amp;lt;br/&amp;gt;Read-only log access]
    end

    subgraph "Analysis Pipeline"
        SFN[Step Functions]
        DISC[Discovery Lambda&amp;lt;br/&amp;gt;Log classification]
        PROC[Processing Lambda&amp;lt;br/&amp;gt;Bedrock Root Cause Analysis]
        RPT[Report Lambda&amp;lt;br/&amp;gt;Health score + alerts]
    end

    LK1 --&amp;gt;|Log write| FSXN
    FSXN --&amp;gt; S3AP --&amp;gt;|Non-intrusive read| DISC
    SFN --&amp;gt; DISC --&amp;gt; PROC --&amp;gt; RPT
    PROC --&amp;gt;|Nova Pro| BEDROCK[Amazon Bedrock]
    RPT --&amp;gt; SNS[SNS Alert]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Design Decisions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Non-intrusive to HA nodes&lt;/strong&gt;: No monitoring agent is installed on HA nodes. The S3 AP read path avoids host-level changes, while still consuming FSx/S3 API throughput like any other read workload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-loop&lt;/strong&gt;: AI analysis is advisory only. LifeKeeper's own health checks handle failover decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health Scoring&lt;/strong&gt;: 0-100 score with deductions for failover events, comm path latency, and resource state anomalies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root Cause Analysis&lt;/strong&gt;: Bedrock Nova Pro analyzes state transitions (ISP→OSF, ISS→ISP) to identify likely causes.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Implementation note&lt;/strong&gt;: This pattern observes LifeKeeper logs and produces advisory analysis. It does not replace LifeKeeper cluster design, quorum/witness configuration, split-brain prevention, protocol-specific recovery kit setup, or application-level failover testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Example Operational Metrics
&lt;/h3&gt;

&lt;p&gt;These are suggested evaluation metrics for future validation. Phase 18 verifies the DemoMode pipeline, not real-cluster failover triage time.&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;Metric&lt;/th&gt;
&lt;th&gt;Demo Target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Operations&lt;/td&gt;
&lt;td&gt;Time from workflow start to structured triage report&lt;/td&gt;
&lt;td&gt;&amp;lt; 10 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical&lt;/td&gt;
&lt;td&gt;Log discovery completeness&lt;/td&gt;
&lt;td&gt;All configured LifeKeeper log paths in scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quality&lt;/td&gt;
&lt;td&gt;False positive alert rate&lt;/td&gt;
&lt;td&gt;&amp;lt; 5% (requires real-cluster validation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Monthly monitoring cost&lt;/td&gt;
&lt;td&gt;&amp;lt; $15 (5-min polling, 1 cluster)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Deploy (DemoMode)
&lt;/h3&gt;

&lt;p&gt;DemoMode=true deploys without FSx for ONTAP — uses a regular S3 bucket with sample LifeKeeper logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;solutions/ha/lifekeeper-monitoring
sam build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sam deploy &lt;span class="nt"&gt;--guided&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--parameter-overrides&lt;/span&gt; &lt;span class="nv"&gt;DemoMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;S3AccessPointAlias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-demo-bucket &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;OutputBucketName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-output-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DemoMode verification confirms: SAM template deploys successfully, Step Functions workflow executes all states, Discovery Lambda classifies sample logs correctly, and Processing Lambda generates a health score report. Bedrock analysis produces structured, advisory observations based on log patterns; failover decisions remain outside the AI workflow.&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%2Fn9j5gotqqo5ddnaasfyn.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%2Fn9j5gotqqo5ddnaasfyn.png" alt="Step Functions graph view — HA LifeKeeper Monitoring workflow completed successfully" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verified in ap-northeast-1 on 2026-06-21. The sample log set intentionally contains failure-like events, resulting in a demo health score of 40/100. The score is not a benchmark for LifeKeeper or FSx for ONTAP; it demonstrates that the scoring pipeline detects and reports anomalies from sample logs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Coming in Phase 19&lt;/strong&gt;: Full E2E verification with a real SIOS LifeKeeper HA cluster (AWS Marketplace + FSx for ONTAP Multi-AZ) including live failover testing, actual state transition detection, and Bedrock RCA quality assessment against real-world failure scenarios.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Category Architecture Diagrams
&lt;/h2&gt;

&lt;p&gt;Phase 18 adds &lt;strong&gt;5 mermaid architecture diagrams&lt;/strong&gt; to README.md (both JA and EN), each in a collapsible &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; block:&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;Key Components&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🏭 FlexCache&lt;/td&gt;
&lt;td&gt;ONTAP REST API → HealthCheck → RouteDecision → DynamoDB routing → Create/Cleanup lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🤖 GenAI&lt;/td&gt;
&lt;td&gt;FPolicy → SQS → EventBridge → Bedrock KB → RetrieveAndGenerate / Agentic tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🛡️ HA&lt;/td&gt;
&lt;td&gt;S3 AP non-intrusive read → Bedrock RCA → Health score → SNS alerts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡ Event-Driven&lt;/td&gt;
&lt;td&gt;FPolicy Engine → ECS Fargate TCP → SQS → EventBridge rule routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🌐 Edge/CDN&lt;/td&gt;
&lt;td&gt;3 delivery modes (ORIGIN_PULL, OAC, PUBLISH_PUSH) → vendor-neutral CDN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All diagrams include &lt;code&gt;accTitle&lt;/code&gt; and &lt;code&gt;accDescr&lt;/code&gt; for screen reader accessibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Infrastructure Improvements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  pyproject.toml (PEP 621)
&lt;/h3&gt;

&lt;p&gt;Modern Python project metadata with unified tool configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.ruff]&lt;/span&gt;
&lt;span class="py"&gt;target-version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"py312"&lt;/span&gt;
&lt;span class="py"&gt;line-length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;

&lt;span class="nn"&gt;[tool.pytest.ini_options]&lt;/span&gt;
&lt;span class="py"&gt;addopts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-v --tb=short --import-mode=importlib"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.coverage.report]&lt;/span&gt;
&lt;span class="py"&gt;fail_under&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependency Pinning
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# requirements.txt — exact versions for reproducibility
&lt;/span&gt;&lt;span class="py"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=1.43.29&lt;/span&gt;
&lt;span class="py"&gt;urllib3&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=2.7.0&lt;/span&gt;
&lt;span class="py"&gt;jsonschema&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=4.17.3&lt;/span&gt;

&lt;span class="c"&gt;# requirements-dev.txt
&lt;/span&gt;&lt;span class="py"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=9.1.0&lt;/span&gt;
&lt;span class="py"&gt;hypothesis&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=6.155.2&lt;/span&gt;
&lt;span class="py"&gt;moto&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=5.2.2&lt;/span&gt;
&lt;span class="py"&gt;ruff&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=0.15.17&lt;/span&gt;
&lt;span class="py"&gt;cfn-lint&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;=1.51.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .cfnlintrc
&lt;/h3&gt;

&lt;p&gt;Project-wide cfn-lint configuration that discovers all templates under &lt;code&gt;solutions/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;templates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solutions/**/template.yaml"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solutions/**/template-deploy.yaml"&lt;/span&gt;
&lt;span class="na"&gt;ignore_checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;W3002&lt;/span&gt;  &lt;span class="c1"&gt;# Local CodeUri (sam build handles upload)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;W1031&lt;/span&gt;  &lt;span class="c1"&gt;# Fn::Sub false positive with Secrets Manager ARNs&lt;/span&gt;
&lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Additional Tooling
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.gitattributes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Consistent line endings, language-specific diff drivers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.github/PULL_REQUEST_TEMPLATE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Project-specific PR checklist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;solutions/README.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Category navigation index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CHANGELOG.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep a Changelog format, all releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CONTRIBUTING.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Adding a New Pattern" section with category guide&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  CI/CD Changes
&lt;/h2&gt;

&lt;p&gt;The CI pipeline was split to avoid pytest &lt;code&gt;importlib&lt;/code&gt; mode namespace collisions when multiple patterns with identically-named &lt;code&gt;handler.py&lt;/code&gt; files are collected together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before: single pytest invocation (collision risk)&lt;/span&gt;
&lt;span class="s"&gt;pytest shared/tests/ solutions/**/tests/ --cov=shared&lt;/span&gt;

&lt;span class="c1"&gt;# After: isolated invocations&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;Run shared tests with coverage&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pytest shared/tests/ --cov=shared&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;Run pattern tests&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pytest solutions/industry/*/tests/ solutions/flexcache/*/tests/ ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also added &lt;code&gt;persist-credentials: false&lt;/code&gt; to all &lt;code&gt;actions/checkout&lt;/code&gt; steps (zizmor security hardening).&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-Perspective Review
&lt;/h2&gt;

&lt;p&gt;The restructuring and HA monitoring pattern were reviewed from partner delivery, storage architecture, HA operations, security, CI/CD, accessibility, and contributor onboarding perspectives. The review resulted in wording changes around neutrality, operational caveats, connector validation, HA safety boundaries, and repository discoverability.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means for You
&lt;/h2&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;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Partner / SI / delivery team&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pattern selection is now intuitive by category — pick &lt;code&gt;solutions/industry/&lt;/code&gt; for industry PoCs, &lt;code&gt;solutions/flexcache/&lt;/code&gt; for distributed workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;New contributor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CONTRIBUTING.md&lt;/code&gt; now includes "Adding a New Pattern" with required checklist and category selection guide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Existing repository users&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git log --follow &amp;lt;file&amp;gt;&lt;/code&gt; still works. &lt;code&gt;sam build&lt;/code&gt; and &lt;code&gt;sam deploy&lt;/code&gt; are unchanged (run from the pattern directory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zero changes needed in your &lt;code&gt;samconfig.toml&lt;/code&gt; or deployment scripts — CodeUri is relative to template.yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure cost&lt;/strong&gt;: Zero. This is a repository organization change; it does not deploy AWS resources by itself.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Phase 16-17 blog articles (GenAI patterns) — ready to publish&lt;/li&gt;
&lt;li&gt;dev.to series updated for directory restructuring (Phase 13 link fixed)&lt;/li&gt;
&lt;li&gt;Next pattern candidates under evaluation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns.git
&lt;span class="nb"&gt;cd &lt;/span&gt;FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns

&lt;span class="c"&gt;# Browse patterns by category&lt;/span&gt;
&lt;span class="nb"&gt;ls &lt;/span&gt;solutions/

&lt;span class="c"&gt;# Deploy HA LifeKeeper monitoring (DemoMode)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;solutions/ha/lifekeeper-monitoring
sam build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sam deploy &lt;span class="nt"&gt;--guided&lt;/span&gt; &lt;span class="nt"&gt;--parameter-overrides&lt;/span&gt; &lt;span class="nv"&gt;DemoMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
make test-quick &lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.venv/bin/python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Yoshiki Fujiwara&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lifekeeper</category>
      <category>amazonfsxfornetappontap</category>
      <category>s3accesspoints</category>
    </item>
    <item>
      <title>Amazon Quick Agentic Workspace Powered by FSx for ONTAP S3 Access Points — Phase 17</title>
      <dc:creator>Yoshiki Fujiwara(藤原 善基)@AWS Community Builder</dc:creator>
      <pubDate>Sun, 21 Jun 2026 15:54:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/amazon-quick-agentic-workspace-powered-by-fsx-for-ontap-s3-access-points-phase-17-1cf2</link>
      <guid>https://dev.to/aws-builders/amazon-quick-agentic-workspace-powered-by-fsx-for-ontap-s3-access-points-phase-17-1cf2</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;UC30 bridges the gap between file-based business data and AI-powered actions. Business users maintain structured and unstructured data on an FSx for ONTAP SMB share, while &lt;strong&gt;Amazon Quick&lt;/strong&gt; (Index / Sight / Flows) consumes it through S3 Access Points and a serverless Action API — providing search, BI, and governed action workflows from a single workspace.&lt;/p&gt;

&lt;p&gt;Where UC29 focuses on "self-service knowledge ingestion into Bedrock KB," UC30 focuses on &lt;strong&gt;unifying search, analytics, and action execution&lt;/strong&gt; behind Quick Suite's agentic interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New in this release&lt;/strong&gt;: &lt;code&gt;generate_brief_with_web&lt;/code&gt; action augments internal context with real-time web search results via AgentCore Web Search Tool (GA June 2026), enabling briefs that combine primary internal data with current public context.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns" rel="noopener noreferrer"&gt;github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns&lt;/a&gt; (see &lt;code&gt;solutions/genai/quick-agentic-workspace/&lt;/code&gt; and &lt;code&gt;samconfig.toml.example&lt;/code&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  Amazon Quick × S3 AP Data Mapping
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Quick Feature&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;S3 AP Data&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;Quick Index / Research&lt;/td&gt;
&lt;td&gt;Unstructured file search&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;index/&amp;lt;role&amp;gt;/&lt;/code&gt; (md/pdf)&lt;/td&gt;
&lt;td&gt;S3 AP as data source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick Sight&lt;/td&gt;
&lt;td&gt;Structured BI &amp;amp; visualization&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;analytics/&amp;lt;role&amp;gt;/&lt;/code&gt; (csv)&lt;/td&gt;
&lt;td&gt;Glue/Athena (Athena Query Lambda)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick Flows&lt;/td&gt;
&lt;td&gt;Action automation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;flows/&amp;lt;role&amp;gt;/&lt;/code&gt; (json)&lt;/td&gt;
&lt;td&gt;Action API (API Gateway + Lambda + Bedrock)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick Flows + Web&lt;/td&gt;
&lt;td&gt;Web-augmented briefs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;flows/&amp;lt;role&amp;gt;/&lt;/code&gt; + web&lt;/td&gt;
&lt;td&gt;Action API + AgentCore Web Search (opt-in)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Seven roles (sales / marketing / finance / IT / operations / legal / developers) share the same AI-dedicated volume — reusable from UC29.&lt;/p&gt;

&lt;p&gt;Design note: FSx for ONTAP S3 Access Points are useful as an integration boundary, but they do not remove the need to validate each consuming service connector. The access path combines S3/IAM policy evaluation with file-system-level identity authorization.&lt;/p&gt;




&lt;h2&gt;
  
  
  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%2Fc9f2071r5vmlyf5xjobl.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%2Fc9f2071r5vmlyf5xjobl.png" alt="Amazon Quick home screen showing Index, Sight, and Flows" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Amazon Quick provides a unified workspace — search (Index), BI (Sight), and action automation (Flows) — powered by FSx for ONTAP data via S3 Access Points.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Windows Explorer (drag &amp;amp; drop into quick-workspace/ SMB share)
  ├── index/&amp;lt;role&amp;gt;/ → Quick Index (unstructured search)
  ├── analytics/&amp;lt;role&amp;gt;/ → Glue/Athena → Quick Sight (BI)
  └── flows/&amp;lt;role&amp;gt;/ → Action API → Quick Flows (actions)

Action API (6 actions):
  API Gateway (IAM auth / SigV4)
  → Lambda (per-action authorization + HITL gate)
  → generate_brief           → Bedrock Converse (internal context only)
  → generate_brief_with_web  → Bedrock Converse + AgentCore Web Search (hybrid)
  → create_action_item       → SNS notification
  → request_approval         → DynamoDB (HITL entry)
  → approve                  → DynamoDB (admin only)
  → execute_approved         → DynamoDB check + execution (enforced HITL)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hybrid RAG Flow (generate_brief_with_web)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Quick&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Flows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;request:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&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;"generate_brief_with_web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"params"&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="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
  &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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Internal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;params.context&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;—&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;FSx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ONTAP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;content)&lt;/span&gt;&lt;span class="w"&gt;
  &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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;AgentCore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Web&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(us-east&lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;MCP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;protocol)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;params.web_query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;params.title&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Amazon&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;web&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;snippets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;URLs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;titles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dates&lt;/span&gt;&lt;span class="w"&gt;
  &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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bedrock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Converse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(ap-northeast&lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="err"&gt;system&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;prompt:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;internal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;primary,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;web&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;supplementary,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;untrusted&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Unified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;brief&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Internal:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &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="err"&gt;Web:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;title&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;(URL)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;citations&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Security Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Authentication + Per-Action Authorization
&lt;/h3&gt;

&lt;p&gt;The Action API uses IAM authentication (SigV4). The handler extracts the &lt;strong&gt;authenticated caller identity&lt;/strong&gt; (&lt;code&gt;requestContext.identity&lt;/code&gt;) — not self-declared body fields — and performs per-action authorization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ACTION_AUTH_MODE=open&lt;/code&gt; (default/demo): No enforcement; audit fields still bound to authenticated caller&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;For production, use &lt;code&gt;ACTION_AUTH_MODE=enforce&lt;/code&gt; and explicitly define &lt;code&gt;AUTHORIZED_PRINCIPALS&lt;/code&gt; and &lt;code&gt;ADMIN_PRINCIPALS&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ACTION_AUTH_MODE=enforce&lt;/code&gt; (production):

&lt;ul&gt;
&lt;li&gt;Read-only actions (&lt;code&gt;generate_brief&lt;/code&gt;, &lt;code&gt;generate_brief_with_web&lt;/code&gt;): always allowed&lt;/li&gt;
&lt;li&gt;Mutating actions: caller must match &lt;code&gt;AUTHORIZED_PRINCIPALS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Admin actions (&lt;code&gt;approve&lt;/code&gt;): caller must match &lt;code&gt;ADMIN_PRINCIPALS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mismatch → &lt;strong&gt;403 Forbidden&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Enforced Human-in-the-Loop (HITL)
&lt;/h3&gt;

&lt;p&gt;High-risk operations are gated by a DynamoDB approval store:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;request_approval&lt;/code&gt; → persists record as &lt;code&gt;pending_approval&lt;/code&gt; (enforced=true)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;approve&lt;/code&gt; → admin transitions to &lt;code&gt;approved&lt;/code&gt; (ConditionExpression prevents race)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute_approved&lt;/code&gt; → &lt;strong&gt;only executes if record is &lt;code&gt;approved&lt;/code&gt;&lt;/strong&gt;; otherwise 409&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Verified live: execute pre-approval → 409, post-approval → 200, re-execute → 409 (no replay).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: Approval records have a &lt;strong&gt;7-day TTL&lt;/strong&gt; (DynamoDB Time-to-Live). Stale pending approvals auto-expire, preventing indefinite accumulation of unreviewed requests. Expired records cannot be approved or executed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Additional Controls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection defense&lt;/strong&gt;: Both &lt;code&gt;generate_brief&lt;/code&gt; and &lt;code&gt;generate_brief_with_web&lt;/code&gt; treat context as untrusted data with explicit delimiter boundaries (&lt;code&gt;&amp;lt;internal_context&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;web_search_results&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web query safety&lt;/strong&gt;: Only &lt;code&gt;params.web_query&lt;/code&gt; or &lt;code&gt;params.title&lt;/code&gt; is sent to Web Search — never internal document content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw SQL disabled by default&lt;/strong&gt;: &lt;code&gt;ALLOW_RAW_SQL=false&lt;/code&gt;; role-level data boundaries enforced via Lake Formation (LF-TBAC) in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Results bucket hardening&lt;/strong&gt;: PublicAccessBlock + TLS-only + 30-day lifecycle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API throttling&lt;/strong&gt;: Rate/burst limits against denial-of-wallet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Search citation obligation&lt;/strong&gt;: Source URLs + titles are always included in responses (Acceptable Use Policy compliance)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Web-Augmented Brief Generation (opt-in)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;GA at AWS Summit NYC 2026 (June 17, 2026). Powered by AgentCore Web Search Tool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Business briefs based solely on internal documents lack current market context. A sales brief about a product launch needs both the internal product spec &lt;em&gt;and&lt;/em&gt; awareness of relevant public announcements published recently. A legal compliance brief needs both the internal policy document &lt;em&gt;and&lt;/em&gt; the latest regulatory guidance.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;A new action &lt;code&gt;generate_brief_with_web&lt;/code&gt; combines internal context with real-time web search results. The internal context remains the primary source; web results are supplemental, cited, and treated as untrusted input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"generate_brief_with_web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"params"&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q3 Data Protection Regulatory Update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Internal operations follow FISC safety standards..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"web_query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data protection regulation 2026 Japan financial services"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Response
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&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;"generate_brief_with_web"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Q3 Data Protection Regulatory Update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brief"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Based on internal FISC compliance documentation... Additionally, [Web: FISC 2026 Revision Summary](https://example.com/fisc) published on 2026-06-10 introduces..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"web_citations"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/fisc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FISC 2026 Revision Summary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"publishedDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-10"&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;"web_search_enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"guardrail_applied"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Design Properties
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&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;&lt;strong&gt;Internal context priority&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal documents are the primary source; web supplements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Graceful degradation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web Search failure → behaves like &lt;code&gt;generate_brief&lt;/code&gt; (internal only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Citation separation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal sources and web sources are visually distinct in the brief&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query safety&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only the &lt;code&gt;web_query&lt;/code&gt; (or &lt;code&gt;title&lt;/code&gt;) is sent externally — never internal content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-region&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gateway in us-east-1 (Web Search Tool constraint); adds ~100-200ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authorization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read-only action (same tier as &lt;code&gt;generate_brief&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt injection defense&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web results wrapped in &lt;code&gt;&amp;lt;web_search_results&amp;gt;&lt;/code&gt; as untrusted data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Activation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sam deploy &lt;span class="nt"&gt;--parameter-overrides&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;EnableWebSearch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;AgentCoreGatewayId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;gateway-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;AgentCoreGatewayRegion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Without these parameters, &lt;code&gt;generate_brief_with_web&lt;/code&gt; still works but produces internal-only briefs (graceful degradation).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Verification Findings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lake Formation + Athena
&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%2F9oksuqa5wmygv9o6ny04.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%2F9oksuqa5wmygv9o6ny04.png" alt="Athena recent queries showing UC30 quick-workspace queries" width="799" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Athena queries running against Glue tables backed by S3 AP data — the foundation for Quick Sight analytics.&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%2Fgha9svhpx9xrhjg3ikgv.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%2Fgha9svhpx9xrhjg3ikgv.png" alt="CloudFormation stack deployed for UC30" width="799" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The UC30 CloudFormation stack with all resources (API Gateway, Lambda, DynamoDB ApprovalsTable, Athena WorkGroup) deployed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The test account had Lake Formation governing the Data Catalog. The Athena Query Lambda's execution role required &lt;strong&gt;Lake Formation permission grants&lt;/strong&gt; (DESCRIBE on DB, SELECT/DESCRIBE on tables) in addition to IAM. Production deployments should design LF-TBAC for role-based data visibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Amazon Quick × FSx for ONTAP S3 AP Integration Boundary
&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%2Fpnv0byog65u1pkl7wr0v.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%2Fpnv0byog65u1pkl7wr0v.png" alt="Quick S3 Knowledge Base connection attempt with FSx for ONTAP S3 AP alias" width="799" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Amazon Quick's S3 KB connector accepts the S3 AP alias but authorization fails due to FSx for ONTAP's dual-layer auth — leading to the recommendation below.&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%2Fahj08qvya1cxlg6bs68v.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%2Fahj08qvya1cxlg6bs68v.png" alt="Quick Knowledge integrations panel" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Quick provides multiple data integration paths — for FSx for ONTAP data, Bedrock KB (UC29) or Athena-mediated access is the validated route in this repository.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Amazon Quick's S3 knowledge base connector accepts an FSx for ONTAP S3 AP alias as a "valid URL," but connection verification fails with &lt;em&gt;"You do not have permissions to access the S3 bucket."&lt;/em&gt; This is &lt;strong&gt;not&lt;/strong&gt; a missing IAM permission that can be simply added — it is a structural authorization path mismatch. Three factors block the standard connector path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ARN format mismatch&lt;/strong&gt; — FSx for ONTAP S3 APs use &lt;code&gt;arn:aws:s3:{region}:{account}:accesspoint/{name}&lt;/code&gt; (not the &lt;code&gt;arn:aws:s3:::{bucket}&lt;/code&gt; format). The connector's internal IAM evaluation likely targets the alias as a bucket name, which does not match IAM policy evaluation for the AP ARN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AP resource policy rejects the principal&lt;/strong&gt; — Adding Quick's data access role to the S3 AP resource policy returns &lt;code&gt;MalformedPolicy: Invalid principal in policy&lt;/code&gt;, indicating a principal constraint on FSx for ONTAP S3 AP policies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 2: filesystem identity&lt;/strong&gt; — Even if IAM layers were resolved, the ONTAP file-system identity (UNIX UID or AD user) associated with the S3 AP must independently permit read access to the target files.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For readers attempting this path&lt;/strong&gt;: Use CloudTrail to capture the exact &lt;code&gt;AccessDenied&lt;/code&gt; event and identify Quick's calling principal ARN. If the &lt;code&gt;MalformedPolicy&lt;/code&gt; constraint can be resolved (potentially by configuring the S3 AP with an AD-based identity rather than UNIX root), the direct path may become viable. As of June 2026, &lt;strong&gt;Bedrock Knowledge Base → FSx for ONTAP S3 AP&lt;/strong&gt; is the confirmed working route for RAG ingestion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Evidence-based implementation guidance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FSx for ONTAP → RAG&lt;/strong&gt;: Bedrock KB (UC29) is the validated path in this repository&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick Index&lt;/strong&gt;: Stage to a standard S3 bucket for predictable connector behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick Sight (BI)&lt;/strong&gt;: Athena-mediated access works (QuickSight role needs Athena/Glue/LF/results-bucket permissions)&lt;/li&gt;
&lt;li&gt;Direct Quick → FSx for ONTAP S3 AP (standard connector path): &lt;strong&gt;validated — does not work&lt;/strong&gt; (tested with UNIX root identity; &lt;code&gt;MalformedPolicy&lt;/code&gt; on AP resource policy). AD-based S3 AP identity configuration is tracked as a future hypothesis&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Glue Tables
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;analytics/&amp;lt;role&amp;gt;/&lt;/code&gt; CSVs are pointed to by Glue tables (&lt;code&gt;sales_pipeline&lt;/code&gt; / &lt;code&gt;it_incidents&lt;/code&gt;) created via Athena DDL. LOCATION uses S3 AP alias format: &lt;code&gt;s3://&amp;lt;alias&amp;gt;/quick-workspace/analytics/&amp;lt;role&amp;gt;/&lt;/code&gt;. For scale, migrate to Parquet + partitioning to reduce Athena scanned costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data Classification
&lt;/h2&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;Classification&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;Action API response (generate_brief)&lt;/td&gt;
&lt;td&gt;INTERNAL&lt;/td&gt;
&lt;td&gt;Source-derived summary; no external disclosure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action API response (generate_brief_with_web)&lt;/td&gt;
&lt;td&gt;INTERNAL&lt;/td&gt;
&lt;td&gt;Contains internal citations; web portion is PUBLIC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action API response (create/approve/execute)&lt;/td&gt;
&lt;td&gt;INTERNAL&lt;/td&gt;
&lt;td&gt;Business operation records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena query results (results bucket)&lt;/td&gt;
&lt;td&gt;INTERNAL&lt;/td&gt;
&lt;td&gt;Encrypted + 30-day lifecycle + TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB ApprovalsTable&lt;/td&gt;
&lt;td&gt;INTERNAL&lt;/td&gt;
&lt;td&gt;Approval state metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNS notifications&lt;/td&gt;
&lt;td&gt;INTERNAL&lt;/td&gt;
&lt;td&gt;Action summaries only; no file content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Search results (raw)&lt;/td&gt;
&lt;td&gt;PUBLIC&lt;/td&gt;
&lt;td&gt;External public information&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Extend &lt;code&gt;shared/data_classification.py&lt;/code&gt; for regulated workloads (CUI / FISC / HIPAA).&lt;/p&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Monthly 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;Amazon Quick&lt;/td&gt;
&lt;td&gt;Per-user/plan billing&lt;/td&gt;
&lt;td&gt;Separate; unsubscribe when done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena&lt;/td&gt;
&lt;td&gt;Scanned-data pricing&lt;/td&gt;
&lt;td&gt;Reduce with Parquet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda / API Gateway&lt;/td&gt;
&lt;td&gt;Serverless pay-per-use&lt;/td&gt;
&lt;td&gt;&amp;lt; $10 for moderate usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock LLM (briefs)&lt;/td&gt;
&lt;td&gt;Usage-based&lt;/td&gt;
&lt;td&gt;Usage-based; verify the current model price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB (approvals)&lt;/td&gt;
&lt;td&gt;Pay-per-request&lt;/td&gt;
&lt;td&gt;Minimal for approval records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS Budgets alarm&lt;/td&gt;
&lt;td&gt;Free (SNS delivery cost only)&lt;/td&gt;
&lt;td&gt;Created when &lt;code&gt;NotificationEmail&lt;/code&gt; set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AgentCore Web Search (opt-in)&lt;/td&gt;
&lt;td&gt;Per-query pricing (see &lt;a href="https://aws.amazon.com/bedrock/agentcore/pricing/" rel="noopener noreferrer"&gt;AgentCore pricing&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Gateway invocation pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-region transfer (opt-in)&lt;/td&gt;
&lt;td&gt;&amp;lt; $0.02&lt;/td&gt;
&lt;td&gt;us-east-1 ↔ ap-northeast-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Teardown / rebuild: one-command idempotent scripts (&lt;code&gt;scripts/teardown-uc29-uc30.sh&lt;/code&gt; / &lt;code&gt;scripts/rebuild-uc29-kb.py&lt;/code&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Yoshiki0705/FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns.git
&lt;span class="nb"&gt;cd &lt;/span&gt;FSx-for-ONTAP-S3AccessPoints-Serverless-Patterns/solutions/genai/quick-agentic-workspace

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt        &lt;span class="c"&gt;# or: uv pip install -r requirements.txt&lt;/span&gt;

&lt;span class="nb"&gt;cat &lt;/span&gt;samconfig.toml.example  &lt;span class="c"&gt;# Review parameters&lt;/span&gt;

sam build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sam deploy &lt;span class="nt"&gt;--guided&lt;/span&gt;

&lt;span class="c"&gt;# DemoMode=true runs without FSx for ONTAP (regular S3 bucket)&lt;/span&gt;

&lt;span class="c"&gt;# Optional: Enable Web Search hybrid RAG&lt;/span&gt;
sam deploy &lt;span class="nt"&gt;--parameter-overrides&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;EnableWebSearch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;AgentCoreGatewayId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;gateway-id&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nv"&gt;AgentCoreGatewayRegion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Governance Note
&lt;/h2&gt;

&lt;p&gt;This article is technical architecture guidance, not legal, compliance, or regulatory advice. Amazon Quick features, pricing, regional availability, and connector behavior are subject to change — verify with official documentation and your own account settings. S3 AP data source boundaries are at volume/prefix granularity. For per-user visibility control, use Quick's document-level ACL or Custom Permission-Aware RAG. Web Search Tool usage requires compliance with the Acceptable Use Policy (source citations must be retained and displayed in end-user output).&lt;/p&gt;




&lt;p&gt;Yoshiki Fujiwara&lt;/p&gt;

</description>
      <category>aws</category>
      <category>amazonquick</category>
      <category>amazonfsxfornetappontap</category>
      <category>s3accesspoints</category>
    </item>
  </channel>
</rss>
