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