Part 1 built the foundation of a file portal on top of Amazon FSx for NetApp ONTAP (FSx for ONTAP) S3 access points. Part 2 added ransomware response and WORM retention. Part 3 put AI agents on it.
Those parts were screens for people who handle files. From here the subject is the people who operate the storage itself. Volume create and resize, FlexCache, FlexClone, SnapMirror, snapshot policies, QoS, quotas, SMB shares. The portal now reaches 182 operations.
The readers I have in mind are infrastructure owners running FSx for ONTAP, and developers building this kind of admin screen with Amplify Gen2. ONTAP terms get a one-line gloss on first use, so ONTAP operations experience is not assumed. You can start here without Parts 1-3.
What this part covers is what had to be decided to put operations on a screen: the design of delegation and record, and the treatment of buttons that cannot be pressed and buttons that were never built.
What this part does not cover:
Up front:
The repository is solutions/amplify-portal/.
| Item | Value |
|---|---|
| Date | 2026-09-02 (JST) |
| Region | ap-northeast-1 |
| ONTAP version | 9.18.1P3D1 |
| Portal | Amplify Gen2 sandbox: AppSync + Cognito + VPC Lambdas |
| Lambda runtime | Python 3.13 |
| How the action count was taken | |
scripts/portal_action_types.py --check and scripts/check_portal_action_params.py |
|
| Cleanup | Test sandboxes deleted. One working stack retained |
First, the premise. Without routing through an additional third-party SaaS, three management interfaces reach FSx for ONTAP.
| Interface | Path | Reach |
|---|---|---|
| AWS Management Console / FSx API | IAM | The AWS-side operations on file systems, SVMs and volumes; backups |
| ONTAP CLI (SSH) | SSH to the management endpoint | Roughly an ONTAP cluster administrator |
| ONTAP REST API | HTTPS to the management endpoint | The CLI's operations, programmatically |
The sources, and how anything outside this list is treated, are in Management interfaces. The CLI and REST management endpoints are reachable only from inside the VPC or through a Transit Gateway peered network.
One consequence follows directly. Consider an everyday request: add a volume, or raise one quota. Answering it needs fsxadmin
-equivalent credentials and a path into the VPC. So every request lands on the infrastructure owner, and none of it can be handed to anyone else.
That separation is what the portal does.
storage-admin
) decides who may run what, per operation. No SSH key and no ONTAP credential is handed out. The credential lives in Secrets Manager and only the Lambda reads itDescribing the portal's benefit as "no VPN needed" would be wrong, though. The Lambda that calls the ONTAP REST API is inside the VPC. The path did not disappear; what holds it moved from a person to a Lambda. That distinction affects operational design, so the repository documentation words it the same way.
The portal reaches 182 actions across 12 endpoints. That number is not maintained by hand. A script reconciles the generated types against the handlers and prints it.
$ python3 scripts/portal_action_types.py --check
DISPATCH ACTION TYPES: PASS (182 actions declared, matching the handlers)
$ python3 scripts/check_portal_action_params.py
PORTAL ACTION PARAMS: PASS (187 literal call sites, 182 actions
across 12 endpoints, 1 call(s) not statically readable)
The reason it is not counted by hand is not that the number grew. Types do not cross the boundary between the screen and the Lambda. The portal's dispatch takes an untyped params
, so code that sends {snapshotName, retentionDays}
to an action reading snapshotId
and expiryTime
compiles, lints, renders a button, and fails on every click. That shipped. Now CI fails until the reconciliation agrees.
By domain.
| Domain | Operations |
|---|---|
| Volume lifecycle | Create / resize / delete / bring online, for both FlexVol and FlexGroup |
| FlexGroup capacity rebalance | Read state / start / stop / schedule |
| FlexCache | Create / list / resize / write-back toggle / delete |
| FlexClone | Create / list / split |
| SnapMirror | Create / list / transfer history / update now / quiesce / resume / break / resync / abort / delete |
| Snapshots | List / create / delete / lock / policy create / assign / enable locking |
| SnapLock and WORM | Read configuration / change retention, S3 Object Lock retention |
| ARP/AI (ransomware protection) | State change / bulk enable / suspect list and clear / surge parameters |
| Containment | Block NFS IP / block SMB user / disconnect sessions / list active blocks |
| QoS | Policy create / update / delete / assign to and release from a volume |
| Quotas | Rule create / update / delete / usage report / per-volume enforcement |
| SMB | Share create, update, delete; local users and groups; members; name mapping |
| Qtrees | Create / update / rename / delete |
| Export policies | Policy and rule create / delete |
| FPolicy and Vscan | Policy and event create / enable / delete |
| Peering | Cluster peer and SVM peer create / accept / delete, intercluster LIFs |
| Cluster inventory | Nodes / licenses / LIFs / protocols / DNS / jobs, EMS events |
| S3 | Bucket list, Object Lock status and retention |
182 is not every ONTAP operation. Operations that need advanced privilege are absent from the REST API, so they cannot appear on this screen.
The containment operations for ransomware response (block an NFS IP, block an SMB user, disconnect sessions) cannot run without a target. Previously the button simply went disabled
— and it did not look disabled either. Pressing it did nothing.
On a screen someone reads during an incident, a button that does nothing is indistinguishable from a broken one. Each button now states what the operation does and what is still missing, and lists the inputs it needs. There is also a note that the grey text is a placeholder and not a value.
Admin features are not hidden from users who lack the permission. They are greyed out with a "storage-admin required" badge. What the screen can do stays visible, and AppSync refuses the call if it is attempted.
The copy limit is the clearest case. It is easy to confuse with the upload limit, so the measured values are split out.
| Operation | Limit | Measured |
|---|---|---|
Upload (single PutObject ) |
||
| 5 GiB | ||
MaxSizeAllowed = 5,368,709,120 |
||
| Upload (multipart, whole object) | 50 GiB | |
53,687,091,200. Checked only at CompleteMultipartUpload |
||
Download (GetObject ) |
||
| No limit | Objects over 50 GiB were retrieved | |
Copy (single CopyObject ) |
||
| 5 GiB | As above | |
Copy (UploadPartCopy ) |
||
| Unusable | ||
Documented as Supported; every form returned NoSuchKey |
||
Uploads do reach 50 GiB (measured with multipart). Copies, though, have no path past 5 GiB. UploadPartCopy
is listed as Supported in Access point compatibility and returned NoSuchKey
in every form measured. Rename, move, copy, trash and restore in the portal are all CopyObject
, so that 5 GiB becomes the screen's limit.
The units caught me out as well. The AWS documentation's "5 GB" and "50 GB" both turned out to be binary when measured. MaxSizeAllowed
from PutObject
is 5,368,709,120 = 5 × 1024³, not the decimal 5,000,000,000. The detail is in the object size limit verification.
Rather than surfacing an S3 error halfway through, the portal checks the size before the copy and refuses with a reason.
{key}
is 6.2 GiB. A single copy is limited to 5 GiB, and the multipart copy that would lift the limit is not usable on this Access Point, so this operation cannot be completed from the portal. Move the file over NFS or SMB instead.
The guard's 5 GiB threshold is the same value as the measured MaxSizeAllowed
.
On the same reasoning, no button was built for converting a FlexVol to a FlexGroup. The conversion is ONTAP CLI advanced-privilege only, with no REST equivalent. Instead the screen carries the prerequisites, the irreversibility, the treatment of existing snapshots, and what AWS recommends — a copy into a new FlexGroup with AWS DataSync rather than an in-place conversion. Writing down that something is absent, and why, is more useful than a button that cannot be pressed.
The free-space estimate for a clone split (volume clone split estimate
) is the same. It exists only in the CLI, so it cannot appear here. Rather than omitting it silently, the screen says why it is missing.
For operations that cannot be undone — SnapLock, snapshot locking — a confirmation dialog does not cover it, because the person pressing it does not know what becomes undeletable. The screen says this:
- The SnapLock type (compliance) can only be set at creation and cannot be changed or removed afterwards
- Under compliance, nobody can delete until retention expires. Not the account administrator, and not AWS
- While one unexpired WORM file remains, this volume, its SVM, and the file systemcannot be deleted- While the file system cannot be deleted, it keeps billing
- A newly created empty volume can still be deleted. It stops being deletable when the first file is committed to WORM
The last two lines are the point. The effect of an irreversible operation does not stop at the resource; it propagates to the parent and to the bill. A test environment is the worst place to put a file system you cannot delete for six months.
Snapshot locking is written up with its asymmetry intact. A lock can only be extended, never shortened or released. The policy side is reversible: removing the retention, switching policies, or detaching stops new locks. But snapshots already locked stay until they expire. Compressing that asymmetry into one sentence makes one half of it wrong.
A retention count does not act as a ceiling either. Locked snapshots cannot be rotated out, so they accumulate past the count until expiry and consume the space. That is on the screen too.
Irreversible operations carry a type-to-confirm (the keyword typed in capitals). The API also requires acknowledgeIrreversible
, so a call that bypasses the UI passes the same check.
What is written here is the technical blast radius of an operation. Which retention period satisfies your regulatory requirements is a legal and compliance judgement, and neither this article nor the portal stands in for it.
The order here is worth stating honestly. After writing the section above I ran the screen, and the lock indicator was broken.
ONTAP keeps two expiry fields on a snapshot. expiry_time
is the snapshot lock; snaplock_expiry_time
is the expiry that comes from a SnapLock volume. The portal writes a lock to the former, and the snapshot list was reading only the latter. So a snapshot locked from the portal was always shown as unlocked.
It surfaced because two panels disagreed about the same snapshot. The panel counting lock state said lockedSnapshotCount: 1
; the list said isTamperproof: false
. A state where one of them is wrong cannot be seen from either one alone. Both fields are read now, and four tests that fail against the old code were added.
The same run produced a measurement on blast radius. A locked snapshot does not block deletion of its volume. The delete was attempted and the volume left the list twenty seconds later. Unexpired WORM files under SnapLock block deletion all the way up to the file system; a snapshot lock is confined to that snapshot. That matched the screen's wording ("other snapshots and the volume itself are unaffected").
| # | What to write down |
|---|---|
| 1 | |
| Do not write "no VPN required." A Lambda holds the path into the VPC. An audit asks who can use that path, not whether it exists | |
| 2 | |
| Decide the unit of delegation per operation. Do not bundle volume creation and enabling SnapLock under one permission | |
| 3 | |
| In the description of an irreversible operation, include the parent resource and the bill. "This volume becomes undeletable" is not enough | |
| 4 | |
| Leave no unpressable buttons. Show the missing input. Where a feature is absent, say it is absent and why | |
| 5 | |
| If one fact is displayed in two places, add a test that reconciles them. While only one is being read, a broken display looks correct |
"Unverified" is not "impossible". The boundary of this article, stated as a boundary:
| Item | State |
|---|---|
| Behaviour under concurrent users | Unmeasured. Only single-operation latency was observed |
| Operations needing advanced privilege | Out of scope. Absent from the REST API, so they cannot be on the screen |
| Enabling SnapLock compliance from the screen | |
| Deliberately not done. Irreversible, and it would make a test file system undeletable for months | |
| Multi-tier delegation with more Cognito groups | Unverified. There is one tier, storage-admin |
| Long-term retention and search of the audit record | Unverified. The record is kept; no retention design was done |
Part 5 covers what the real cluster refused while these 182 actions were being built: why creating a FlexGroup on FSx for ONTAP always fails with the defaults, why a capacity rebalance never starts with ONTAP's defaults, and the two places the documentation and the running system disagreed.
Putting operations on a screen was not a matter of listing features. Three paths already reach the file system and none of them went away, so what a screen adds is delegation and record, not reach. Getting that backwards produces the claim that a VPN is no longer needed.
Half the screen's job turned out to be saying what cannot be done rather than showing what can. Name the missing input on a button that cannot be pressed, write down absent features with the reason, and give irreversible operations their blast radius. Where a limit is measurable — the 5 GiB copy — using the measured value directly in the guard keeps the screen and the storage saying the same thing.
And one display defect surfaced after the article was written. Until two panels were reconciled, the broken indicator looked correct.
The figures come from one specific environment and configuration and will differ with workload and setup. This article does not stand in for governance or regulatory judgement.