cloud native notes

Technology, musings and anything else that comes to mind.

An OCI VisionAI Service and Cloud-navtive Pipeline

2025-09-23 post Matt Ferguson

Migrating Serverless Applications to OCI: End-to-End AI Vision Pipeline

I’ve been working on this project for some time, and I’m excited to finally share it with you. This post also serves as a preview of my upcoming session at Oracle CloudWorld / AI WorldStrategies for Migrating Serverless Applications to OCI [SHO1222] — where I’ll be co-presenting with my colleagues Tom Moore and Shadai Williams on October 15th 2025.
👉 Session link here.

If you’ve ever wondered how to stitch together OCI Functions, Object Storage, AI Vision, and Autonomous Database into a practical serverless architecture — this tutorial is for you.

Why This Matters (Use Cases)

The architecture we’ll build shows how build an end-to-end serverless pipeline in OCI and taking advantage of event-driven services and resource principals for secure, low-maintenance operation. Example use cases:

  • 📷 Image analysis pipelines: Auto-tagging, object detection, and metadata enrichment for uploaded files.
  • 🏢 Enterprise workflows: Automating back-office tasks like claims processing or inventory audits.
  • 🎓 Education & research: Students uploading images and receiving AI-assisted analysis through a web portal.
  • Modernization: Re-platforming serverless apps from other clouds into OCI with Terraform-based automation.

Project Overview

At a high level, this pipeline delivers an end-to-end serverless app on OCI:

  1. A Flask web UI uploads images to Object Storage.
  2. When new files arrive, an OCI Function runs AI Vision object detection.
  3. The JSON output is persisted in Autonomous JSON Database via ORDS REST APIs.
  4. The results are rendered back in the web app dashboard.

Repository Layout

The GitHub repo (link coming soon 👀) provides everything you need:

  • app/ # Flask web application (UI + APIs)
  • vision_function/ # Fn handler for AI Vision
  • main.tf # Terraform for full infra provisioning
  • terraform.tfvars.example
  • Dockerfile # Build Flask container image

Step 1: Provision OCI Resources with Terraform

While there’s a lot in step one I’m confident that once you have everything necessary you’ll be up and running in no time.

1a: Get an OCI account

You’ll need an Oracle Cloud Infrastructure account. If you don’t have one, sign up for a Free Tier:
https://www.oracle.com/cloud/free/

Tip: the Free Tier is enough to follow along and understand the workflow. Some services may require paid limits for production scale.

1b: Install the necessary dependencies

Quick checks:

oci -v
docker --version    # or: podman --version
terraform -version

1c: Collect the values you’ll put in terraform.tfvars

You’ll create an API key for your user and copy a few identifiers from the Console. Here’s exactly what you need and where to find each item:

  1. tenancy_ocidTenancy OCID

    • Console: click your avatar (top right) → Tenancy: Tenancy OCIDCopy.
  2. user_ocidUser OCID

    • Console: click your avatar → User settingsOCIDCopy.
  3. fingerprintAPI key fingerprint for your user

    • Console: User settingsAPI KeysAdd API Key → choose Paste Public Key (see key generation below) → after adding, copy the Fingerprint shown in the table.
  4. private_key_pathLocal path to your API key’s private key

    • You’ll store the private key on your machine (e.g., ~/.oci/oci_api_key.pem).
  5. regionYour home region code (e.g., us-ashburn-1, ca-toronto-1, etc.)

    • Console: top-nav region selector shows your current region’s code.
    • (Optional) CLI list:
      oci iam region list --query "data[].name" --raw-output
      
  6. compartment_ocidTarget compartment OCID

    • Console: Identity & SecurityCompartments → pick (or create) a compartment → Copy OCID.

Generate an API key pair (one-time)

If you don’t already have an OCI API key for your user:

mkdir -p ~/.oci
openssl genrsa -out ~/.oci/oci_api_key.pem 2048
openssl rsa -pubout -in ~/.oci/oci_api_key.pem -out ~/.oci/oci_api_key_public.pem
chmod 600 ~/.oci/oci_api_key.pem

1d: Create and fill in terraform.tfvars file

Copy the template below into a new file named terraform.tfvars in the repo root and replace the placeholder values with the ones you collected above.

# Copy this file to terraform.tfvars and fill in your values

# Find these values in the OCI Console:
tenancy_ocid      = "ocid1.tenancy.oc1..aaaaaaaa..."         # Console → Tenancy Information
user_ocid         = "ocid1.user.oc1..aaaaaaaa..."            # Console → User Settings
fingerprint       = "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"  # Console → User → API Keys → Fingerprint
private_key_path  = "~/.oci/oci_api_key.pem"                  # Local path to your private key
region            = "ca-toronto-1"                            # e.g., us-ashburn-1, ca-toronto-1
compartment_ocid  = "ocid1.compartment.oc1..aaaaaaaa..."      # Identity & Security → Compartments

1d Verify and Proceed

Run Terraform from the repo root:

terraform init
terraform plan   # should succeed and show the resources to be created

If plan succeeds, you’re ready for terraform apply in the next step of the tutorial.

If you hit auth or permission errors:

  • Re-check the fingerprint and private key path.
  • Confirm your user has permission to manage resources in the chosen compartment (IAM policies).
  • Make sure the region string exactly matches the Console (e.g., us-ashburn-1).

Here’s a simplified version of the Terraform configuration (main.tf):

provider "oci" {
  region = var.region
}

resource "oci_objectstorage_bucket" "images" {
  compartment_id = var.compartment_ocid
  name           = "image-uploads"
  namespace      = data.oci_objectstorage_namespace.ns.namespace
  storage_tier   = "Standard"
}

resource "oci_database_autonomous_database" "json_db" {
  compartment_id = var.compartment_ocid
  db_name        = "JSONDB"
  display_name   = "Autonomous JSON DB"
  workload       = "AJD"
  data_storage_size_in_tbs = 1
  cpu_core_count = 1
  is_free_tier   = true
}

This config creates an Object Storage bucket and an Autonomous JSON Database. The full repo includes networking, Vault, Fn application, container instance, and a public load balancer.

Step 2: Build the Flask Web App

The Flask app lets users upload images and view AI Vision results.

# app/app.py
from flask import Flask, request, render_template
import oci
import requests, json, os

app = Flask(__name__)

# OCI resource principal signer
signer = oci.auth.signers.get_resource_principals_signer()
object_storage_client = oci.object_storage.ObjectStorageClient(config={}, signer=signer)

@app.route("/", methods=["GET", "POST"])
def upload():
    if request.method == "POST":
        file = request.files["file"]
        object_storage_client.put_object(
            "my-namespace",
            "image-uploads",
            file.filename,
            file
        )
    return render_template("index.html")

The app uploads images into Object Storage and lists results from the Autonomous JSON Database via ORDS.

Step 3: Serverless AI Vision Function

The function is triggered automatically when a new file lands in Object Storage.

# vision_function/func.py
import io, json, oci

def handler(ctx, data: io.BytesIO = None):
    signer = oci.auth.signers.get_resource_principals_signer()
    vision_client = oci.ai_vision.AIServiceVisionClient(config={}, signer=signer)
    obj_storage_client = oci.object_storage.ObjectStorageClient(config={}, signer=signer)

    evt = json.loads(data.getvalue())
    bucket = evt["data"]["resourceName"].split("buckets/")[1].split("/")[0]
    name   = evt["data"]["resourceName"].split("/")[-1]

    # Run AI Vision
    resp = vision_client.analyze_image(
        analyze_image_details=oci.ai_vision.models.AnalyzeImageDetails(
            features=[oci.ai_vision.models.ImageObjectDetectionFeature()],
            image=oci.ai_vision.models.ImageObjectStorageUri(
                source_uri=f"oci://{bucket}@namespace/{name}"
            )
        )
    )

    detections = [obj.name for obj in resp.data.image_objects]
    # Persist via ORDS
    ords_url = os.environ["ORDS_URL"]
    payload = {"file": name, "detections": detections}
    requests.post(ords_url, auth=("user","pass"), json=payload)

    return {"status": "success", "detections": detections}

This uses AI Vision Object Detection and stores structured results in the JSON database.

Step 4: Connect Autonomous JSON Database via ORDS

No wallet, no drivers — just REST APIs.

Example insert via curl:

curl -u $DB_USER:$DB_PASS \
  -X POST $ORDS_URL/IMAGE_ANALYSIS/ \
  -H "Content-Type: application/json" \
  -d '{"file":"example.png","detections":["cat","sofa"]}'

Step 5: Deploy and Test

  1. Build and push your Flask container to OCIR:
docker build -t <region>.ocir.io/<tenancy>/flask-app .
docker push <region>.ocir.io/<tenancy>/flask-app
  1. Apply Terraform:
terraform init
terraform apply

Closing

This project is more than a tutorial — it’s a pattern for migrating serverless apps into OCI, using Terraform for repeatability and OCI’s cloud-native services for scale and security.

I’ll go deeper into strategies, migration considerations, and live demos during my CloudWorld session on October 15th. 👉 Join me here .

Stay tuned for the GitHub repo drop — and let me know if you try the pipeline. 🚀