OCI IAM Hardening

Overview

This page covers Oracle Cloud Infrastructure Identity & Access Management hardening across the surfaces that determine whether an attacker who lands a credential in an OCI tenancy can pivot to full tenancy takeover. OCI's IAM model differs from the AWS, Azure, and GCP siblings in three load-bearing ways: compartments are the primary scoping primitive (not accounts, subscriptions, or projects), policy syntax is human-readable (statements such as Allow group GroupName to manage resource-type in compartment CompartmentName are written verbatim, not compiled from JSON), and as of the 2024 Identity Domains general-availability, the tenancy's IAM service is itself federated by default through one or more Identity Domains rather than the legacy IDCS (Identity Cloud Service) instance. The control inventory here is eight items mirroring the Azure, GCP, and oci sibling pages; AWS has ten on account of its larger root + Organizations surface. Scope is commercial OCI realms; Government Cloud and dedicated-region tenancies inherit the same controls but require realm-specific endpoints and have their own Identity Domain enrolment ceremonies.

The mental model: OCI authentication and authorisation are the product of users and groups (humans and service accounts inside an Identity Domain), compartments (the logical container for all OCI resources, nested to arbitrary depth with the tenancy as the root compartment), policies (statements granting groups, dynamic groups, or services the ability to act on resource families inside a compartment subtree), and dynamic groups + instance principals (a per-resource identity for compute, functions, and other workloads, eliminating user-style API keys on machines). The cross-cutting principles — least privilege, separation of duties, credential rotation, secrets management, MFA — are explained in the General IAM page; this page maps them to OCI primitives. Federation patterns (SAML and OIDC) and the MFA factor matrix are detailed in General IAM — MFA and General IAM — Identity federation. Equivalence callouts at the bottom of each control point to the matching control on the AWS, Azure, and GCP pages so a reader can compare modelling across providers.

Order matters in this list. Controls 01–03 are CRITICAL/HIGH PREVENTIVE and address the single biggest residual risk in nearly every audited OCI tenancy: standing administrative credentials in the default Administrators group with MFA gaps. Control 04 establishes Identity Domains and federation so subsequent human onboarding flows do not re-introduce local IAM users. Controls 05–06 progressively eliminate long-lived API signing keys by moving workload authentication onto instance principals + dynamic groups. Controls 07–08 fence the blast radius via the compartment hierarchy and least-privilege policy statements. Detective coverage (Cloud Guard policy advisor, audit log review) is handled in the Logging domain at Phase 9 and referenced by the equivalence callouts on control 08. Reviewing the compliance-frameworks page first will clarify why each control row lists CIS, NIST 800-53 rev5, and ISO 27001/27017 cells in the same order across all four provider pages.

oci-iam-01-tenancy-admin-disabled ! CRITICAL PREVENTIVE

After the OCI landing-zone deployment completes and named human administrators are onboarded into the Identity Domain, the initial tenancy admin user — the one created at tenancy provisioning time with credentials emailed to the account owner — must be deactivated (not deleted; deletion would orphan resources it owns). The initial admin is the OCI analogue of the AWS root user: it sits in the Administrators group with manage all-resources in tenancy, and its credentials typically arrive in a fan-out of provisioning emails that a typical SOC has no way to audit. CIS OCI Foundations v2.0.0 control 1.1 calls for separating tenancy administration from day-to-day administration; deactivating the initial admin is the operational form of that separation (OCI IAM security structure — cloud adoption framework (accessed 2026-05)). The principle is reinforced in the General IAM — MFA section, which makes the broader point that "the principal that bootstrapped the tenancy is the principal you most want never to use".

Remediation — OCI CLI

# Step 1 — confirm named human admins exist in the Administrators group before
# deactivating the initial admin (otherwise you lock yourself out).
oci iam group list-users \
  --group-id "$(oci iam group list \
                 --compartment-id $OCI_TENANCY_OCID \
                 --query 'data[?name==`Administrators`].id | [0]' --raw-output)" \
  --query 'data[].{name:name,active:"lifecycle-state"}' --output table

# Step 2 — locate the initial tenancy admin user (created at tenancy birth;
# email typically matches the original tenancy-provisioning recipient).
oci iam user list \
  --compartment-id $OCI_TENANCY_OCID \
  --query 'data[?email==``].{id:id,name:name,active:"lifecycle-state"}'

# Step 3 — deactivate (do NOT delete; deletion orphans resources the user owns).
oci iam user update \
  --user-id  \
  --is-active false

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Model the initial admin's deactivation as desired state. The user resource is
# imported (terraform import oci_identity_user.initial_admin ) rather
# than created here, because it pre-dates Terraform management.
resource "oci_identity_user" "initial_admin" {
  compartment_id = var.tenancy_ocid
  name           = var.initial_admin_name
  description    = "Initial tenancy admin — deactivated post landing-zone bootstrap"
  email          = var.initial_admin_email

  # The Terraform provider does not expose an is_active toggle directly; this
  # block models the documented post-bootstrap state and forces drift detection
  # if the user is reactivated out-of-band.
  lifecycle {
    prevent_destroy = true
    ignore_changes  = [email, description]
  }
}

# Companion: a tenancy-wide policy denies reactivation of the initial admin
# except by a small break-glass group, scoped to a single audited compartment.
resource "oci_identity_policy" "initial_admin_lockdown" {
  compartment_id = var.tenancy_ocid
  name           = "initial-admin-lockdown"
  description    = "Forbid reactivation of the initial tenancy admin user."
  statements = [
    "Allow group BreakGlass to use users in tenancy where target.user.id = '${oci_identity_user.initial_admin.id}'"
  ]
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-01-tenancy-admin-disabled" \
  --display-name "oci-iam-01-tenancy-admin-disabled" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-01-tenancy-admin-disabled" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Remediation — Pulumi (TypeScript)

import * as pulumi from "@pulumi/pulumi";
import * as oci from "@pulumi/oci";

// Replace tenancy-admin direct membership with a break-glass-only policy.
// The Administrators group should remain empty in steady state.
const cfg = new pulumi.Config();
const tenancyOcid = cfg.require("tenancyOcid");

// Audit current Administrators membership via the IAM resource (read-only state).
const admins = oci.identity.getGroup({
  groupId: cfg.require("administratorsGroupOcid"),
});

// Deny-by-default policy on the tenancy: only break-glass users may join Administrators.
const denyAdminPolicy = new oci.identity.Policy("deny-tenancy-admin", {
  compartmentId: tenancyOcid,
  name: "deny-tenancy-admin-direct-membership",
  description: "Block direct Administrators membership; break-glass only via PIM-like workflow",
  statements: [
    "Deny group Administrators to manage all-resources in tenancy where request.user.mfaTotp.is.absent='true'",
  ],
});

export const policyOcid = denyAdminPolicy.id;

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
1.51.1.11.11.1 AC-6(7)A.5.15; A.5.16n/a

Log signals

  • OCI Logging Analytics entries where 'Log Source' = 'OCI Audit Logs' and eventName in (CreatePolicy, UpdatePolicy, DeletePolicy) at the tenancy compartment scope.
  • Audit entries where data.identity.principalName matches the default Administrators group or any policy statement granting manage all-resources in tenancy.
  • OCI Identity Domains audit feed deltas tracking the tenancy-admin allow-list membership over time.

Query

'Log Source' = 'OCI Audit Logs'
and eventName in ('CreatePolicy', 'UpdatePolicy', 'DeletePolicy')
and 'Compartment Name' = 'tenancy'
| timestats count as logrecords by 'User Name', eventName
| sort -logrecords

Run in OCI Logging Analytics against the tenancy-level Audit log group. Persist as a saved search backing a Management Agent alarm; OCI Logging Analytics natively interprets the SQL-flavoured query syntax.

Alert threshold

  • Any CreatePolicy / UpdatePolicy / DeletePolicy from a User Name not on the tenancy-admin allow-list — page on first occurrence.
  • Tune per compartment baseline using Logging Analytics timestats over a 30-day calibration window; expect near-zero policy-change cadence outside scheduled IAM-tier reviews.

Initial response

  1. Verify the policy change against the documented OCI Bastion break-glass log and the named tenancy administrator; if no break-glass entry exists, treat as confirmed compromise.
  2. Roll back: diff the policy statement against the prior version captured in source-control, revert to the last known-good HCL via Resource Manager, and rotate compartment-admin credentials (console password + API signing keys).
  3. Escalate per general/ir.html — open an incident, export the affected tenancy audit window to Object Storage for forensic retention, and confirm the IAM Bastion + MFA-enforced compartment guardrails remain in effect.

References

Equivalent on: AWS · Azure · GCP

oci-iam-02-named-admin-accounts ! HIGH PREVENTIVE

Every human administrator must hold a named OCI user with their own credentials; shared tenancy credentials are forbidden. The named-admin pattern is what makes audit logs attributable, what makes offboarding tractable (deactivate one user, not re-key a shared password), and what makes MFA enforceable per-human. CIS OCI Foundations v2.0.0 control 1.1 calls for separating tenancy administration from day-to-day operations; named admin accounts inside a per-role group structure (for example NetworkAdmins, DatabaseAdmins, SecurityAdmins) are the operational form (OCI Identity and Access Management documentation (accessed 2026-05)). Named admins should live inside an Identity Domain rather than the legacy local IAM user store (see control 04); MFA enrolment for them is mandatory and covered by control 03.

Remediation — OCI CLI

# Create a named admin user inside the default Identity Domain.
oci iam user create \
  --name alice.example \
  --description "Alice Example — Cloud Platform admin (named)" \
  --email alice@corp.example

# Add the user to a per-role group (not the catch-all Administrators group).
oci iam group add-user \
  --user-id  \
  --group-id "$(oci iam group list \
                 --compartment-id $OCI_TENANCY_OCID \
                 --query 'data[?name==`SecurityAdmins`].id | [0]' --raw-output)"

# Audit: list every user in the Administrators group; expect a small, named set.
oci iam group list-users \
  --group-id "$(oci iam group list \
                 --compartment-id $OCI_TENANCY_OCID \
                 --query 'data[?name==`Administrators`].id | [0]' --raw-output)"

Remediation — Terraform

# Terraform OCI provider ~> 5.0
resource "oci_identity_user" "alice" {
  compartment_id = var.tenancy_ocid
  name           = "alice.example"
  description    = "Alice Example — Cloud Platform admin (named)"
  email          = "alice@corp.example"
}

resource "oci_identity_group" "security_admins" {
  compartment_id = var.tenancy_ocid
  name           = "SecurityAdmins"
  description    = "Named security administrators — MFA enforced via Identity Domain policy."
}

resource "oci_identity_user_group_membership" "alice_security_admins" {
  user_id  = oci_identity_user.alice.id
  group_id = oci_identity_group.security_admins.id
}

resource "oci_identity_policy" "security_admins_scope" {
  compartment_id = var.tenancy_ocid
  name           = "security-admins-scope"
  description    = "Named SecurityAdmins manage security-list, vault, and audit; not the entire tenancy."
  statements = [
    "Allow group SecurityAdmins to manage vaults in tenancy",
    "Allow group SecurityAdmins to read audit-events in tenancy",
    "Allow group SecurityAdmins to manage cloud-guard-family in tenancy"
  ]
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-02-named-admin-accounts" \
  --display-name "oci-iam-02-named-admin-accounts" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-02-named-admin-accounts" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
1.41.1.31.11.1 AC-2; IA-2A.5.16n/a

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and 'Service Name' = 'identity' filtered to eventName in (CreateUser, UpdateUserCapabilities, AddUserToGroup) targeting the Administrators group OCID.
  • Identity Domain SCIM audit deltas where a UserResource is added to the tenancy-admin group without an accompanying corporate ticket reference in the request metadata.
  • Shared-credential heuristic: multiple distinct source IPs authenticating as the same 'User Name' within a short rolling window across geographically distinct OCI realms.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('CreateUser', 'AddUserToGroup', 'UpdateUserCapabilities')
          and data.response.payload.groupName = 'Administrators'
          | stats count by 'User Name', eventName, 'Compartment Name'
          | sort -count

Save the search inside the tenancy-root Logging Analytics namespace and pipe it to an OCI Monitoring alarm scoped to the audit log group.

Alert threshold

  • Any addition to Administrators outside the documented quarterly admin review window — page on the first event.
  • More than one distinct source IP signing in as the same named admin across two OCI home regions inside 60 minutes — page as a credential-sharing indicator.

Initial response

  1. Cross-reference the AddUserToGroup event against the change-advisory ticket queue; if no matching ticket exists, treat the new membership as out-of-band and freeze the user pending review.
  2. Remove the user from Administrators with oci iam group remove-user-from-group, leaving the user object intact so the audit trail remains attributable.
  3. Open an incident per general/ir.html, rotate any console password on the affected user via the Identity Domain self-service flow, and require FIDO2 re-enrolment before re-granting admin rights.

References

Equivalent on: AWS · Azure · GCP

oci-iam-03-mfa-console ! CRITICAL PREVENTIVE

Every OCI user with console (non-programmatic) access must have MFA enrolled and enforced. In OCI Identity Domains — the post-2024 default — MFA factor configuration lives in the Identity Domain's Sign-On Policies and the Authentication Factor Settings, where a tenant administrator selects which factors are enabled (TOTP, mobile push via Oracle Mobile Authenticator, FIDO2 security key, SMS, email) and which sign-on rules require a second factor for which user populations. Phishing-resistant factors (FIDO2 security keys) should be required for any user in an admin-tier group; TOTP is acceptable as a baseline for non-admin populations. CIS OCI Foundations v2.0.0 control 1.7 codifies the MFA-on-console requirement (OCI Identity Domains overview (accessed 2026-05)). The severity derivation matches the worked example in methodology EX-MFA-01: single-step path from a leaked password to console takeover, CRITICAL PREVENTIVE.

Remediation — OCI CLI

# List authentication factors currently enabled in the default Identity Domain.
DOMAIN_ID=$(oci iam domain list \
              --compartment-id $OCI_TENANCY_OCID \
              --query 'data[?"display-name"==`Default`].id | [0]' --raw-output)

oci iam domain get \
  --domain-id $DOMAIN_ID \
  --query 'data.{name:"display-name",url:"url",state:"lifecycle-state"}'

# Update the Identity Domain's authentication factor settings via the REST API
# wrapper (oci iam domain update covers domain-level mutable fields; granular
# AuthenticationFactorSetting mutations use the Identity Domain admin REST endpoint).
# Example: require MFA on Sign-On Policy "Console Admins".
oci iam domain update \
  --domain-id $DOMAIN_ID \
  --description "Default domain — MFA enforced via Sign-On Policies; FIDO2 mandatory for admin groups."

# For legacy IAM users (pre-Identity Domain, retained for migration windows),
# manage TOTP devices directly:
oci iam mfa-totp-device create --user-id 
oci iam mfa-totp-device list   --user-id 

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Domain-level authentication factor settings: enable TOTP + FIDO2 + push;
# disable SMS and security questions; require MFA for the Admin sign-on rule.
data "oci_identity_domain" "default" {
  domain_id = var.default_domain_ocid
}

resource "oci_identity_domains_authentication_factor_setting" "default" {
  idcs_endpoint                  = data.oci_identity_domain.default.url
  authentication_factor_setting_id = "AuthenticationFactorSettings"
  schemas = ["urn:ietf:params:scim:schemas:oracle:idcs:AuthenticationFactorSettings"]

  totp_enabled         = true
  push_enabled         = true
  fido_authenticator_enabled = true
  sms_enabled          = false
  security_questions_enabled = false

  mfa_enrollment_type  = "Required"

  totp_settings {
    time_step_in_secs            = 30
    passcode_length              = 6
    sms_otp_validity_duration_in_mins = 10
  }
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-03-mfa-console" \
  --display-name "oci-iam-03-mfa-console" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-03-mfa-console" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Remediation — Pulumi (TypeScript)

import * as pulumi from "@pulumi/pulumi";
import * as oci from "@pulumi/oci";

// Enforce MFA for console sign-in via Identity Domain authentication policy.
const cfg = new pulumi.Config();
const idcsEndpoint = cfg.require("idcsEndpoint");
const domainOcid = cfg.require("identityDomainOcid");

// Console sign-on policy: require MFA factor (TOTP / FIDO2) for all users.
const consoleSignOnPolicy = new oci.identity.DomainsAuthenticationFactorSetting(
  "console-mfa-required",
  {
    idcsEndpoint: idcsEndpoint,
    schemas: ["urn:ietf:params:scim:schemas:oracle:idcs:AuthenticationFactorSetting"],
    totpEnabled: true,
    fidoAuthenticatorEnabled: true,
    mfaEnabled: "ENABLED",
    autoEnrollEmailFactorDisabled: false,
    bypassCodeEnabled: false,  // no bypass — break-glass uses separate workflow
  },
);

export const mfaSettingOcid = consoleSignOnPolicy.id;

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
1.101.1.21.21.7 IA-2(1)A.5.17; A.8.5n/a

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' referencing the Identity Domain AuthenticationFactorSettings SCIM endpoint with eventName in (UpdateAuthenticationFactorSettings) lowering mfaEnrollmentType from Required.
  • Identity Domain sign-on events showing a successful console login where 'Authentication Factor' attribute is empty or equals Password only — indicating MFA was bypassed for that session.
  • SignOnPolicy mutation events where a rule covering an admin group population moves from requireMFA = true to requireMFA = false.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('UpdateAuthenticationFactorSettings', 'UpdateSignOnPolicy', 'PatchSignOnPolicy')
          | eval drift = if(data.response.payload.mfaEnrollmentType = 'Optional', 'WEAKENED', 'OK')
          | where drift = 'WEAKENED'
          | timestats count as changes by 'User Name', 'Compartment Name'

Schedule as a 5-minute interval saved search; emit a Service Connector Hub message to a Notifications topic on any non-zero count.

Alert threshold

  • Any movement of mfaEnrollmentType away from Required at the Identity Domain scope — page immediately, this is a control-fence break.
  • Any console-tier sign-on event lacking a second-factor assertion attribute on a user member of an admin-tier group — open an incident.

Initial response

  1. Restore the prior AuthenticationFactorSettings SCIM payload from the Terraform state stored in Resource Manager; re-apply to revert the enrolment policy in one declarative step.
  2. Force termination of every active Identity Domain session via the admin console (Sessions → Terminate all) so that current single-factor sessions are invalidated.
  3. Notify each affected admin to re-enrol a FIDO2 factor before next sign-in; document the rollback per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-iam-04-identity-domains ! HIGH PREVENTIVE

Migrate human identity off the local OCI IAM user store (legacy; migrated to OCI Identity Domains) and onto one or more Identity Domains, federated via SAML 2.0 or OpenID Connect to the corporate identity provider (Okta, Entra ID, Ping, Auth0). Identity Domains were generally available across all OCI commercial realms in 2024 and are the supported go-forward identity surface; new tenancies are provisioned with a Default domain at creation time. Federation eliminates per-user OCI passwords for the human population, removes the offboarding race (deprovision in the corporate IdP → access disappears from OCI within the IdP's session-lifetime window), and inherits the IdP's MFA, conditional-access, and risk-signal infrastructure (OCI Identity Domains overview (accessed 2026-05)).

Remediation — OCI CLI

# Create a new Identity Domain (a tenancy may operate multiple domains, e.g.
# Workforce + Customer; here we add one for B2B partners).
oci iam domain create \
  --compartment-id $OCI_TENANCY_OCID \
  --display-name "Partners" \
  --description "Federated B2B partner identity domain" \
  --home-region eu-frankfurt-1 \
  --license-type premium

# Replicate the domain to additional regions for low-latency sign-in.
oci iam domain replicate-to-region \
  --domain-id  \
  --replica-region eu-amsterdam-1

# Federation: an Identity Provider (SAML or OIDC) is configured inside the
# domain via the Identity Domain admin endpoints (the IdentityProvider SCIM
# resource); the OCI CLI surface for this is the domains identity-provider
# sub-tree once the domain is in ACTIVE state.
oci iam domains identity-provider list --idcs-endpoint 

Remediation — Terraform

# Terraform OCI provider ~> 5.0
resource "oci_identity_domain" "partners" {
  compartment_id = var.tenancy_ocid
  display_name   = "Partners"
  description    = "Federated B2B partner identity domain"
  home_region    = "eu-frankfurt-1"
  license_type   = "premium"
}

# SAML federation to the corporate IdP (Okta in this example).
resource "oci_identity_domains_identity_provider" "okta" {
  idcs_endpoint = oci_identity_domain.partners.url
  schemas       = ["urn:ietf:params:scim:schemas:oracle:idcs:IdentityProvider"]

  partner_name        = "okta-corp"
  partner_provider_id = "http://www.okta.com/exk1abcDEFGHIJKL"
  metadata            = file("${path.module}/okta-metadata.xml")
  name_id_format      = "saml-emailaddress"
  jit_user_prov_enabled = true
  enabled             = true
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-04-identity-domains" \
  --display-name "oci-iam-04-identity-domains" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-04-identity-domains" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
1.211.1.4best-practicesbest-practices IA-2; IA-8A.5.17n/a

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' with eventName in (CreateIdentityProvider, UpdateIdentityProvider, DeleteIdentityProvider) against any non-corporate SAML metadata URL.
  • Federation trust deltas: new IdentityProvider SCIM resource inserted with jitUserProvisioning = true outside the documented federation onboarding flow.
  • Local-user creation events inside an Identity Domain configured for federation-only — these should be impossible by design and indicate a federation bypass.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('CreateIdentityProvider', 'UpdateIdentityProvider', 'DeleteIdentityProvider', 'CreateUser')
          | eval idp_metadata = data.request.payload.metadataUrl
          | where (eventName like 'IdentityProvider' and not idp_metadata like '%corp.example%')
             or  (eventName = 'CreateUser' and data.request.payload.federated = 'false')
          | stats count by eventName, 'User Name', idp_metadata

Run continuously against the tenancy audit log group; pivot output into a Cloud Guard managed list as enrichment data.

Alert threshold

  • Any new IdentityProvider SCIM entry whose metadata URL does not match the corporate IdP allow-list — page on first occurrence.
  • Any local OCI user creation in a federation-only Identity Domain — page as a federation-bypass attempt.

Initial response

  1. Disable the rogue IdentityProvider via oci iam domains identity-provider patch setting enabled = false while preserving the audit record.
  2. Revoke any sessions issued by that IdP using the Identity Domain admin console session inventory.
  3. Roll the trust back to the approved corporate IdP via the Resource Manager stack that owns the federation configuration, and notify the Identity team per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-iam-05-api-key-rotation ! HIGH PREVENTIVE

OCI API access uses RSA signing keys (PEM-encoded keypairs, the public half registered against an OCI user) rather than symmetric secrets, but the operational risk of a leaked signing key is identical to a leaked symmetric secret: anyone holding the private half can sign arbitrary OCI API requests as the user. Two invariants apply. First, admin-tier users (members of Administrators or any per-role admin group) must not hold API keys at all — admin actions go through the console with MFA, and any programmatic admin work flows through a separately-audited automation principal. Second, the API keys that remain (on non-admin users for legitimate programmatic workflows) must be rotated at most every 90 days. CIS OCI Foundations v2.0.0 control 1.14 codifies the rotation cadence (CIS Oracle Cloud Infrastructure Benchmark v2.0.0 (accessed 2026-05)); the admin-no-API-keys invariant is reinforced by OCI's own published best practices (OCI Identity and Access Management documentation (accessed 2026-05)).

Remediation — OCI CLI

# Audit: list API keys per user with their fingerprints + creation timestamps.
oci iam user list \
  --compartment-id $OCI_TENANCY_OCID \
  --query 'data[].id' --output text \
  | tr '\t' '\n' \
  | while read uid; do
      oci iam user api-key list --user-id "$uid" \
        --query 'data[?"time-created"<=`'"$(date -u -d '90 days ago' +%Y-%m-%dT%H:%M:%SZ)"'`].[fingerprint,"time-created"]' \
        --output table 2>/dev/null
    done

# Admin users must have zero API keys; assert this invariant.
ADMIN_GROUP_ID=$(oci iam group list \
                   --compartment-id $OCI_TENANCY_OCID \
                   --query 'data[?name==`Administrators`].id | [0]' --raw-output)
oci iam group list-users --group-id $ADMIN_GROUP_ID \
  --query 'data[].id' --output text \
  | tr '\t' '\n' \
  | while read uid; do
      n=$(oci iam user api-key list --user-id "$uid" --query 'length(data)' --output text)
      [ "$n" != "0" ] && echo "VIOLATION: admin user $uid holds $n api-key(s)"
    done

# Rotate: upload new public key, then delete the old key by fingerprint.
oci iam user api-key upload --user-id  --key-file new-public.pem
oci iam user api-key delete --user-id  --fingerprint 

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Manage an automation user's API key as state, so rotation is a plan/apply
# rather than a manual console action. The public key material is sourced from
# a secrets backend (Vault, OCI Vault); the private key never enters Terraform.
resource "oci_identity_api_key" "ci_pipeline" {
  user_id  = oci_identity_user.ci_pipeline.id
  key_value = data.vault_kv_secret_v2.ci_pipeline_public_key.data["pem"]

  lifecycle {
    # Force rotation by recreating when the source secret version changes.
    replace_triggered_by = [
      data.vault_kv_secret_v2.ci_pipeline_public_key.data["version"]
    ]
  }
}

# Tenancy-wide policy preventing API key creation on members of Administrators.
resource "oci_identity_policy" "no_admin_api_keys" {
  compartment_id = var.tenancy_ocid
  name           = "no-admin-api-keys"
  description    = "Admin-tier users must not hold API signing keys."
  statements = [
    "Allow group SecurityAdmins to inspect users in tenancy",
    "Deny  group Administrators to manage api-keys in tenancy"
  ]
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-05-api-key-rotation" \
  --display-name "oci-iam-05-api-key-rotation" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-05-api-key-rotation" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
1.141.1.61.41.14 IA-5(1)A.5.17; A.8.2n/a

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' and eventName = 'UploadApiKey' targeting any user OCID resolvable to the Administrators group via the Identity Domain SCIM membership feed.
  • API signing key fingerprints whose 'Time Created' is older than 90 days yet still appear as the signing fingerprint on inbound API calls in the audit feed.
  • Successful API calls bearing a fingerprint that does not exist in the current ApiKey inventory — indicating a stale-fingerprint replay window.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName = 'UploadApiKey'
          | eval admin_target = if(data.target.user.groups like '%Administrators%', 'YES', 'NO')
          | where admin_target = 'YES'
             or data.request.payload.keyAgeDays > 90
          | stats count by 'User Name', data.target.user.id, admin_target

Pair the saved search with a Cloud Guard custom detector recipe rule that fires on the same JMESPath expression in near-real time.

Alert threshold

  • Any UploadApiKey targeting a user with admin-tier group membership — page immediately, admin-tier users must hold zero API keys by policy.
  • Continued use of a signing fingerprint older than 90 days — open a ticket on the owning workload team for rotation.

Initial response

  1. Delete the offending API key via oci iam user api-key delete by fingerprint, then confirm the deletion in the audit trail.
  2. Reset the workload's signing material by issuing a fresh keypair from OCI Vault and re-binding the public half through the Resource Manager pipeline.
  3. Reconcile the affected workload's instance principal posture per control oci-iam-06 — long-lived API keys on workloads should be migrated to instance principals or resource principals.

References

Equivalent on: AWS · Azure · GCP

oci-iam-06-instance-principals ! HIGH PREVENTIVE

Compute workloads (Compute instances, OKE worker nodes, Functions, Data Flow runs) must authenticate to OCI services using instance principals (compute) or resource principals (Functions, Data Flow) — not API signing keys baked into the workload's configuration. An instance principal is a per-instance identity, attested by the OCI control plane via the instance's metadata service, that can be granted IAM rights by policy targeting a dynamic group — a group whose membership is derived from instance attributes (compartment OCID, defined tags) rather than enumerated by user OCID (OCI instance principals documentation (accessed 2026-05)). Combined with control 05's prohibition on admin API keys, instance principals eliminate the long-lived-credential rotation problem for the workload tier entirely.

Remediation — OCI CLI

# Create a dynamic group whose membership is "every Compute instance in the
# Workload compartment tagged role=app".
oci iam dynamic-group create \
  --compartment-id $OCI_TENANCY_OCID \
  --name AppRuntimeInstances \
  --description "Compute instances backing the app tier; identity for OCI API calls." \
  --matching-rule "ALL {instance.compartment.id = '', tag.WorkloadTagNamespace.role.value = 'app'}"

# Grant the dynamic group the rights its workloads actually need; nothing more.
oci iam policy create \
  --compartment-id  \
  --name app-runtime-policy \
  --description "Dynamic-group bound rights for the app runtime instances." \
  --statements '["Allow dynamic-group AppRuntimeInstances to manage objects in compartment Workload where target.bucket.name = '"'"'app-data'"'"'"]'

# Inside the instance, the OCI SDK auto-discovers the instance principal:
#   from oci.auth.signers import InstancePrincipalsSecurityTokenSigner
#   signer = InstancePrincipalsSecurityTokenSigner()
# — no API key, no config file, no rotation.

Remediation — Terraform

# Terraform OCI provider ~> 5.0
resource "oci_identity_dynamic_group" "app_runtime" {
  compartment_id = var.tenancy_ocid
  name           = "AppRuntimeInstances"
  description    = "Compute instances backing the app tier; identity for OCI API calls."
  matching_rule  = "ALL {instance.compartment.id = '${var.workload_compartment_id}', tag.WorkloadTagNamespace.role.value = 'app'}"
}

resource "oci_identity_policy" "app_runtime_rights" {
  compartment_id = var.workload_compartment_id
  name           = "app-runtime-policy"
  description    = "Dynamic-group bound rights for the app runtime instances."
  statements = [
    "Allow dynamic-group AppRuntimeInstances to manage objects in compartment Workload where target.bucket.name = 'app-data'",
    "Allow dynamic-group AppRuntimeInstances to use vaults in compartment Workload",
    "Allow dynamic-group AppRuntimeInstances to read secret-family in compartment Workload"
  ]
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-06-instance-principals" \
  --display-name "oci-iam-06-instance-principals" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-06-instance-principals" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
best-practices1.1.6best-practicesbest-practices IA-5(7); AC-6A.5.16CLD.6.3.1

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' with eventName in (CreateDynamicGroup, UpdateDynamicGroup, DeleteDynamicGroup) — every dynamic-group mutation alters workload-tier trust.
  • DynamicGroup matchingRule deltas where the rule widens (drops a tag predicate, drops a compartment predicate, or substitutes ANY for ALL in the boolean shape).
  • Workload-tier API calls signed by static API keys instead of an instance principal token — surface the absence of a 'Federation Type' = 'instance' claim.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('CreateDynamicGroup', 'UpdateDynamicGroup', 'DeleteDynamicGroup')
          | eval rule_open = if(data.request.payload.matchingRule like 'ANY {%', 'BROAD', 'SCOPED')
          | where rule_open = 'BROAD'
          | stats count by 'User Name', data.target.dynamicGroup.name, data.request.payload.matchingRule

Persist as a saved search; feed into a Cloud Guard policy detector recipe to surface dynamic-group widening across the tenancy.

Alert threshold

  • Any UpdateDynamicGroup producing a matchingRule with leading ANY { at the top level — page on first event; ALL {...} with tag predicates is the supported pattern.
  • Workload OCIDs continuing to authenticate via static API keys 30 days after a dynamic-group / instance-principal migration window closes — open a workload-team ticket.

Initial response

  1. Revert the dynamic-group matchingRule to the prior version captured in the Resource Manager state file; the previous rule was the last-known-good least-privilege scope.
  2. Rotate any API signing keys associated with the affected workload's static-credential fallback so the broadened blast radius is closed even if a copy of the wider rule was exploited.
  3. File a follow-up to migrate the workload to instance principals if it is still using static keys, per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

oci-iam-07-compartment-hierarchy ! HIGH PREVENTIVE

Compartments are OCI's primary blast-radius primitive. Design the compartment hierarchy so that the tenancy (root compartment) holds only shared-infrastructure resources (the Audit service configuration, central Logging, central Vault, the Identity Domain itself), and workloads live in workload compartments beneath it with their own admin groups. Nest workload compartments by environment (Workload/Prod, Workload/NonProd) or by business unit, with policies attached at the smallest compartment that grants the required privilege. The reference deployment is OCI's Cloud Adoption Framework "landing zone" pattern, in which the tenancy compartment is administered by a small TenancyAdmins group and every workload compartment has its own ${Compartment}Admins group scoped to that subtree (OCI IAM security structure — cloud adoption framework (accessed 2026-05)).

Remediation — OCI CLI

# Create a top-level Workload compartment and an environment-scoped child.
oci iam compartment create \
  --compartment-id $OCI_TENANCY_OCID \
  --name Workload \
  --description "Top-level workload compartment; tenant of all business-unit subtrees."

WORKLOAD_OCID=$(oci iam compartment list \
                  --compartment-id $OCI_TENANCY_OCID \
                  --query 'data[?name==`Workload`].id | [0]' --raw-output)

oci iam compartment create \
  --compartment-id $WORKLOAD_OCID \
  --name Prod \
  --description "Production workloads."

# Create the admin group bound to the Workload subtree.
oci iam group create --compartment-id $OCI_TENANCY_OCID \
  --name WorkloadAdmins --description "Admin rights inside compartment Workload only."

# Policy attached at the Workload compartment grants subtree-wide rights.
oci iam policy create \
  --compartment-id $WORKLOAD_OCID \
  --name workload-admin-policy \
  --statements '["Allow group WorkloadAdmins to manage all-resources in compartment Workload"]' \
  --description "WorkloadAdmins manage everything inside Workload, nothing in sibling compartments."

Remediation — Terraform

# Terraform OCI provider ~> 5.0
resource "oci_identity_compartment" "workload" {
  compartment_id = var.tenancy_ocid
  name           = "Workload"
  description    = "Top-level workload compartment."
  enable_delete  = false
}

resource "oci_identity_compartment" "workload_prod" {
  compartment_id = oci_identity_compartment.workload.id
  name           = "Prod"
  description    = "Production workloads."
  enable_delete  = false
}

resource "oci_identity_group" "workload_admins" {
  compartment_id = var.tenancy_ocid
  name           = "WorkloadAdmins"
  description    = "Admin rights inside compartment Workload only."
}

resource "oci_identity_policy" "workload_admin_policy" {
  compartment_id = oci_identity_compartment.workload.id
  name           = "workload-admin-policy"
  description    = "WorkloadAdmins manage everything inside Workload, nothing in sibling compartments."
  statements = [
    "Allow group WorkloadAdmins to manage all-resources in compartment Workload"
  ]
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-07-compartment-hierarchy" \
  --display-name "oci-iam-07-compartment-hierarchy" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-07-compartment-hierarchy" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
best-practices1.1.41.51.1 AC-6(1); AC-5A.5.15n/a

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' with eventName in (CreateCompartment, MoveCompartment, DeleteCompartment) targeting any compartment whose parent is the tenancy root.
  • MoveCompartment events that re-parent a workload compartment under the tenancy root, flattening the hierarchy and breaking the landing-zone topology.
  • Policy statements newly created at the tenancy compartment that grant manage all-resources in tenancy to any group other than TenancyAdmins.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('CreateCompartment', 'MoveCompartment', 'DeleteCompartment')
          | eval parent = data.request.payload.parentCompartmentId
          | where parent = '$OCI_TENANCY_OCID'
             or eventName = 'MoveCompartment'
          | stats count by 'User Name', eventName, data.target.compartment.name, parent

Run hourly; the steady-state rate is near zero outside scheduled landing-zone evolutions.

Alert threshold

  • Any CreateCompartment directly under the tenancy root outside the landing-zone change window — page; new top-level compartments require architecture review.
  • Any MoveCompartment targeting an existing workload compartment — page; reparenting changes policy inheritance silently.

Initial response

  1. Re-parent the compartment back to its documented owner subtree via oci iam compartment move, restoring inheritance to the landing-zone baseline.
  2. Diff the active tenancy-root policy set against the Resource Manager state; revert any newly added manage all-resources in tenancy statements.
  3. Open an incident per general/ir.html, capture the audit window covering the move, and brief the platform team on the topology delta.

References

Equivalent on: AWS · Azure · GCP

oci-iam-08-policy-least-privilege ! HIGH PREVENTIVE

Every policy statement must scope its verb (inspect < read < use < manage) to the smallest necessary resource-type (e.g. objects, not object-family; instance-family, not all-resources) inside the smallest necessary compartment (a leaf workload compartment, not the tenancy). The blanket statement Allow group SomeGroup to manage all-resources in tenancy belongs only to the tightly-controlled tenancy administrator group; everything else expresses least privilege using OCI's human-readable policy syntax (OCI IAM policy syntax reference (accessed 2026-05)). Detective coverage of policy drift — surfacing newly over-broad statements, unused statements, or policies referencing deleted groups — is provided by OCI Cloud Guard's policy advisor, which is treated as a DETECTIVE pair to this PREVENTIVE control and is authored on the OCI Logging page in Phase 9.

Remediation — OCI CLI

# Inventory: list every policy in the tenancy and grep for tenancy-wide manage.
oci iam policy list --compartment-id $OCI_TENANCY_OCID \
  --query 'data[].{name:name,id:id,statements:statements}' --output json \
  | jq -r '.[] | select(.statements[] | test("manage all-resources in tenancy"; "i")) | .name'

# Replace a too-broad statement with a compartment-scoped equivalent.
oci iam policy update \
  --policy-id  \
  --statements '["Allow group ComputeAdmins to manage instance-family in compartment Workload", "Allow group ComputeAdmins to use virtual-network-family in compartment Workload"]'

# Detective companion (authored in Phase 9): Cloud Guard's policy detector
# surfaces statements that drift toward over-broad grants between reviews.

Remediation — Terraform

# Terraform OCI provider ~> 5.0
# Least-privilege policy authored against OCI's human-readable policy syntax.
# Verbs (inspect, read, use, manage) and resource-types (instance-family,
# virtual-network-family, object-family) are scoped to one compartment.
resource "oci_identity_policy" "compute_admins_workload" {
  compartment_id = var.workload_compartment_id
  name           = "compute-admins-workload"
  description    = "ComputeAdmins manage compute + network in compartment Workload only."
  statements = [
    "Allow group ComputeAdmins to manage instance-family in compartment Workload",
    "Allow group ComputeAdmins to use   virtual-network-family in compartment Workload",
    "Allow group ComputeAdmins to read  audit-events in compartment Workload"
  ]
}

# Companion: a tenancy-level guard policy whose statements deliberately omit
# any "manage all-resources in tenancy" — making the absence auditable.
resource "oci_identity_policy" "tenancy_admin_guard" {
  compartment_id = var.tenancy_ocid
  name           = "tenancy-admin-guard"
  description    = "Tenancy-level policy: no group except TenancyAdmins receives tenancy-wide manage."
  statements = [
    "Allow group TenancyAdmins to manage all-resources in tenancy"
  ]
}

Remediation — OCI Resource Manager

# Submit the Terraform block above to OCI Resource Manager via a configured
# Git source-provider. Variables are entered through the Console UI (schema-driven
# by an optional schema.yaml); state is stored in OCI Object Storage automatically.
# This is an INVOCATION snippet — the .tf body is the existing Terraform block
# on this same control-box (do NOT duplicate HCL here).
oci resource-manager stack create-from-git-provider \
  --compartment-id "$COMPARTMENT_OCID" \
  --config-source-provider-id "$CONFIG_SRC_PROVIDER_OCID" \
  --repository-url "https://example.com/org/hardening-iac" \
  --branch-name "main" \
  --working-directory "modules/oci-iam-08-policy-least-privilege" \
  --display-name "oci-iam-08-policy-least-privilege" \
  --terraform-version "1.5.x"

# Plan + apply via the ORM job lifecycle (state stored in OCI automatically).
STACK_OCID=$(oci resource-manager stack list \
  --compartment-id "$COMPARTMENT_OCID" \
  --display-name "oci-iam-08-policy-least-privilege" \
  --query 'data[0].id' --raw-output)
PLAN_JOB_OCID=$(oci resource-manager job create-plan-job \
  --stack-id "$STACK_OCID" --query 'data.id' --raw-output)
oci resource-manager job create-apply-job \
  --stack-id "$STACK_OCID" \
  --execution-plan-strategy FROM_PLAN_JOB \
  --execution-plan-job-id "$PLAN_JOB_OCID"

Compliance mapping

CIS AWS Foundations v3.0.0 CIS Microsoft Azure Foundations v3.0.0 CIS GCP Foundation v4.0.0 CIS OCI Foundation v2.0.0 NIST SP 800-53 rev5 ISO/IEC 27001:2022 ISO/IEC 27017:2015
best-practices1.1.7best-practicesbest-practices AC-6(1); AC-6(2)A.5.15CLD.6.3.1

Log signals

  • OCI Logging Analytics records where 'Log Source' = 'OCI Audit Logs' with eventName in (CreatePolicy, UpdatePolicy) whose statement body matches the literal phrase manage all-resources in tenancy.
  • Policy diffs in which a previously compartment-scoped statement is replaced with a tenancy-scoped statement of the same verb-resource pair.
  • Cloud Guard policy-advisor findings flagging statements with verb manage on resource type all-resources attached at the tenancy compartment.

Query

'Log Source' = 'OCI Audit Logs'
          and 'Service Name' = 'identity'
          and eventName in ('CreatePolicy', 'UpdatePolicy')
          | eval stmt = data.request.payload.statements
          | where stmt like '%manage all-resources in tenancy%'
          | stats count by 'User Name', data.target.policy.name, 'Compartment Name'

Run continuously; this is the canonical over-broad grant pattern and must never appear outside the dedicated TenancyAdmins policy.

Alert threshold

  • Any new policy outside tenancy-admin-guard containing the manage all-resources in tenancy string — page immediately.
  • Cloud Guard policy-advisor risk rating of HIGH on a tenancy-scoped policy statement — escalate within one business day.

Initial response

  1. Revert the policy to its prior version via the OCI console policy history view or the Resource Manager state; OCI Identity preserves the last 50 versions of every policy.
  2. Re-scope the intent: convert manage all-resources in tenancy to manage {resource-type-family} in compartment {workload} using the verb-and-resource-family pair the workload actually needs.
  3. Notify the policy owner and record the rollback decision in the IAM change log per general/ir.html.

References

Equivalent on: AWS · Azure · GCP

Sources