Skip to content

Deploy the image service

Same shape as every recipe in Phase-1 and Phase-2. Terraform composes the E1 baseline; each provider adds only what the image service actually needs — which is often nothing more than IAM.

Official docs verified 2026-08-08

Layout

examples/image/
  service/                        # per-cloud text→image, one signature
    generate.py                   # dispatch on CHIRON_PROVIDER
    main.py                       # FastAPI /image  (POST { prompt } -> PNG)
    Dockerfile
    requirements.txt
  chart/                          # Helm chart same shape as E2 + image env
    Chart.yaml
    values.yaml
    templates/
      deployment.yaml
      service.yaml
      serviceaccount.yaml
  terraform/
    azure/  (module "baseline" + IAM/deployment for image model)
    gcp/    (module "baseline" + service account bound to aiplatform.user)
    aws/    (module "baseline" + IAM policy allowing bedrock:InvokeModel on Nova Canvas)

Terraform — compose, add only IAM

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

# Reuse the Azure OpenAI account you already have (created in E2 by
# the shared portal). The image-model deployment is declared here so
# the recipe is one `terraform apply` from a working image service.
resource "azurerm_cognitive_deployment" "image" {
  name                 = var.image_deployment_name        # e.g. "gpt-image-1"
  cognitive_account_id = var.azure_openai_account_id       # from your existing OpenAI account

  model {
    format  = "OpenAI"
    name    = var.image_model_name                         # "gpt-image-1"
    version = var.image_model_version                      # e.g. "2024-10-01"
  }

  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 Imagen uses the standard aiplatform.user role — same as chat.
resource "google_service_account" "image" {
  account_id   = "${var.name}-image"
  display_name = "Chiron image service"
}

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

data "aws_iam_policy_document" "nova_canvas_invoke" {
  statement {
    effect  = "Allow"
    actions = ["bedrock:InvokeModel"]
    resources = [
      "arn:aws:bedrock:*::foundation-model/amazon.nova-canvas-v1:0",
    ]
  }
}

resource "aws_iam_policy" "nova_canvas_invoke" {
  name   = "${var.name}-nova-canvas-invoke"
  policy = data.aws_iam_policy_document.nova_canvas_invoke.json
}

Helm — one small env block

Only the image-model IDs need env — every other lever (chat, embed, TTS) is unchanged from E2/P2.1.

helm upgrade --install image ./examples/image/chart \
  --set image.repository="…/image" --set image.tag="v1" \
  --set env.CHIRON_PROVIDER="azure" \
  --set env.AZURE_IMAGE_DEPLOYMENT="gpt-image-1"

Verify (validate-only)

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

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

python -m compileall examples/image/service

Live image generation needs real cloud accounts + model access; Phase 3 covers billed apply.