Skip to main content

OCI Resource Principals — The Passwordless Superpower of OCI Data Science

Calculating read time…

Imagine you work at a big company. 🏢


Every time you need a file from the archive room, you have to walk there, show your ID, type your password, and sign a form.
Now imagine instead — your employee badge automatically opens every door you are authorised for. No password. No signing. Just walk in! 🪄
That is exactly what Resource Principals do for your OCI services.




The Problem Resource Principals Solve

When your code (running inside an OCI Notebook, a Function, or a Job) needs to call another OCI service — like reading from Object Storage or calling the Vision API — it needs to prove its identity to OCI first.

Without Resource Principals, the traditional approach was:

  • Create a human user in OCI IAM 👤
  • Generate an API key for that user 🔑
  • Download the private key file .pem 📄
  • Copy that key file into every machine, notebook, and server 📋
  • Store the key file path in a config file ~/.oci/config ⚙️
  • Pray nobody accidentally leaks that key file online! 😰
❌ Why this old approach is dangerous:

  • Key files can be accidentally committed to GitHub — exposing your entire tenancy!
  • Keys need to be manually rotated every 90 days — easy to forget
  • When a team member leaves, you have to hunt down every place their key was copied
  • Keys are static credentials — if stolen once, the attacker has permanent access

Resource Principals eliminate ALL of these problems. No key files. No passwords. No config files. Ever. 

🎭 What Exactly is a Resource Principal?

A Resource Principal is an identity that OCI automatically assigns to a resource (like a Notebook Session, a Function, or a Compute Instance) — instead of a human user.

The resource itself becomes a first-class citizen in OCI's identity system. It can authenticate itself, request access to other services, and receive short-lived, automatically rotating tokens from OCI — all without any human involvement.

💡 Real-World Analogy — The Smart Employee Badge:

Think of Resource Principals like a smart employee badge that knows who you are. 🪪

When you (the OCI resource — e.g. a Notebook Session) walk into a new building (call an OCI service), the building's security system checks your badge automatically.
Your badge refreshes its own access codes every few minutes.
No human IT admin needs to issue new passwords.
When you leave the company (the resource is deleted), the badge stops working instantly. There is nothing to revoke, no keys to hunt down! 🎯

⚙️ How Resource Principals Work — Under the Hood

Let's look at the exact sequence of events when your OCI Notebook Session uses a Resource Principal to call Object Storage:

  ┌─────────────────────────────────────────────────────────────────────────┐
  │            RESOURCE PRINCIPAL AUTHENTICATION FLOW                       │
  └─────────────────────────────────────────────────────────────────────────┘

  YOUR NOTEBOOK                 OCI METADATA SERVICE         OCI IAM SERVICE
  (Running Code)                (Inside OCI Network)         (Security Engine)
  ───────────────               ────────────────────         ─────────────────

  1. Your code calls            2. OCI injects a            3. IAM verifies
     ads.set_auth                  temporary security           the notebook is
     ("resource_principal")  ───►  token into the         ───► in the Dynamic
                                   notebook's local             Group with correct
                                   metadata endpoint            policies ✅
                                   169.254.0.2

                                                            4. IAM issues a
                                                               short-lived
                                                               token (valid
                                                               ~15 minutes)
                                   ◄──────────────────────────

  5. Your code uses             6. Object Storage
     the token to call     ───►   checks the token,
     Object Storage               finds it valid,
                                  returns the data ✅
  ◄──────────────────────────────

The key insight: tokens last only ~15 minutes and are auto-refreshed. Even if someone somehow intercepted a token, it would expire before they could use it! 🔒

The Three Building Blocks of Resource Principals

  • 👥 Dynamic Group — A special kind of group in OCI IAM that contains resources (not users). You define a rule like: "All Notebook Sessions in compartment X are members of this group." OCI automatically adds/removes resources as they are created or deleted.
  • 📋 IAM Policy — A permission statement that grants the Dynamic Group access to specific OCI services. Like: "This group of Notebooks can read from Object Storage."
  • 🔄 Instance Metadata Service (IMDS) — A special internal-only endpoint (169.254.0.2) available inside every OCI resource. Your code contacts this endpoint to get a fresh token automatically. It never needs to go to the internet — it is like an intercom to OCI's security desk!

🏗️ Step-by-Step Setup — Enable Resource Principals for OCI Data Science

Step 1: Create a Dynamic Group

A Dynamic Group is a group whose members are defined by a matching rule, not by manual user additions. As soon as a resource matches the rule, it automatically joins the group.

📌 What does the rule below do?

This matching rule tells OCI: "Any Notebook Session or Data Science Job that exists inside our compartment automatically becomes a member of this Dynamic Group." Like a club that automatically accepts anyone from your school district! 🏫
  • Go to OCI Console → Identity & Security → Dynamic Groups
  • Click "Create Dynamic Group"
  • Name it: DataScienceResourceGroup
  • Add these matching rules:
-- Dynamic Group Matching Rule for OCI Data Science
-- Matches ALL Notebook Sessions in the compartment
ALL {
  resource.type = 'datasciencenotebooksession',
  resource.compartment.id = 'ocid1.compartment.oc1..your_compartment_id'
}

-- ALSO match Data Science Jobs (for automated pipeline runs)
ALL {
  resource.type = 'datasciencejobrun',
  resource.compartment.id = 'ocid1.compartment.oc1..your_compartment_id'
}

-- ALSO match Model Deployments (for inference endpoints)
ALL {
  resource.type = 'datasciencemodeldeployment',
  resource.compartment.id = 'ocid1.compartment.oc1..your_compartment_id'
}
💡 Pro Tip — Use the combined rule format:

You can combine all three into one Dynamic Group using ANY:
ANY {
  resource.type = 'datasciencenotebooksession',
  resource.type = 'datasciencejobrun',
  resource.type = 'datasciencemodeldeployment'
}
AND resource.compartment.id = 'ocid1.compartment.oc1..your_compartment_id'
This is cleaner and easier to maintain! ✨

Step 2: Create IAM Policies for the Dynamic Group

Creating the Dynamic Group only identifies who the resources are. Now we write policies to define what they are allowed to do. 📋

📌 What do the policies below do?

These policy statements grant your Notebook Sessions and Jobs the permissions they need to do their work — read data, save models, call AI services — all without needing any API keys or passwords! 🗝️➡️🚫
-- OCI IAM Policies for Data Science Resource Principals
-- Create these in OCI Console → Identity → Policies

-- 1. Allow notebooks to read/write data in Object Storage
Allow dynamic-group DataScienceResourceGroup to manage objects
  in compartment DataScienceCompartment
  where target.bucket.name = 'datalake-curated'

Allow dynamic-group DataScienceResourceGroup to read buckets
  in compartment DataScienceCompartment

-- 2. Allow notebooks to save and load models from Model Catalog
Allow dynamic-group DataScienceResourceGroup to manage data-science-models
  in compartment DataScienceCompartment

-- 3. Allow notebooks to call OCI AI Vision service
Allow dynamic-group DataScienceResourceGroup to use ai-service-vision-family
  in tenancy

-- 4. Allow notebooks to call OCI Language service
Allow dynamic-group DataScienceResourceGroup to use ai-service-language-family
  in tenancy

-- 5. Allow notebooks to call OCI Generative AI (GenAI) service
Allow dynamic-group DataScienceResourceGroup to use generative-ai-family
  in tenancy

-- 6. Allow notebooks to read secrets from OCI Vault
Allow dynamic-group DataScienceResourceGroup to read secret-family
  in compartment DataScienceCompartment

-- 7. Allow notebooks to log outputs to OCI Logging
Allow dynamic-group DataScienceResourceGroup to use log-content
  in compartment DataScienceCompartment
✅ Principle of Least Privilege — Always grant minimum required access:

Only grant the permissions your notebooks actually need. If your notebook only reads data and never writes, use read objects not manage objects. This limits the blast radius if something ever goes wrong! 🛡️

💻 Step 3 — Use Resource Principals in Your Code

Now the beautiful part — using Resource Principals in Python is ridiculously simple. One line of code replaces the entire ~/.oci/config file setup! ✨

Method 1: Using OCI ADS SDK (Recommended for Data Science)

📌 What does the code below do?

This code tells the OCI Python library: "Don't look for a config file or API key. Instead, use the Resource Principal identity of the machine I am currently running on." After that one line, ALL subsequent OCI calls are automatically authenticated — like swiping your badge once and staying logged in all day! 🪪
import ads
import pandas as pd
import oci

# ── THE ONE MAGIC LINE ──────────────────────────────────────────────────────
# This tells OCI: "Use the Resource Principal of this Notebook Session"
# No config file. No API key. No password. Nothing else needed!
ads.set_auth(auth="resource_principal")
# ───────────────────────────────────────────────────────────────────────────

# Now use it to read Parquet data from Object Storage
print("📥 Reading data from Object Storage using Resource Principal...")
df = pd.read_parquet(
    "oci://datalake-curated@your_namespace/sales/year=2026/month=04/",
    storage_options={"config": {}}
)
print(f"✅ Loaded {len(df):,} rows — no password needed! 🎉")

# Use it to call OCI Object Storage SDK
config     = oci.auth.signers.get_resource_principals_signer()
os_client  = oci.object_storage.ObjectStorageClient({}, signer=config)
namespace  = os_client.get_namespace().data
print(f"\n✅ Connected to Object Storage!")
print(f"   Namespace: {namespace}")
print(f"   Auth type: Resource Principal (no keys!)")

Output:

📥 Reading data from Object Storage using Resource Principal...
✅ Loaded 124,901 rows — no password needed! 🎉

✅ Connected to Object Storage!
   Namespace: ocitenancyexample
   Auth type: Resource Principal (no keys!)

Method 2: Using the OCI SDK Directly

📌 What does the code below do?

This code uses the OCI Python SDK's built-in Resource Principal signer. The signer is like an automatic signature machine — every API request your code makes gets signed with the notebook's identity automatically, without you touching any keys or credentials! ✍️🤖
import oci

# Get the Resource Principal signer
# This contacts the metadata service at 169.254.0.2 internally
# and retrieves a fresh, rotating token — all automatically!
signer = oci.auth.signers.get_resource_principals_signer()

# Now pass this signer to ANY OCI service client
# Notice: the config dict is empty {} — no keys, no files needed!
object_storage_client = oci.object_storage.ObjectStorageClient(
    config={},
    signer=signer
)

ai_vision_client = oci.ai_vision.AIServiceVisionClient(
    config={},
    signer=signer
)

ai_language_client = oci.ai_language.AIServiceLanguageClient(
    config={},
    signer=signer
)

gen_ai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(
    config={},
    signer=signer,
    service_endpoint="https://inference.generativeai.ap-mumbai-1.oci.oraclecloud.com"
)

print("✅ All OCI service clients created using Resource Principal!")
print("   No API keys. No config file. No passwords.")
print("   Authentication is fully automatic and token-based. 🔒")

Output:

✅ All OCI service clients created using Resource Principal!
   No API keys. No config file. No passwords.
   Authentication is fully automatic and token-based. 🔒

🌍 Real-World Examples — Resource Principals in Action

Example 1: Notebook reads data + calls OCI GenAI — No keys!

📌 What does the code below do?

This is a complete real-world example: an OCI Notebook Session reads customer reviews from Object Storage, then passes them to OCI Generative AI for sentiment analysis — all using Resource Principal authentication. No API keys anywhere in the code! 🤖📄
import oci
import ads
import pandas as pd
import json

# ── Step 1: Authenticate using Resource Principal (one line!)
ads.set_auth(auth="resource_principal")
signer = oci.auth.signers.get_resource_principals_signer()

# ── Step 2: Load customer reviews from Object Storage
print("📥 Loading customer reviews from Object Storage...")
df = pd.read_csv(
    "oci://datalake-raw@your_namespace/reviews/customer_reviews_april.csv",
    storage_options={"config": {}}
)
print(f"   Loaded {len(df)} reviews")

# ── Step 3: Create OCI Generative AI client using Resource Principal
gen_ai_client = oci.generative_ai_inference.GenerativeAiInferenceClient(
    config={},
    signer=signer,
    service_endpoint="https://inference.generativeai.ap-mumbai-1.oci.oraclecloud.com"
)

# ── Step 4: Analyse each review using OCI GenAI
print("\n🤖 Analysing reviews with OCI Generative AI...")
results = []

for idx, row in df.head(5).iterrows():   # process first 5 for demo
    review_text = row["review"]

    # Build the prompt for GenAI
    prompt = f"""Analyse this customer review and respond in JSON only:
Review: "{review_text}"

Respond with this exact JSON format:
{{"sentiment": "Positive/Negative/Neutral", "score": 0.0-1.0, "key_issue": "brief summary"}}"""

    # Call OCI GenAI — authenticated via Resource Principal automatically!
    response = gen_ai_client.chat(
        oci.generative_ai_inference.models.ChatDetails(
            compartment_id="ocid1.compartment.oc1..your_compartment_id",
            serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(
                model_id="meta.llama-3.3-70b-instruct"
            ),
            chat_request=oci.generative_ai_inference.models.GenericChatRequest(
                messages=[
                    oci.generative_ai_inference.models.UserMessage(
                        content=[oci.generative_ai_inference.models.TextContent(text=prompt)]
                    )
                ],
                max_tokens=150,
                temperature=0.0
            )
        )
    )

    # Parse the JSON response
    reply_text = response.data.chat_response.choices[0].message.content[0].text
    analysis   = json.loads(reply_text)
    analysis["review"] = review_text[:60] + "..."
    results.append(analysis)

    print(f"   Review {idx+1}: {analysis['sentiment']} ({analysis['score']:.2f})")

# ── Step 5: Save results back to Object Storage using Resource Principal
results_df = pd.DataFrame(results)
results_df.to_parquet(
    "oci://datalake-curated@your_namespace/review_sentiment/results.parquet",
    storage_options={"config": {}}
)
print(f"\n✅ Analysis complete! Results saved to Object Storage.")
print(f"   No API keys were used anywhere in this code! 🔐")

Output:

📥 Loading customer reviews from Object Storage...
   Loaded 2,847 reviews

🤖 Analysing reviews with OCI Generative AI...
   Review 1: Positive (0.96)
   Review 2: Negative (0.88)
   Review 3: Neutral  (0.61)
   Review 4: Positive (0.91)
   Review 5: Negative (0.95)

✅ Analysis complete! Results saved to Object Storage.
   No API keys were used anywhere in this code! 🔐

Example 2: Data Science Job reads secrets from OCI Vault

📌 What does the code below do?

This code runs inside an OCI Data Science Job (an automated batch job). It needs a database password to connect to Oracle Autonomous Database. Instead of hardcoding the password in the code, it reads it securely from OCI Vault using Resource Principal authentication. Like asking the company's secure safe for a key, rather than keeping the key in your pocket! 🔐
import oci
import base64

# Resource Principal auth — works inside a Data Science Job too!
signer = oci.auth.signers.get_resource_principals_signer()

# Create a Vault client using Resource Principal
vault_client = oci.vault.VaultsClient(config={}, signer=signer)
secrets_client = oci.secrets.SecretsClient(config={}, signer=signer)

# The OCID of the secret stored in OCI Vault
# (Store your DB password here ONCE, then reference it by OCID)
db_password_secret_id = "ocid1.vaultsecret.oc1..your_secret_id"
db_username_secret_id = "ocid1.vaultsecret.oc1..your_username_secret_id"

# Fetch the secret value from Vault — Resource Principal handles auth!
print("🔐 Fetching database credentials from OCI Vault...")

password_bundle = secrets_client.get_secret_bundle(db_password_secret_id)
db_password = base64.b64decode(
    password_bundle.data.secret_bundle_content.content
).decode("utf-8")

username_bundle = secrets_client.get_secret_bundle(db_username_secret_id)
db_username = base64.b64decode(
    username_bundle.data.secret_bundle_content.content
).decode("utf-8")

print(f"✅ Credentials fetched from Vault!")
print(f"   Username : {db_username}")
print(f"   Password : {'*' * len(db_password)}  (hidden for security)")

# Now connect to the database using the fetched credentials
import oracledb
connection = oracledb.connect(
    user=db_username,
    password=db_password,
    dsn="your_adb_connection_string"
)
print(f"\n✅ Connected to Autonomous Database!")
print(f"   No passwords were hardcoded in this script. 🎉")

Output:

🔐 Fetching database credentials from OCI Vault...
✅ Credentials fetched from Vault!
   Username : admin_user
   Password : **************  (hidden for security)

✅ Connected to Autonomous Database!
   No passwords were hardcoded in this script. 🎉

Example 3: Verify Your Resource Principal is Working

📌 What does the code below do?

This is a quick diagnostic script you can run in any notebook to confirm that Resource Principal authentication is working correctly. It prints your notebook's identity, what compartment it belongs to, and whether the auth token is valid — like checking if your badge scanner is working! 🔍
import oci

print("🔍 Resource Principal Diagnostic Tool")
print("=" * 50)

try:
    # Attempt to get the Resource Principal signer
    signer = oci.auth.signers.get_resource_principals_signer()
    print("✅ Resource Principal signer created successfully!")

    # Inspect the signer's identity
    rp_region   = signer.region
    rp_tenancy  = signer.tenancy_id

    print(f"\n📋 Resource Principal Details:")
    print(f"   Region    : {rp_region}")
    print(f"   Tenancy   : {rp_tenancy}")

    # Try a real API call to confirm permissions are working
    identity_client = oci.identity.IdentityClient(config={}, signer=signer)
    tenancy = identity_client.get_tenancy(rp_tenancy)

    print(f"   Tenancy Name: {tenancy.data.name}")
    print(f"\n✅ Resource Principal is fully working!")
    print(f"   Authentication : Automatic token-based (no API keys)")
    print(f"   Token type     : Short-lived, auto-rotating")

except oci.exceptions.ServiceError as e:
    print(f"\n❌ Resource Principal is set up but lacks permissions!")
    print(f"   Error  : {e.message}")
    print(f"   Status : {e.status}")
    print(f"   Fix    : Check your Dynamic Group matching rule")
    print(f"            and IAM Policy for this resource type.")

except Exception as e:
    print(f"\n❌ Resource Principal is NOT available!")
    print(f"   Error: {str(e)}")
    print(f"   Fix  : You may be running outside OCI (e.g. local laptop).")
    print(f"          Resource Principals only work inside OCI resources.")

Output (when working correctly):

🔍 Resource Principal Diagnostic Tool
==================================================
✅ Resource Principal signer created successfully!

📋 Resource Principal Details:
   Region    : ap-mumbai-1
   Tenancy   : ocid1.tenancy.oc1..exampletenancyid

   Tenancy Name: MyCompanyTenancy

✅ Resource Principal is fully working!
   Authentication : Automatic token-based (no API keys)
   Token type     : Short-lived, auto-rotating

🖥️ Step 4 — What About Local Development? (Laptop / PC)

Resource Principals only work inside OCI resources. When you develop on your local laptop, there is no OCI metadata service available. Here is how to handle both environments gracefully:

📌 What does the code below do?

This is a smart authentication helper that detects where your code is running. If it detects it is inside an OCI resource (like a Notebook Session), it automatically uses Resource Principal. If it detects you are on a local laptop, it falls back to the regular config file. Like a light switch that auto-adjusts — night mode when it's dark, day mode when it's bright! 🌓
import oci
import os

def get_smart_signer():
    """
    Smart authentication: uses Resource Principal inside OCI,
    falls back to config file when running locally.
    """
    # Check if we are inside an OCI resource
    # The environment variable OCI_RESOURCE_PRINCIPAL_VERSION
    # is set automatically by OCI on all supported resources
    is_inside_oci = os.environ.get("OCI_RESOURCE_PRINCIPAL_VERSION") is not None

    if is_inside_oci:
        print("🔑 Auth mode: Resource Principal (running inside OCI)")
        signer = oci.auth.signers.get_resource_principals_signer()
        config = {}   # empty config — not needed with Resource Principal
    else:
        print("🔑 Auth mode: Config file (running locally on laptop)")
        config = oci.config.from_file()   # reads ~/.oci/config
        signer = oci.signer.Signer(
            tenancy=config["tenancy"],
            user=config["user"],
            fingerprint=config["fingerprint"],
            private_key_file_location=config["key_file"],
            pass_phrase=config.get("pass_phrase")
        )

    return config, signer


# Use it anywhere in your code
config, signer = get_smart_signer()

# Works on BOTH your laptop AND OCI Notebook — same code! ✅
os_client = oci.object_storage.ObjectStorageClient(config=config, signer=signer)
namespace = os_client.get_namespace().data
print(f"✅ Connected to Object Storage! Namespace: {namespace}")

Output (inside OCI Notebook):

🔑 Auth mode: Resource Principal (running inside OCI)
✅ Connected to Object Storage! Namespace: ocitenancyexample

Output (on local laptop):

🔑 Auth mode: Config file (running locally on laptop)
✅ Connected to Object Storage! Namespace: ocitenancyexample

Same code works in both places! No changes needed when moving from laptop to OCI. 🎉

📊 Resource Principal vs Other Auth Methods — Comparison

  ┌─────────────────────────┬──────────────────┬──────────────┬────────────────┐
  │  FEATURE                │ Resource         │ API Key +    │ Instance       │
  │                         │ Principal ⭐     │ Config File  │ Principal      │
  ├─────────────────────────┼──────────────────┼──────────────┼────────────────┤
  │ No key file needed      │ ✅ Yes           │ ❌ No        │ ✅ Yes         │
  │ Works inside OCI        │ ✅ Yes           │ ✅ Yes       │ ✅ Yes         │
  │ Works on laptop         │ ❌ No            │ ✅ Yes       │ ❌ No          │
  │ Tokens auto-rotate      │ ✅ Yes           │ ❌ No        │ ✅ Yes         │
  │ Risk if stolen          │ Very Low (15min) │ High (perm.) │ Very Low       │
  │ Setup complexity        │ Low              │ Medium       │ Medium         │
  │ Best for                │ Data Science,    │ Local dev,   │ Compute VMs    │
  │                         │ Functions, Jobs  │ CI/CD        │ only           │
  └─────────────────────────┴──────────────────┴──────────────┴────────────────┘

For OCI Data Science, Resource Principal is always the best choice — it is the most secure, requires no maintenance, and Oracle recommends it officially. 🏆

🔧 Troubleshooting Common Resource Principal Issues

Problem 1: "NotAuthenticated" or "401 Unauthorized"

  • 🔍 Check: Is your Notebook Session in the right compartment?
    Your Dynamic Group rule must match the compartment where the notebook lives.
  • 🔍 Check: Does the Dynamic Group exist?
    Go to Identity → Dynamic Groups and confirm it is listed.
  • 🔍 Check: Does the resource type match?
    For Notebook Sessions, use resource.type = 'datasciencenotebooksession'

Problem 2: "NotAuthorized" or "403 Forbidden"

  • 🔍 Check: Does your IAM Policy reference the correct Dynamic Group name? Names are case-sensitive!
  • 🔍 Check: Is the policy in the right compartment scope?
    Policies in a child compartment do not automatically apply to parent compartments.
  • 🔍 Check: Does the policy grant sufficient access?
    read objects vs manage objects — make sure the verb is correct.

Problem 3: Works in Notebook but Not in Job

💡 Remember:

Notebook Sessions and Job Runs are different resource types!
Your Dynamic Group needs to include both:
resource.type = 'datasciencenotebooksession'
resource.type = 'datasciencejobrun'
Check your matching rule includes both if you use both features. ✅

Quick Debug Checklist

📌 What does the code below do?

This is a one-stop debug script that checks your Resource Principal setup and tells you exactly what is missing or wrong. Run it at the very start of any new notebook to confirm everything is configured correctly before writing any actual code! 🩺
import oci
import os

print("🩺 Resource Principal Health Check")
print("=" * 55)

# Check 1: Environment variable presence
rp_version = os.environ.get("OCI_RESOURCE_PRINCIPAL_VERSION")
print(f"\n1️⃣  OCI_RESOURCE_PRINCIPAL_VERSION : {rp_version or '❌ NOT SET (not inside OCI?)'}")

rp_region = os.environ.get("OCI_RESOURCE_PRINCIPAL_REGION_PARAMETER_V2")
print(f"2️⃣  OCI_RESOURCE_PRINCIPAL_REGION  : {rp_region or '❌ NOT SET'}")

# Check 2: Can we create a signer?
try:
    signer = oci.auth.signers.get_resource_principals_signer()
    print(f"3️⃣  Signer creation                : ✅ SUCCESS")
    print(f"   Region  : {signer.region}")
    print(f"   Tenancy : {signer.tenancy_id}")
except Exception as e:
    print(f"3️⃣  Signer creation                : ❌ FAILED — {str(e)[:80]}")

# Check 3: Can we call Object Storage?
try:
    os_client = oci.object_storage.ObjectStorageClient(config={}, signer=signer)
    ns = os_client.get_namespace().data
    print(f"4️⃣  Object Storage access          : ✅ SUCCESS (namespace: {ns})")
except oci.exceptions.ServiceError as e:
    print(f"4️⃣  Object Storage access          : ❌ FAILED — HTTP {e.status}: {e.message[:60]}")

# Check 4: Can we call IAM?
try:
    id_client = oci.identity.IdentityClient(config={}, signer=signer)
    tenancy = id_client.get_tenancy(signer.tenancy_id)
    print(f"5️⃣  IAM Tenancy access             : ✅ SUCCESS ({tenancy.data.name})")
except oci.exceptions.ServiceError as e:
    print(f"5️⃣  IAM Tenancy access             : ❌ FAILED — HTTP {e.status}: {e.message[:60]}")

print("\n" + "=" * 55)
print("📋 If any check failed:")
print("   1. Verify your Dynamic Group includes this resource type")
print("   2. Verify your IAM Policy references the correct Dynamic Group")
print("   3. Allow 2–3 minutes after policy changes for propagation")

Output (fully healthy setup):

🩺 Resource Principal Health Check
=======================================================

1️⃣  OCI_RESOURCE_PRINCIPAL_VERSION : 2.2
2️⃣  OCI_RESOURCE_PRINCIPAL_REGION  : ap-mumbai-1
3️⃣  Signer creation                : ✅ SUCCESS
   Region  : ap-mumbai-1
   Tenancy : ocid1.tenancy.oc1..exampletenancyid
4️⃣  Object Storage access          : ✅ SUCCESS (namespace: ocitenancyexample)
5️⃣  IAM Tenancy access             : ✅ SUCCESS (MyCompanyTenancy)

=======================================================
📋 If any check failed:
   1. Verify your Dynamic Group includes this resource type
   2. Verify your IAM Policy references the correct Dynamic Group
   3. Allow 2–3 minutes after policy changes for propagation

🏆 Benefits of Resource Principals — A Full Summary

  WITHOUT Resource Principals       WITH Resource Principals
  ──────────────────────────        ─────────────────────────
  ❌ API key files everywhere        ✅ Zero key files anywhere
  ❌ Keys shared across team         ✅ Each resource has its own identity
  ❌ Keys expire and break code      ✅ Tokens auto-rotate every 15 minutes
  ❌ Manual key rotation every 90d   ✅ No rotation needed — ever!
  ❌ Risk of key leaking to GitHub   ✅ Nothing to accidentally commit
  ❌ Hard to audit who did what      ✅ Clear audit trail per resource
  ❌ Offboarding is manual pain      ✅ Delete resource = access revoked
  ❌ Config file on every machine    ✅ One Dynamic Group rule for all
  ❌ Different code for local/OCI    ✅ Same code works (smart fallback)

✅ Best Practices for Resource Principals

  • 🎯 Always use Resource Principals inside OCI — never use API keys in notebooks, jobs, or functions. If you see ~/.oci/config in a notebook, that is a red flag! 🚩
  • 🏗️ One Dynamic Group per use case — create separate Dynamic Groups for dev, staging, and production. Each gets different IAM policies with different permission levels.
  • 🔍 Use OCI Audit Logs — every API call made by a Resource Principal is logged. Go to Observability → Audit to see exactly what each notebook accessed and when.
  • 🔐 Never grant admin access to notebooks — use the minimum permissions required. Notebooks should read data and call AI services — they should never manage IAM or billing.
  • 🧪 Test with the health check script first — run the diagnostic script at the top of every new notebook before writing any logic. Catches permission issues before you waste time debugging.
❌ Things to never do with Resource Principals:

  • Never grant manage all-resources in tenancy to a Dynamic Group — that is admin access for your entire cloud!
  • Never use Resource Principals on a local laptop — they won't work and will confuse you
  • Never skip the Dynamic Group and try to grant policies directly to individual resources
  • Never assume policies take effect instantly — allow 2–3 minutes after creating or changing policies

📝 Quick Summary — What We Learned

  • The Problem → API key files are risky, manual to manage, and easy to leak
  • What Resource Principals are → Automatic identities assigned to OCI resources (not people)
  • How they work → Short-lived tokens fetched from the local metadata service, auto-rotating every 15 minutes
  • Three Building Blocks → Dynamic Group + IAM Policy + Instance Metadata Service
  • Setup Steps → Create Dynamic Group with matching rule → Write IAM Policy → Use get_resource_principals_signer() in code
  • One Magic Line → ads.set_auth(auth="resource_principal") replaces all key-based auth
  • Smart Fallback → Detect OCI_RESOURCE_PRINCIPAL_VERSION env var to switch auth mode automatically
  • Benefits → Zero key files, auto-rotating tokens, clear audit trail, instant revocation on resource deletion

Resource Principals are the single biggest security upgrade you can make to your OCI Data Science setup. 🏆
One Dynamic Group, a few policy lines, and one line of Python code — and your entire platform becomes passwordless, keyless, and dramatically more secure! 🔒✨

Comments