Skip to main content
Cross-cloud IAM import is only safe when it refuses to guess

Cross-cloud IAM import is only safe when it refuses to guess

Chandra Prakash Singh By Chandra Prakash Singh
Published: June 11, 2026 4 min read

A cross-cloud IAM import that maps every role on a best-effort basis is the most dangerous kind of governance tool. It looks complete. It produces a clean result with no warnings. And it quietly grants the wrong access, because the providers do not agree on what a permission means.

With ZopNight v1.17.0, Azure IAM Import now runs end-to-end, joining AWS and GCP. It translates both built-in and custom roles into ZopNight’s permission model, and it flags anything that cannot be mapped safely for human review. We call this flag-don’t-guess translation: the import refuses to approximate, because an approximated permission is worse than a missing one. The first preserves a false sense of correctness. The second is at least visible.

This matters because IAM is the one place where a silent error has no symptom until it has a breach. A mis-sized VM shows up on the bill. An over-granted role shows up only in an audit, or in an incident. So the import treats the un-mappable set as the product, not as an exception to sweep aside.

Built-in roles translate deterministically, custom roles are where ambiguity lives

Azure built-in roles are stable and well-documented. Their action sets are fixed by the provider, so they translate deterministically into ZopNight’s model. Reader maps to a read scope, Contributor to a write scope, and the mapping does not depend on guesswork. v1.16 already did this for AWS and GCP imports, flagging only the actions that could not be translated.

Custom roles are the hard part. A team writes a custom Azure role with provider-specific action strings, sometimes scoped to one resource group, sometimes with NotActions that subtract permissions. Those constructs do not have a clean one-to-one shape in AWS or GCP terms. This is where the import either guesses or flags, and ZopNight flags.

The mechanism is a classifier. Each role is decomposed into its actions. Actions with a known cross-cloud equivalent translate cleanly. Actions without one are routed to the flagged set for a human to decide. The role is not silently dropped and it is not silently expanded.

The decisive detail is that confidence is per action, not per role. A custom role with twelve actions might translate eleven cleanly and flag one. ZopNight imports the eleven and surfaces the one, rather than failing the whole role or accepting all twelve. This keeps the flagged surface as small as the data allows, which is what makes review tractable instead of overwhelming.

Cross-cloud IAM import is only safe when it refuses to guess - diagram

The translated set lands in your model immediately. The flagged set is where review effort concentrates, and that is by design.

Flagging beats approximating because the failure mode is invisible

Silent approximation has two failure modes, and both are invisible at import time. Over-granting widens a role to cover an action it should not have, which expands the cloud blast radius of a single credential. Under-granting drops a needed action, which breaks the workload later and far from the import.

Neither failure announces itself. That is the whole problem with a best-effort mapper. It cannot tell you which of its mappings it was confident about, because it threw the uncertainty away to produce a clean result.

Azure makes this sharper than AWS or GCP. Its NotActions model subtracts permissions from a grant, so a role is defined partly by what it removes. A mapper that ignores the subtraction will over-grant, because it copies the additive permissions and silently loses the limit that made the role safe. Flagging the NotActions it cannot reproduce is the only honest move.

Flag-don’t-guess keeps the uncertainty. The three-cloud trio now behaves consistently, which means a policy-aware governance layer can reason about all of it the same way.

CloudIAM import status (v1.17.0)What gets flagged
AWSEnd-to-end since v1.16Actions with no ZopNight model equivalent
GCPEnd-to-end since v1.16Actions with no ZopNight model equivalent
AzureEnd-to-end, new in v1.17.0Custom-role actions and NotActions that cannot map safely

Least privilege survives the import only if the flagged set gets reviewed

The governance promise is narrow and honest: least privilege survives the import, because nothing is widened to make the mapping succeed. A role that cannot be expressed safely stays flagged rather than rounded up to the nearest broad permission.

That promise has a failure condition. It works when most roles map cleanly and a person clears the flagged queue. It breaks when the flagged set is ignored, because then those identities sit in limbo, neither imported nor explicitly handled. The tool moved the risk into the open, but a human still has to close it.

So treat the flagged count as a work item, the same way you treat a SOC 2 control checklist or a tag governance backlog. Import the trio, read the flags, and resolve them before the roles go live.

A cross-cloud import that prints zero warnings is not the safe one. The safe one tells you exactly which permissions it would not translate on your behalf.

Chandra Prakash Singh

Written by

Chandra Prakash Singh Author

Chandra works on Zop.Dev's data plane and the audit / activity-log pipeline. He writes about event sourcing in cost systems, late-arriving facts, and how to design logs that survive a forensic review.

ZopDev Resources

Stay in the loop

Get the latest articles, ebooks, and guides
delivered to your inbox. No spam, unsubscribe anytime.