Skip to main content
OCISecurityintermediate

OCI IAM, Compartments & Policies

Master OCI identity domains, compartment hierarchies, policy syntax, and group-based access control.

CloudToolStack Team22 min readPublished Mar 14, 2026

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.

bash
# 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 table

Users 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.

bash
# 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 table

Naming 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

PatternStructureBest For
Environment-BasedRoot > Dev, Staging, ProductionSmall teams, simple applications
Team-BasedRoot > Platform, AppTeamA, AppTeamBMultiple teams, shared infrastructure
FunctionalRoot > Network, Security, Shared, AppsEnterprise with centralized network/security
HybridRoot > Network, Security, Prod > AppA, AppBLarge organizations
bash
# 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:

text
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

VerbPermissionsExample Use Case
inspectList and get metadata (no sensitive data)Auditors viewing resource inventory
readinspect + get resource details and contentDevelopers reading configurations
useread + act on existing resourcesOperators starting/stopping instances
manageuse + create, delete, and full controlAdministrators 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

bash
# 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.

bash
# 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.

bash
# 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.

bash
# 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.

bash
# 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 table

API 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.

bash
# 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 table

API 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.

PracticeDescription
Enable MFA for all usersRequire multi-factor authentication, especially for privileged groups
Use groups, never individual grantsAlways assign permissions to groups, then add users to groups
Least privilegeStart with minimal permissions and add more as needed
Separate admin from workloadsUse different compartments and groups for admin vs application access
Use dynamic groups for servicesAvoid storing credentials in instances; use instance principals instead
Audit regularlyReview policies, group memberships, and API key usage periodically
Tag-based policiesUse defined tags in conditions for scalable access control
Break-glass accountsMaintain emergency admin accounts with MFA stored securely offline
bash
# 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.

OCI Security Best PracticesGetting Started with Oracle CloudTerraform on OCI

Key Takeaways

  1. 1OCI policies use a human-readable syntax: Allow group X to verb resource-type in compartment Y.
  2. 2Identity domains replace the legacy IDCS for user and group management.
  3. 3Policy inheritance flows down the compartment hierarchy automatically.
  4. 4Tag-based policies enable attribute-based access control (ABAC).

Frequently Asked Questions

What are the OCI policy verbs?
OCI IAM uses four verbs that grant increasing levels of access: inspect (list/metadata only), read (inspect + get contents), use (read + work with existing resources), and manage (full control including create/delete). Each verb includes all permissions of the lower verbs.
Can I restrict policies to specific resources?
Yes, you can use conditions in policies with the where clause. Common conditions include matching on resource tags, specific resource OCIDs, request.operation values, and target compartment names. Tag-based policies are the recommended approach for fine-grained access control.

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.