# Cloud IAM Import

> Import IAM users, groups, and policies from a connected AWS or GCP account into ZopNight users, teams, and roles; review-before-apply.

Source: https://zop.dev/docs/zopnight/cloud-setup/iam-import

---

If you've already organised your cloud accounts around IAM users, groups, and policies, IAM Import skips you past the tedious step of recreating that structure in ZopNight. The wizard previews every user, group, and policy attachment AWS or GCP has and proposes a ZopNight equivalent for each one. Nothing is written until you click **Apply**.

## What gets imported

| Cloud concept | ZopNight equivalent |
|---|---|
| AWS IAM User | ZopNight User |
| AWS IAM Group | ZopNight Team |
| AWS managed policy | ZopNight Role (mapped via curated translation table) |
| AWS custom policy | Lands in Manage IAM → Needs Attention until you map it |
| GCP user member | ZopNight User |
| GCP Google Group member (optional) | ZopNight Team |
| GCP predefined role | ZopNight Role (mapped via curated translation table) |
| GCP custom role | Lands in Needs Attention until you map it |

**Note**

Service accounts on GCP are imported as inventory (you can see them under Resources) but **not** as ZopNight users. GKE Workload Identity creates hundreds of SA-only custom roles per cluster; importing them would flood Manage IAM with unactionable rows. ZopNight reports the dropped count so operators can verify the heuristic is firing as expected.

## The wizard

Two-phase: passive inventory + admin-triggered identity import. The passive layer keeps cloud-side IAM principals in your resource inventory on every discovery cron; no admin action needed. The identity layer creates ZopNight users, teams, and roles only when you click Apply.

**Open Manage IAM**

  Settings → Cloud accounts → the AWS or GCP card → **Import IAM** (pre-import) or **Manage IAM** (post-import). The footer timestamp tells you when the cloud account was last connected.

**Preview**

  The Preview tab runs the suggestion staircase: every IAM user / group / policy attachment with a proposed ZopNight equivalent. Emails for AWS users come from the AWS `email` tag.

**Resolve Needs Attention**

  Custom policies / roles that didn't auto-map land here. You map them to ZopNight policies one at a time. Team bindings for these custom roles are pre-positioned at import time; once you map the policy, the bindings activate immediately, no re-import.

**Apply**

  Click Apply. ZopNight creates the ZopNight users, teams, roles, role-to-policy assignments, and team memberships based on your resolutions. Every imported entity carries provenance metadata (see [Idempotency](#idempotency) below).

## What's never auto-granted

This is the part that matters for security review. ZopNight's translation table is **deliberately conservative**:

**Warning**

Org-management policies (`role:*`, `user:*`, `assignment:*`, `organisation:*`) are **never** auto-granted from cloud IAM; even for `AdministratorAccess` or `IAMFullAccess` on AWS. Granting these requires explicit admin action in Settings → Roles after the import lands.

The reason: cloud IAM often has admin-shape policies attached liberally (an `iam_role` Terraform module from five years ago, a "platform admin" group that grew to 30 people). Auto-promoting those into ZopNight org-management policies would silently widen access. The wizard requires you to look at each one.

Type-scoped policies (`resource:view:s3`, `resource:delete:lambda`) are auto-granted from service-specific cloud roles. Those are safe to translate because they preserve the cloud-side scope.

## AWS users without an email tag

A common edge case. AWS IAM users don't have a built-in email field. ZopNight reads it from a `email` tag on the user. If a user has no `email` tag, the wizard skips them at Apply time and surfaces them in Manage IAM → **Missing emails**.

You have two ways to resolve:

- **One at a time**: type the email into each row, click Save
- **Bulk**: download the CSV template (`cloud-username,email`), fill it in, upload

The Apply that follows runs a **server-side catch-up**: for every newly-resolved user, ZopNight looks up the cloud roles and groups they were attached to in AWS, and emits the missing role and team-membership assignments; against ZopNight roles **already imported by the original wizard run**. So the user lands in the org with the bindings they were supposed to get the first time.

The catch-up never creates new ZopNight roles. Roles imported during the original wizard remain the universe of options; cloud roles that weren't imported initially stay in Needs Attention.

## GCP IAM Import

Same wizard, same review-before-apply contract. Differences from AWS:

| Behaviour | AWS | GCP |
|---|---|---|
| **Scope** | Account | Project |
| **Inventory call** | Per-region IAM listing | Single `ListAssets` with `ContentType=IAM_POLICY` |
| **Bindings outside project scope** | n/a | **Excluded**: org-level and folder-level bindings are not imported. A user granted `roles/owner` at the org but never at the project will not appear. Grant the role at the project too, or invite them manually. |
| **Group members** | n/a | Optional; requires `roles/cloudidentity.groups.reader` at the org level. Without it, imported teams come in with zero members. The wizard surfaces a copy-paste `gcloud` command to grant it. |
| **Service account dropping** | Kept | Custom roles whose principals were all `serviceAccount:` get dropped at import, and the dropped-role count is reported for that import. |

The GCP translation table maps ~58 predefined roles (`roles/owner`, `roles/storage.admin`, `roles/iam.workloadIdentityUser`, …) following the same admin-collapse pattern: `roles/owner` → built-in Admin, predefined viewer-style roles → Viewer.

## Idempotency

Every imported entity records the cloud-native identifier it came from (the AWS ARN or GCP resource path). A second wizard run finds existing entities by this identifier and reuses them instead of duplicating.

| Change in cloud directory | What happens on re-import |
|---|---|
| User renamed in AWS | ZopNight user keeps the same `id`, ARN lookup still hits |
| User renamed in ZopNight | Re-import preserves the ZopNight name, ARN lookup still hits |
| Group renamed in AWS | Same. ARN is the key |
| User deleted in AWS | Re-import leaves the ZopNight user unchanged. Use Disconnect to remove. |

For users / teams / roles that should exist in multiple connected accounts (a single shared role), the `linkedFrom` array preserves the multi-account link. Disconnecting one CA removes only its contribution; the row stays if other CAs still reference it.

## Disconnect

Disconnecting from the wizard (or from the cloud-account card's Remove flow) soft-deletes everything imported from that cloud account and clears the matching role and team-membership assignments.

The IAM import view is refreshed on cloud-account delete, so reconnecting and re-importing produces a clean state immediately.

## RBAC for the wizard

No new RBAC policy. The wizard gates on existing policies:

- **Read (Preview)**: `cloud-account:view`
- **Write (Apply, resolve Needs Attention, Disconnect)**: `cloud-account:update`

If your role doesn't include these, the wizard renders the "Access restricted" placeholder.

## Where to verify after import

After Apply, check three places:

- **Settings → Users**: imported users with `Imported from cloud` badge
- **Settings → Teams**: imported teams with member counts
- **Settings → Roles**: imported roles with their policy sets

The audit log captures every row written, with the originating cloud account ID and ARN, so SOC-2 reviewers can trace any imported entity back to its cloud-side origin.
