Every CI/CD pipeline that touches Oracle Cloud Infrastructure eventually hits the same wall: something needs an API key. A Terraform apply, a container push, a database migration script — all of it wants long-lived credentials sitting in a secrets manager or, worse, a pipeline variable. Those keys don't rotate themselves, they don't know which job invoked them, and when a build server is compromised they become the attacker's cloud console. OCI dynamic groups CI/CD authentication solves this by letting your pipeline runners prove who they are using cryptographic identity instead of a shared secret. By the end of this guide you'll have a dynamic group with matching rules scoped to your build infrastructure, IAM policies that grant it exactly the access it needs, and a working pipeline — whether it runs on OCI DevOps or on GitHub Actions — that authenticates without a single stored API key.
Why OCI Dynamic Groups CI/CD Authentication Matters
A dynamic group in OCI is not a static list of principals — it's a rule-based bucket that OCI evaluates at request time. Instead of adding a resource's OCID to a group manually, you write a matching rule that says "any compute instance in this compartment" or "any OCI DevOps build run tagged like this" belongs to the group. IAM policies are then written against the dynamic group, not against individual resources. For CI/CD, this is the difference between a pipeline that carries a bearer token for its entire lifetime and one that mints a short-lived, request-scoped identity every time it needs to touch a cloud resource. If a build agent is compromised, there's no key to steal — the attacker would need to run workloads inside your actual compute or DevOps environment, which is a much higher bar.
Step 1: Decide Which Principal Type Fits Your Pipeline
Before writing any rules, identify what's actually running your CI/CD jobs:
- Instance principals — your runners are OCI compute instances (self-hosted GitHub Actions runners, GitLab runners, Jenkins agents on OCI VMs).
- Resource principals — your jobs run inside OCI DevOps build pipelines, Functions, or Data Science jobs.
- Workload identity federation — your jobs run outside OCI entirely, such as on GitHub-hosted Actions runners, and need to federate in via OIDC.
Each maps to a different matching rule syntax, but the downstream IAM policy pattern is identical. Most teams end up combining at least two of these — instance principals for self-hosted build fleets and federation for SaaS-hosted CI.
Step 2: Create the Dynamic Group and Write Matching Rules
In the OCI Console, go to Identity & Security > Domains > Dynamic Groups and create a new group. The core of the configuration is the matching rule — this is where most misconfigurations happen, so start narrow and widen only when necessary.
For instance-principal runners scoped to a compartment:
ALL {instance.compartment.id = 'ocid1.compartment.oc1..aaaaaaaabuildfleet'}
For runners tagged with a specific freeform tag (recommended so you can rotate the group's scope without redeploying infrastructure):
ANY {instance.compartment.id = 'ocid1.compartment.oc1..aaaaaaaabuildfleet',
tag.namespace.ci-role.value = 'pipeline-runner'}
For OCI DevOps service authentication, the resource principal comes from the build pipeline itself, not a compute instance:
ALL {resource.type = 'devopsbuildpipeline',
resource.compartment.id = 'ocid1.compartment.oc1..aaaaaaaadevops'}
You can scope this further to a single pipeline OCID if you want per-pipeline blast-radius control rather than compartment-wide trust — worth doing for anything that deploys to production.
Step 3: Configure OCI Federation for GitHub Actions
If your pipelines run on GitHub-hosted runners rather than OCI compute, you need OCI federation GitHub Actions to be set up before instance principals will work — GitHub's hosted runners aren't OCI resources, so there's no instance or resource principal to match against. OCI supports this via its identity domain's OIDC federation, where GitHub Actions' own OIDC token issuer (token.actions.githubusercontent.com) is registered as an external identity provider, and a mapping rule ties incoming claims (repository, branch, environment) to an OCI dynamic group membership equivalent.
At a high level, the configuration involves:
- Registering GitHub's OIDC issuer URL and thumbprint in your identity domain's federation settings.
- Defining a claims mapping so
repository:your-org/your-repo:ref:refs/heads/mainmaps to a specific group. - Requesting a short-lived token in your workflow instead of reading a secret:
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Request OIDC token and exchange for OCI session
run: |
OIDC_TOKEN=$(curl -sLS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=oci")
# Exchange OIDC_TOKEN for a scoped OCI session via your token broker
Most teams front this exchange with a small broker function (an OCI Function works well) so the workflow YAML never needs OCI-specific SDK logic — it just receives short-lived, scoped credentials for that job run.
Step 4: Grant IAM Policies to the Dynamic Group
With the group and matching rule in place, write policies against the group name, not against individuals:
Allow dynamic-group CI-Pipeline-Runners to manage objects in compartment build-artifacts
Allow dynamic-group CI-Pipeline-Runners to use ons-topic in compartment notifications
Allow dynamic-group DevOps-Pipeline-Group to manage repos in compartment source-control
Scope every verb and resource type as tightly as the pipeline actually needs. manage on an entire compartment is a common shortcut that turns a scoped identity strategy back into a broad one — resist it unless the pipeline genuinely needs full lifecycle control over that resource type.
Step 5: Update Your Pipeline to Use Principal-Based Auth
Remove the OCI config file and API key from your pipeline's secrets store, then update the SDK or CLI initialization to use the appropriate principal:
# Instance principal (self-hosted runner on OCI compute)
oci os object list --bucket-name build-artifacts --auth instance_principal
# Resource principal (inside an OCI DevOps build pipeline)
oci os object list --bucket-name build-artifacts --auth resource_principal
For SDKs, the equivalent is swapping ConfigFileAuthenticationDetailsProvider for InstancePrincipalsAuthenticationDetailsProviderBuilder or ResourcePrincipalAuthenticationDetailsProvider in your build scripts. This is usually a two- or three-line change per script, and it's the step that finally lets you delete the stored key.
Step 6: Test With a Least-Privilege Dry Run
Before rolling this out to production pipelines, run a single low-stakes job through the new path and confirm the identity resolves correctly:
oci iam dynamic-group list --compartment-id <tenancy-ocid> \
--query "data[?name=='CI-Pipeline-Runners']"
Then, from inside the runner itself, verify the request is actually being evaluated against your rule rather than falling back to a cached config:
oci os ns get --auth instance_principal --debug
Watch the debug output for the resource principal or instance principal token exchange — if you instead see a config-file lookup, your SDK initialization order is wrong and it's still reading old credentials.
Troubleshooting and Verification
"NotAuthorizedOrNotFound" errors despite a correct-looking policy. This is almost always a matching rule that doesn't actually match the resource making the request. Double-check compartment OCIDs character-for-character — a dynamic group scoped to the wrong compartment fails silently rather than throwing a rule-syntax error.
Instance principal calls work locally but fail in the pipeline. Confirm the compute instance's compartment hasn't changed after a redeploy, and that the matching rule uses ALL versus ANY as intended — a rule requiring multiple conditions with ANY when you meant ALL will over-match and can silently include instances you didn't intend to trust.
OIDC federation token exchange returns 401. Check that the GitHub Actions workflow requests id-token: write in its permissions block — without it, ACTIONS_ID_TOKEN_REQUEST_TOKEN is empty and the exchange fails before it reaches OCI. Also verify the audience claim matches exactly what your federation configuration expects.
Resource principal auth fails inside OCI DevOps builds. OCI DevOps service authentication requires the build pipeline's resource principal session token to be present in the build environment — confirm the build pipeline stage explicitly has resource principal auth enabled in its runtime configuration, since it isn't on by default for every stage type.
Validate the whole chain periodically, not just at setup. Run a scheduled job that lists dynamic group membership and diffs it against your expected matching rule scope, so a compartment reorganization or a mistakenly retagged instance doesn't quietly widen who can assume your pipeline's identity.
How Safeguard Helps
Getting dynamic groups and matching rules right is a one-time configuration exercise, but keeping them right as your pipeline topology changes is the harder, ongoing problem — new repos get added, compartments get restructured, and matching rules quietly drift wider than intended. Safeguard continuously maps the identities your CI/CD pipelines actually use against the OCI policies and dynamic group rules that grant them access, flagging over-broad matching rules, stale federation claims tied to deleted branches or repos, and pipelines that still have unused static API keys sitting alongside their new principal-based auth. Instead of trusting that a dynamic group configuration written six months ago still reflects least privilege, Safeguard gives you a live view of which build systems can reach which cloud resources, so a supply chain compromise in one pipeline can't quietly pivot into broader access than that pipeline was ever supposed to have.