OCI DevOps Build Pipelines: CI/CD for Container Workloads with Terraform

OCI DevOps Build Pipelines provide managed CI inside your OCI tenancy. Build runners are ephemeral, spinning up for each job and terminating when the build completes. Source code and secrets stay inside OCI, accessed through IAM Dynamic Groups rather than credentials stored outside your control. This post covers creating a full build pipeline in Terraform: GitHub mirroring, Docker image build, OCIR push, and automatic deployment trigger on every merge to main.

Step 1: IAM Policy

resource "oci_identity_dynamic_group" "devops_builders" {
  compartment_id = var.tenancy_ocid
  name           = "devops-build-pipelines"
  description    = "OCI DevOps build pipeline runners"
  matching_rule  = "All {resource.type = 'devopsbuildpipeline', resource.compartment.id = 'COMPARTMENT_OCID'}"
}

resource "oci_identity_policy" "devops_build" {
  compartment_id = var.compartment_id
  name           = "devops-build-policy"
  statements = [
    "Allow dynamic-group devops-build-pipelines to manage repos in compartment id COMPARTMENT_OCID",
    "Allow dynamic-group devops-build-pipelines to read secret-bundle in compartment id COMPARTMENT_OCID",
    "Allow dynamic-group devops-build-pipelines to manage devops-family in compartment id COMPARTMENT_OCID"
  ]
}

Step 2: DevOps Project and GitHub Mirror

resource "oci_ons_notification_topic" "devops_builds" {
  compartment_id = var.compartment_id
  name           = "devops-build-notifications"
}

resource "oci_devops_project" "orders_platform" {
  compartment_id = var.compartment_id
  name           = "orders-platform"
  notification_config {
    topic_id = oci_ons_notification_topic.devops_builds.id
  }
  defined_tags = {
    "Operations.Environment" = "production"
    "Operations.ManagedBy"   = "terraform"
  }
}

resource "oci_devops_connection" "github" {
  project_id      = oci_devops_project.orders_platform.id
  display_name    = "github-orders-api"
  connection_type = "GITHUB_ACCESS_TOKEN"
  access_token    = var.github_pat_vault_secret_id
}

resource "oci_devops_repository" "orders_api" {
  project_id      = oci_devops_project.orders_platform.id
  name            = "orders-api"
  repository_type = "MIRRORED"
  mirror_repository_config {
    connector_id   = oci_devops_connection.github.id
    repository_url = "https://github.com/yourorg/orders-api.git"
    trigger_schedule { schedule_type = "NONE" }
  }
}

Step 3: Build Pipeline and Stages

resource "oci_devops_build_pipeline" "orders_api" {
  project_id   = oci_devops_project.orders_platform.id
  display_name = "orders-api-build"

  build_pipeline_parameters {
    items {
      name          = "IMAGE_TAG"
      default_value = "latest"
    }
  }
}

resource "oci_devops_build_pipeline_stage" "build" {
  build_pipeline_id         = oci_devops_build_pipeline.orders_api.id
  display_name              = "build-docker-image"
  build_pipeline_stage_type = "BUILD"
  image                     = "OL7_X86_64_STANDARD_10"
  build_spec_file           = "build_spec.yaml"

  build_source_collection {
    items {
      connection_type = "DEVOPS_CODE_REPOSITORY"
      branch          = "main"
      name            = "orders-api-source"
      repository_id   = oci_devops_repository.orders_api.id
      repository_url  = "https://devops.scmservice.me-jeddah-1.oci.oraclecloud.com/namespaces/NS/projects/orders-platform/repositories/orders-api"
    }
  }

  build_runner_shape_config { memory_in_gbs = 8; ocpus = 2 }
  stage_execution_timeout_in_seconds = 3600

  build_pipeline_stage_predecessor_collection {
    items { id = oci_devops_build_pipeline.orders_api.id }
  }
}

resource "oci_devops_build_pipeline_stage" "deliver" {
  build_pipeline_id         = oci_devops_build_pipeline.orders_api.id
  display_name              = "push-to-ocir"
  build_pipeline_stage_type = "DELIVER_ARTIFACT"

  deliver_artifact_collection {
    items {
      artifact_id   = var.orders_api_artifact_ocid
      artifact_name = "orders-api-image"
    }
  }

  build_pipeline_stage_predecessor_collection {
    items { id = oci_devops_build_pipeline_stage.build.id }
  }
}

resource "oci_devops_build_pipeline_stage" "deploy" {
  build_pipeline_id              = oci_devops_build_pipeline.orders_api.id
  display_name                   = "trigger-deployment"
  build_pipeline_stage_type      = "TRIGGER_DEPLOYMENT_PIPELINE"
  deploy_pipeline_id             = var.deploy_pipeline_ocid
  is_pass_all_parameters_enabled = true

  build_pipeline_stage_predecessor_collection {
    items { id = oci_devops_build_pipeline_stage.deliver.id }
  }
}

Step 4: GitHub Webhook Trigger

resource "oci_devops_trigger" "github_push" {
  project_id     = oci_devops_project.orders_platform.id
  display_name   = "github-main-push-trigger"
  trigger_source = "GITHUB"

  actions {
    build_pipeline_id = oci_devops_build_pipeline.orders_api.id
    type              = "TRIGGER_BUILD_PIPELINE"
    filter {
      trigger_source = "GITHUB"
      events         = ["PUSH"]
      include {
        head_ref = "main"
      }
    }
  }
}

output "webhook_url" {
  value       = oci_devops_trigger.github_push.trigger_url
  description = "Add this URL as a GitHub repository webhook for push events"
}

Operational Notes

Build runners are ephemeral and start fresh on every job. Any dependency caching must use Object Storage or pre-built base images with dependencies already installed. This is a security benefit: no state leaks between builds from different branches or different repositories sharing the same runner.

Reference all credentials in the build spec using vaultVariables, not environment variables set in the pipeline. The runner authenticates to Vault using Resource Principal with the Dynamic Group policy. Credentials in vaultVariables are injected at runtime and do not appear in build logs, whereas environment variables set at the pipeline level may be visible in diagnostic output.

Regards,
Osama

#OCI #OracleCloud #DevOps #CICD #Terraform #BuildPipeline #IaC #TechBlog #Oracle #PlatformEngineering #ContainerRegistry #OKE #Docker #GitOps #CloudNative #OracleCloudInfrastructure #Automation #SoftwareDelivery #DevSecOps #BuildAutomation

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.