IAM¶
IAM controls who can do what in your account: console principals (users + groups + policies), data-plane Roles/Policies, and Access Keys used by CLI/SDK/Terraform.
| Item | Value |
|---|---|
| Console | IAM → Users (create / edit) · Account → Users (read-only preview) |
Two layers (do not mix them up)¶
1. Console access (people) — Groups + policies (ADR-053)¶
People get permissions from policy attachments and group membership, not from a four-role matrix. The console Access column shows those attachments (any group or policy). account_role remains in the API for compatibility and is not used for display.
The four system groups still exist and can be attached like any other group:
| System group | Managed policy | Typical use |
|---|---|---|
AccountOwners |
SystemAccountOwners (Action: *) |
Full account administration; new catalog actions apply automatically |
AccountAdmins |
SystemAccountAdmins (Action: *) |
Admin pack; new catalog actions apply automatically |
Builders |
SystemBuilders |
Build / operate workloads (service:* when that service is fully granted) |
Readers |
SystemReaders |
Read-only |
Older ConsoleSystem* policy names and ARNs still resolve after rename. The * is stored on the policy version row (authorize reads that document). The console does not rewrite a long action list into * only for display. Owner-only console actions (delete account, transfer ownership) stay gated by the owner role.
Fine-grained custom groups and policies live under IAM → Groups / Policies. Evaluation is Deny > Allow > implicit Deny. Granting to others requires CanDelegate: a subset of concrete actions you can perform. Owner/admin packs store Action: * for perform (new catalog actions apply automatically). That star does not let an admin hand out * or so:* — only an owner (or someone who already holds that wildcard) may grant wildcards.
Create users under IAM → Users (email invite or username/password). Grants at creation are optional — a user with none authenticates but has no meaningful access until you attach groups or policies. Open a user to manage Groups and Direct policies as tabs. The breadcrumb shows the username, not the internal user id. Each tab has an add control at the top and a table of current memberships or attachments below. Search filters that table (and the add picker). Use IAM → Diagnostics to debug Allows: simulator first, then effective access cards by service.
Open a group for a members table like IAM Users: username, email, Access, status, and a ⋮ menu (open the user, or remove them from the group). Multi-select plus Remove from group works on custom groups and built-in packs. A remove from Builders (or any pack) stays removed — the console no longer puts the user back from account_role on the next request.
Account root¶
The account root has implicit full access (no policy document required). You do not need to put root in any group.
| Action | Allowed? |
|---|---|
| Disable, delete, or change role | No |
| Attach or detach a user policy | No — 403 identity.root_permissions_immutable |
| Add or remove from a group | Yes |
The user page hides attach/detach for root. Existing attachments stay visible and read-only.
The visual policy builder accumulates statements across services (it does not replace the document when you switch service). Opening a policy never mutates it; Visual ↔ JSON tab switches never generate a new document. The console groups the document by service for display even when one statement lists many services. Permission cards sit in a four-across grid; click anywhere on a card to open that slice.
Create policy opens a bottom drawer. Name is at the top. Add from is one picker (managed templates and your policies); choosing an item merges those statements into what you already added — it does not replace manual work and does not duplicate an identical statement. Same effect + same resource unions actions; same effect + same actions unions resources. Different action/resource pairs stay as separate statements so a merge never grants the Cartesian product (Get on bucket A plus Put on bucket B does not become Get+Put on both). A broader Allow (so:* on *) drops a narrower one. Choosing All for a service (mq:*) replaces every other Allow for that service. Deny never merges with Allow. Create stays disabled until the policy has a name and at least one permission. Selected permissions stay in a compact service list (capability tags such as Read / Write — not every action). That list scrolls on its own — it does not scroll the whole sheet. On a phone the cards sit at the bottom and scroll sideways. Click a service to edit that slice: effect, multiple resource rows, actions, and optional slice JSON (Sid / Condition). All writes {service}:* and disables the individual checkboxes; turning it off clears them so you pick specific actions. Resource fields are free text with catalog suggestions as you type (no # required). Editing a mixed statement updates only that service; a resource or effect change splits it out so siblings stay untouched.
Workspace catalog¶
The console lists services from GET /api/v1/accounts/{id}/catalog (released catalog ∩ IAM). Unreleased products are omitted. A released service you are not granted opens as an empty workspace — not an Access Denied screen.
2. Data-plane IAM (automation)¶
| Object | Purpose |
|---|---|
| Policies | Documents of service:Action + resource ARNs (Deny wins over Allow) |
| Roles | Assumeable identities (Functions use a Role ARN as execution_role) |
| Access Keys | Long-lived credentials for a user; permissions come from attached policies / groups |
Account IDs in ARNs are the 12-digit account number.
Access Keys (credentials-first)¶
Create and rotate under IAM → Users (the key belongs to the user). Admin inventory: IAM → Access Keys. Full how-to: Access Keys.
User Access Keys can call management APIs (list buckets, queues, …) with SigV1 when policies Allow — no console JWT required. Interactive homecloud login is for the browser and rare CLI step-up. See CLI authentication and Authentication architecture.
Roles can also trust GitHub Actions OIDC (Principal.Federated + Condition on sub / aud). Terraform then uses HC_ROLE_ARN instead of a long-lived Access Key. Assumed-role sessions can Create/Delete/Get queues, buckets, and secrets; other console routes return 403 iam.management_role_not_enabled. See Terraform — GitHub OIDC.
Policies¶
- Open Policies → create or clone a managed starter.
- Useful starters often include patterns like MQ consumer, SO read/write, Function basic execution.
- Attach the policy to a user, group, or role.
Example SO deploy policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["so:ListBucket", "so:PutObject", "so:DeleteObject"],
"Resource": [
"arn:holab:so:::my-website",
"arn:holab:so:::my-website/*"
]
}
]
}
Roles and Functions¶
Functions must use an execution Role ARN (not an Access Key name).
- Create a Role with the permissions the function needs (SO, MQ, Secrets, …).
- Ensure the trust policy allows the Functions service account to assume it (
arn:…:service-account/functionsstyle principal — copy the exact principal from the console helper if shown). - Set that Role ARN on the function configuration.
Diagnostics¶
Open IAM → Diagnostics.
- Simulator (top) — ask whether
service:Actionis allowed on a resource ARN without performing it. - Effective access (below) — cards by service with Read / Write / Create chips. Click a chip or
+Nfor exact action names.
The simulator answers the question you arrived with. The cards summarize what is attached after Deny wins.
Security settings¶
Under IAM / Account security: - MFA / passkeys for console login - Session devices and revoke