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
- Bedrock Nova Reel requires an S3 output bucket per invocation — docs.aws.amazon.com/nova/…/video-gen-code-examples
azurerm_cognitive_deploymentfor Sora (attached to an existing Azure OpenAI account): registry.terraform.io/…/azurerm/latest/docs/resources/cognitive_deployment- Vertex Veo uses
roles/aiplatform.user(same role as Vertex chat + image; no per-model resource).
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.