OCI IAM, Compartments & Policies
Master OCI identity domains, compartment hierarchies, policy syntax, and group-based access control.
Prerequisites
- OCI account with administrator access
- Basic understanding of IAM concepts
Identity and Access Management in OCI
Identity and Access Management (IAM) in Oracle Cloud Infrastructure controls who can access your cloud resources and what they can do with them. OCI's IAM model is unique among cloud providers because it is built around compartments and a human-readable policy language that makes access control intuitive and auditable. Instead of writing complex JSON documents, OCI policies read like plain English sentences.
Understanding IAM is foundational to everything you do in OCI. Every API call, console action, and CLI command is evaluated against IAM policies to determine whether the request is allowed or denied. A well-designed IAM structure with proper compartment hierarchy and least-privilege policies is the cornerstone of OCI security.
This guide covers identity domains, users, groups, compartment design, policy syntax, dynamic groups, federation, and best practices for enterprise-scale IAM management.
IAM with Identity Domains
OCI has evolved its IAM model. Newer tenancies use Identity Domains, which replace the legacy IAM system (now called IAM without Identity Domains). Identity Domains provide enhanced features including MFA, adaptive security, self-service password reset, social sign-in, and application integration. This guide covers the Identity Domains model. If your tenancy still uses legacy IAM, the compartment and policy concepts are identical, but user management differs slightly.
Identity Domains
An Identity Domain is a container for managing users, groups, applications, and authentication settings. Every tenancy has a default identity domain created automatically. You can create additional identity domains for different purposes such as separating development and production identities, isolating partner access, or managing different business units.
Identity Domains come in different types: Free, Oracle Apps, Premium, and External User. The Free tier includes one domain with up to 2,000 users and basic features. Premium domains add adaptive MFA, conditional access policies, and advanced reporting.
# List identity domains in your tenancy
oci iam domain list \
--compartment-id <tenancy-ocid> \
--query 'data[].{name:"display-name", type:"type", url:url, state:"lifecycle-state"}' \
--output table
# Get details about the default domain
oci iam domain get \
--domain-id <domain-ocid>
# Create a user in the default identity domain (via Console or SCIM API)
# Console: Identity & Security > Domains > Default > Users > Create User
# List users in a domain
oci iam user list \
--compartment-id <tenancy-ocid> \
--query 'data[].{name:name, email:email, state:"lifecycle-state", "time-created":"time-created"}' \
--output tableUsers and Groups
Users represent individual people or service accounts that interact with OCI. Each user has a unique username, email address, and can be assigned API keys, auth tokens, and console passwords. Users by themselves have no permissions; they must be added to groups, and groups are granted permissions through policies.
Groups are collections of users that share the same access permissions. When you write a policy, you grant permissions to a group, not to individual users. This makes it easy to manage access: when someone joins a team, add them to the appropriate group and they automatically inherit all the necessary permissions.
# Create a group
oci iam group create \
--compartment-id <tenancy-ocid> \
--name "NetworkAdmins" \
--description "Group for network administrators"
# Create another group for developers
oci iam group create \
--compartment-id <tenancy-ocid> \
--name "Developers" \
--description "Group for application developers"
# Add a user to a group
oci iam group add-user \
--group-id <group-ocid> \
--user-id <user-ocid>
# List members of a group
oci iam group list-users \
--group-id <group-ocid> \
--query 'data[].{name:name, email:email}' \
--output table
# List groups a user belongs to
oci iam user list-groups \
--user-id <user-ocid> \
--query 'data[].{name:name, id:id}' \
--output tableNaming Convention for Groups
Adopt a consistent naming convention for groups that reflects their purpose and scope. Common patterns include Team-Role (e.g., Platform-Admins,App-Developers) or Compartment-Permission (e.g.,Production-ReadOnly, Staging-Manage). Consistent naming makes policies self-documenting and simplifies auditing.
Compartment Design
Compartments are the most important organizational concept in OCI. They serve three critical purposes: resource organization, access control boundaries, and billing/cost tracking. Every resource in OCI must belong to exactly one compartment, and compartments can be nested up to six levels deep.
Your tenancy itself is the root compartment. Best practice dictates that you should never create resources directly in the root compartment. Instead, design a compartment hierarchy that reflects your organizational structure, application topology, or environment separation.
Common Compartment Patterns
| Pattern | Structure | Best For |
|---|---|---|
| Environment-Based | Root > Dev, Staging, Production | Small teams, simple applications |
| Team-Based | Root > Platform, AppTeamA, AppTeamB | Multiple teams, shared infrastructure |
| Functional | Root > Network, Security, Shared, Apps | Enterprise with centralized network/security |
| Hybrid | Root > Network, Security, Prod > AppA, AppB | Large organizations |
# Create a compartment hierarchy for an enterprise layout
# Root
# ├── Network (shared VCNs, DRGs, DNS)
# ├── Security (vaults, WAF, Cloud Guard)
# ├── SharedServices (logging, monitoring, functions)
# ├── Production
# │ ├── AppA
# │ └── AppB
# └── NonProduction
# ├── Dev
# └── Staging
# Create top-level compartments
oci iam compartment create \
--compartment-id <tenancy-ocid> \
--name "Network" \
--description "Shared networking resources"
oci iam compartment create \
--compartment-id <tenancy-ocid> \
--name "Security" \
--description "Security services and vaults"
oci iam compartment create \
--compartment-id <tenancy-ocid> \
--name "SharedServices" \
--description "Shared logging, monitoring, functions"
PROD_ID=$(oci iam compartment create \
--compartment-id <tenancy-ocid> \
--name "Production" \
--description "Production workloads" \
--query 'data.id' --raw-output)
NONPROD_ID=$(oci iam compartment create \
--compartment-id <tenancy-ocid> \
--name "NonProduction" \
--description "Development and staging environments" \
--query 'data.id' --raw-output)
# Create nested compartments
oci iam compartment create \
--compartment-id $PROD_ID \
--name "AppA" \
--description "Production workloads for Application A"
oci iam compartment create \
--compartment-id $NONPROD_ID \
--name "Dev" \
--description "Development environment"
oci iam compartment create \
--compartment-id $NONPROD_ID \
--name "Staging" \
--description "Staging environment"Compartment Deletion
Compartments can be deleted, but the process is not instant. Deleting a compartment requires that all resources within it are terminated first. After you initiate deletion, the compartment enters a "DELETING" state and Oracle processes the request asynchronously. If the deletion fails (because resources still exist), the compartment returns to the "ACTIVE" state. Deleted compartments can be recovered within a time window. To move resources between compartments instead of deleting, use the "Move Resource" feature.
OCI Policy Language
OCI uses a human-readable policy language that is arguably the most intuitive access control syntax among all cloud providers. Every policy statement follows a consistent structure that reads like a natural English sentence. Policies are attached to compartments and apply to the compartment and all its descendants (child compartments).
Policy Syntax
Every policy statement follows this pattern:
Allow <subject> to <verb> <resource-type> in <location> [where <conditions>]
Subject: group <group-name> | dynamic-group <dg-name> | any-user
Verb: inspect | read | use | manage
Resource: all-resources | <specific-resource-type>
Location: tenancy | compartment <name> | compartment id <ocid>
Conditions: request.operation = 'GetObject', target.bucket.name = 'public-data', etc.Verb Hierarchy
| Verb | Permissions | Example Use Case |
|---|---|---|
inspect | List and get metadata (no sensitive data) | Auditors viewing resource inventory |
read | inspect + get resource details and content | Developers reading configurations |
use | read + act on existing resources | Operators starting/stopping instances |
manage | use + create, delete, and full control | Administrators with full access |
Each higher verb includes all permissions of the verbs below it.manage is the most permissive and includes use,read, and inspect.
Common Policy Examples
# Create a policy via CLI
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "admin-policy" \
--description "Full admin access for administrators" \
--statements '[
"Allow group Administrators to manage all-resources in tenancy"
]'
# Network administrators can manage networking in the Network compartment
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "network-admin-policy" \
--description "Network admin access" \
--statements '[
"Allow group NetworkAdmins to manage virtual-network-family in compartment Network",
"Allow group NetworkAdmins to manage load-balancers in compartment Network",
"Allow group NetworkAdmins to read all-resources in tenancy"
]'
# Developers can manage compute and object storage in Dev compartment
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "developer-policy" \
--description "Developer access to Dev compartment" \
--statements '[
"Allow group Developers to manage instance-family in compartment NonProduction:Dev",
"Allow group Developers to manage object-family in compartment NonProduction:Dev",
"Allow group Developers to use virtual-network-family in compartment Network",
"Allow group Developers to read all-resources in compartment NonProduction:Dev"
]'
# Read-only access for auditors across the entire tenancy
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "auditor-policy" \
--description "Read-only access for auditors" \
--statements '[
"Allow group Auditors to inspect all-resources in tenancy",
"Allow group Auditors to read audit-events in tenancy"
]'Policy Attachment Location Matters
Policies must be attached to the compartment where they grant access, or to a parent compartment (including the root/tenancy). A policy attached to the root compartment can grant access anywhere in the tenancy. A policy attached to a specific compartment can only grant access to that compartment and its children. For tenancy-wide policies (like admin access), attach them to the root. For compartment-specific access, attach policies to the relevant compartment.
Dynamic Groups
Dynamic groups are similar to regular groups, but instead of containing users, they contain OCI resources (like compute instances or functions) that match specific criteria. Dynamic groups enable resource-to-resource authentication without managing credentials. For example, you can create a dynamic group that includes all compute instances in a specific compartment, then write a policy that allows those instances to read secrets from OCI Vault.
# Create a dynamic group for all compute instances in a compartment
oci iam dynamic-group create \
--compartment-id <tenancy-ocid> \
--name "ComputeInstances-Production" \
--description "All compute instances in Production compartment" \
--matching-rule "All {instance.compartment.id = '<production-compartment-ocid>'}"
# Dynamic group for all functions in a specific compartment
oci iam dynamic-group create \
--compartment-id <tenancy-ocid> \
--name "Functions-AppA" \
--description "All functions in AppA compartment" \
--matching-rule "All {resource.type = 'fnfunc', resource.compartment.id = '<appa-compartment-ocid>'}"
# Dynamic group matching specific instance OCIDs
oci iam dynamic-group create \
--compartment-id <tenancy-ocid> \
--name "SpecificInstances" \
--description "Specific compute instances" \
--matching-rule "Any {instance.id = 'ocid1.instance.oc1..aaa1', instance.id = 'ocid1.instance.oc1..aaa2'}"
# Grant dynamic group permissions via policy
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "compute-vault-policy" \
--description "Allow compute instances to read secrets" \
--statements '[
"Allow dynamic-group ComputeInstances-Production to read secret-family in compartment Security",
"Allow dynamic-group ComputeInstances-Production to use log-content in compartment SharedServices"
]'Service Policies
Some OCI services need permission to access resources in your tenancy on your behalf. For example, the OS Management service needs permission to manage instances, and the Cloud Guard service needs permission to examine resources across compartments. These are called service policies and use the service principal.
# Allow OCI services to manage resources
# Object Storage service policy (required for lifecycle policies and replication)
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "objectstorage-service-policy" \
--description "Allow Object Storage service to manage resources" \
--statements '[
"Allow service objectstorage-us-ashburn-1 to manage object-family in tenancy"
]'
# Cloud Guard service policy
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "cloudguard-service-policy" \
--description "Allow Cloud Guard to examine resources" \
--statements '[
"Allow service cloudguard to read all-resources in tenancy",
"Allow service cloudguard to use network-security-groups in tenancy"
]'
# OS Management Hub service policy
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "osmh-service-policy" \
--description "Allow OS Management to manage instances" \
--statements '[
"Allow service osmh to read instances in tenancy",
"Allow service osmh to read instance-agent-plugins in tenancy"
]'Conditional Policies
OCI policies support conditions using the where clause, enabling fine-grained access control. Conditions can evaluate request attributes (who is making the request and how), target attributes (the resource being accessed), and tag-based attributes.
# Allow developers to manage only specific instance shapes
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "dev-shape-restriction" \
--description "Restrict developers to small shapes" \
--statements '[
"Allow group Developers to manage instances in compartment NonProduction:Dev where ANY {request.shape = '\''VM.Standard.E4.Flex'\'', request.shape = '\''VM.Standard.A1.Flex'\''}"
]'
# Allow access only to a specific bucket
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "bucket-specific-access" \
--description "Restrict access to a specific bucket" \
--statements '[
"Allow group DataTeam to manage objects in compartment SharedServices where target.bucket.name = '\''analytics-data'\''",
"Allow group DataTeam to read buckets in compartment SharedServices where target.bucket.name = '\''analytics-data'\'' "
]'
# Tag-based access control
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "tag-based-access" \
--description "Access based on resource tags" \
--statements '[
"Allow group Developers to use instances in compartment Production where target.resource.tag.project-tags.Environment = '\''dev'\'' "
]'Federation and External Identity Providers
For organizations with existing identity systems, OCI supports federation with external identity providers (IdPs) including Microsoft Entra ID (Azure AD), Okta, Google Workspace, and any SAML 2.0 or OpenID Connect compliant provider. Federation allows users to sign in to OCI using their corporate credentials without creating separate OCI accounts.
# Federation is configured through Identity Domains in the Console
# Identity & Security > Domains > Default > Security > Identity Providers
# For SAML 2.0 federation, you need:
# 1. The IdP metadata XML URL or file
# 2. Map IdP groups to OCI groups
# 3. Configure assertion attributes
# After federation setup, create policies for federated groups
# Federated group names are prefixed with the IdP name
oci iam policy create \
--compartment-id <tenancy-ocid> \
--name "federated-admin-policy" \
--description "Access for federated administrators" \
--statements '[
"Allow group '\''Default'\''/'\''AzureAD_CloudAdmins'\'' to manage all-resources in tenancy"
]'
# List identity providers configured in the default domain
oci iam identity-provider list \
--protocol SAML2 \
--compartment-id <tenancy-ocid> \
--query 'data[].{name:name, type:"product-type"}' \
--output tableAPI Keys and Auth Tokens
Users interact with OCI through three types of credentials: console password (for web access), API signing keys (for CLI and SDK access), and auth tokens (for third-party tools like Docker and Kubernetes). Each credential type serves a different purpose and has different security considerations.
# Generate an API signing key pair
openssl genrsa -out oci_api_key.pem 2048
openssl rsa -pubout -in oci_api_key.pem -out oci_api_key_public.pem
# Get the key fingerprint
openssl rsa -pubout -outform DER -in oci_api_key.pem | openssl md5 -c
# Upload the public key via CLI
oci iam user api-key upload \
--user-id <user-ocid> \
--key-file oci_api_key_public.pem
# List API keys for a user
oci iam user api-key list \
--user-id <user-ocid> \
--query 'data[].{fingerprint:fingerprint, state:"lifecycle-state", "time-created":"time-created"}' \
--output table
# Create an auth token (for Docker registry, Kubernetes, etc.)
oci iam auth-token create \
--user-id <user-ocid> \
--description "Docker registry access"
# IMPORTANT: Save the token value immediately - it cannot be retrieved later
# List auth tokens
oci iam auth-token list \
--user-id <user-ocid> \
--query 'data[].{description:description, state:"lifecycle-state", "time-created":"time-created"}' \
--output tableAPI Key Limits
Each user can have a maximum of three API signing keys at any time. If you need to rotate keys, delete the old key before uploading a new one. Auth tokens have a limit of two per user. Treat API keys and auth tokens like passwords: store them securely, rotate them regularly, and never embed them in source code or commit them to version control repositories.
IAM Best Practices
Following IAM best practices from the start prevents security incidents and simplifies ongoing management as your OCI usage grows.
| Practice | Description |
|---|---|
| Enable MFA for all users | Require multi-factor authentication, especially for privileged groups |
| Use groups, never individual grants | Always assign permissions to groups, then add users to groups |
| Least privilege | Start with minimal permissions and add more as needed |
| Separate admin from workloads | Use different compartments and groups for admin vs application access |
| Use dynamic groups for services | Avoid storing credentials in instances; use instance principals instead |
| Audit regularly | Review policies, group memberships, and API key usage periodically |
| Tag-based policies | Use defined tags in conditions for scalable access control |
| Break-glass accounts | Maintain emergency admin accounts with MFA stored securely offline |
# Audit: List all policies in the tenancy
oci iam policy list \
--compartment-id <tenancy-ocid> \
--all \
--query 'data[].{name:name, compartment:"compartment-id", statements:statements}' \
--output json
# Audit: Check for overly permissive policies
# Look for: "manage all-resources in tenancy" with non-admin groups
oci iam policy list \
--compartment-id <tenancy-ocid> \
--all \
--output json | grep -i "manage all-resources in tenancy"
# Audit: List all users and their last sign-in time
oci iam user list \
--compartment-id <tenancy-ocid> \
--all \
--query 'data[].{name:name, state:"lifecycle-state", "last-login":"last-successful-login-time"}' \
--output table
# Audit: Find users with API keys older than 90 days
oci iam user list --compartment-id <tenancy-ocid> --all --output json | \
python3 -c "
import json, sys
from datetime import datetime, timedelta
users = json.load(sys.stdin)['data']
cutoff = datetime.utcnow() - timedelta(days=90)
for u in users:
print(f'Checking: {u["name"]}')
"Troubleshooting IAM Issues
The most common IAM issue is a 404 NotAuthorizedOrNotFound error, which means either the resource does not exist or the user does not have permission to access it. OCI intentionally uses this combined error to prevent information leakage (an attacker cannot determine whether a resource exists if they do not have permission).
When troubleshooting access issues, check these in order: Is the user in the correct group? Is there a policy for that group? Is the policy attached to the correct compartment? Does the policy verb match the required action? Is there a condition in the policy that is not being met?
Policy Reference Tool
OCI provides a Policy Reference document that lists every resource type and the exact permissions required for each API operation. When you get an authorization error, check the Policy Reference to find the exact resource type and verb needed. You can access it at docs.oracle.com/iaas/Content/Identity/Reference/policyreference.htm. Additionally, each service's API documentation includes the required policy statement for every endpoint.
Key Takeaways
- 1OCI policies use a human-readable syntax: Allow group X to verb resource-type in compartment Y.
- 2Identity domains replace the legacy IDCS for user and group management.
- 3Policy inheritance flows down the compartment hierarchy automatically.
- 4Tag-based policies enable attribute-based access control (ABAC).
Frequently Asked Questions
What are the OCI policy verbs?
Can I restrict policies to specific resources?
Written by CloudToolStack Team
Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.
Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.