Skip to content

Deploy the video service

Same shape as every recipe. Terraform composes the E1 baseline; each provider adds only what video actually needs. Nova Reel is the only path with new infra (an S3 bucket for the async output).

Official docs verified 2026-08-08

Layout

examples/video/
  service/                        # per-cloud async submit/poll/fetch, one signature
    video.py                      # dispatch on CHIRON_PROVIDER
    main.py                       # FastAPI: POST /video, GET /video/{id}, GET /video/{id}/content
    Dockerfile
    requirements.txt
  chart/                          # Helm chart same shape as image/voice
    Chart.yaml
    values.yaml
    templates/{deployment,service,serviceaccount}.yaml
  terraform/
    azure/  (module "baseline" + optional azurerm_cognitive_deployment for sora-2)
    gcp/    (module "baseline" + SA + aiplatform.user)
    aws/    (module "baseline" + S3 output bucket + bedrock:StartAsyncInvoke policy)

Terraform — compose, add only what video needs

module "baseline" {
  source  = "../../../foundations/azure"
  name    = var.name
  region  = var.region
  compute = var.compute
}

# Attach a Sora deployment to an existing Azure OpenAI account.
# Skipped when var.azure_openai_account_id is empty (validate-only
# default; the module stays green without pre-existing OpenAI infra).
resource "azurerm_cognitive_deployment" "video" {
  count                = var.azure_openai_account_id != "" ? 1 : 0
  name                 = var.video_deployment_name     # e.g. "sora-2"
  cognitive_account_id = var.azure_openai_account_id

  model {
    format  = "OpenAI"
    name    = var.video_model_name                     # "sora-2"
    version = var.video_model_version                  # e.g. "2025-09-30"
  }

  sku {
    name     = "GlobalStandard"
    capacity = 1
  }
}
module "baseline" {
  source     = "../../../foundations/gcp"
  project_id = var.project_id
  name       = var.name
  region     = var.region
  compute    = var.compute
}

# Vertex Veo uses roles/aiplatform.user — one SA + one binding.
resource "google_service_account" "video" {
  account_id   = "${var.name}-video"
  display_name = "Chiron video service"
}

resource "google_project_iam_member" "video_aiplatform_user" {
  project = var.project_id
  role    = "roles/aiplatform.user"
  member  = "serviceAccount:${google_service_account.video.email}"
}
module "baseline" {
  source  = "../../../foundations/aws"
  name    = var.name
  region  = var.region
  compute = var.compute
}

# Nova Reel writes output MP4s here.
resource "aws_s3_bucket" "video" {
  bucket        = "${var.name}-video-${data.aws_caller_identity.current.account_id}"
  force_destroy = true
}

resource "aws_s3_bucket_public_access_block" "video" {
  bucket                  = aws_s3_bucket.video.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# IAM for the compute role: start async invokes + read the output MP4.
data "aws_iam_policy_document" "nova_reel" {
  statement {
    effect  = "Allow"
    actions = [
      "bedrock:StartAsyncInvoke",
      "bedrock:GetAsyncInvoke",
      "bedrock:ListAsyncInvokes",
    ]
    resources = ["*"]
  }
  statement {
    effect  = "Allow"
    actions = ["bedrock:InvokeModel"]
    resources = [
      "arn:aws:bedrock:*::foundation-model/amazon.nova-reel-v1:1",
    ]
  }
  statement {
    effect    = "Allow"
    actions   = ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"]
    resources = ["${aws_s3_bucket.video.arn}/*"]
  }
}

resource "aws_iam_policy" "nova_reel" {
  name   = "${var.name}-nova-reel"
  policy = data.aws_iam_policy_document.nova_reel.json
}

Verify (validate-only)

for p in azure gcp aws; do
  ( cd examples/video/terraform/$p && terraform init -backend=false && terraform validate )
done

helm lint examples/video/chart
helm template video examples/video/chart > /dev/null

python -m compileall examples/video/service

Live video generation needs real accounts + model access and typically costs a few dollars per job — Phase 3 handles billed apply.