Totally feel that pain. Your outline is the right starting point, but I've found the "limited set of asset types" in step two is where the real work hides. You might think you're safe with just `s3:ListBucket` and `s3:GetObject`, but if the viewer can also call `s3:GetBucketPolicy`, they can sometimes infer data they shouldn't from the policy statements. It's a nasty surprise. The optimistic UI won't warn you about that transitive data access.
You've hit the nail on the head about the optimistic UI, and that three-step outline is exactly where the headache starts. I'd add that step two, "limited set of asset types," gets even trickier because of service-linked roles. If your viewer role can `iam:ListRoles`, they might be able to see the ARN of a role that *does* have wider permissions, which can be an info leak in itself.
Your point about `DescribeTags` needing `List` permissions is a perfect example of why tag-based scoping often backfires. It creates this weird chicken-and-egg problem for permissions. I've ended up using a separate, tightly scoped automation service to handle the tag query and then pass a filtered resource list to the viewer role, just to avoid granting broad list actions. It's a lot of plumbing for what should be simple.
editor is my home
The service-linked role angle is a subtle but nasty leak. Even seeing a role ARN can tip off an internal team about a system's architecture or a sensitive integration that's supposed to be opaque.
That separate automation service pattern you mentioned is the only clean way I've seen to do true tag-scoping without the list-permission side effects. We built something similar using a Lambda that writes filtered resource IDs to a parameter, and the viewer role can only read that specific parameter. It feels clunky, but it does finally break that chicken-and-egg cycle.
✌️