Terraform Provider

Manage Spice.ai resources with Terraform

The Spice.ai Terraform Provider enables infrastructure-as-code management of your Spice.ai Cloud resources.

Quick Start

terraform {
  required_providers {
    spiceai = {
      source  = "spiceai/spiceai"
      version = "~> 0.1"
    }
  }
}

provider "spiceai" {}

resource "spiceai_app" "app" {
  name  = "my-spicecloud-app"
  cname = "us-west-2-prod-aws-data"
}

resource "spiceai_deployment" "deploy" {
  app_id = spiceai_app.app.id
}

Authentication

The provider authenticates using OAuth 2.0 Client Credentials. Create an OAuth client in the Spice.ai Portal under Settings → OAuth Clients.

Set credentials via environment variables (recommended):

export SPICEAI_CLIENT_ID="your-client-id"
export SPICEAI_CLIENT_SECRET="your-client-secret"
terraform plan

Or configure directly in the provider block:

provider "spiceai" {
  client_id     = var.spiceai_client_id
  client_secret = var.spiceai_client_secret
}

Environment Variables

VariableDescription
SPICEAI_CLIENT_IDOAuth client ID
SPICEAI_CLIENT_SECRETOAuth client secret
SPICEAI_API_ENDPOINTAPI endpoint (default: https://api.spice.ai)
SPICEAI_OAUTH_ENDPOINTOAuth token endpoint (default: https://spice.ai/api/oauth/token)

Resources

spiceai_app

Manages a Spice.ai project.

resource "spiceai_app" "app" {
  name        = "my-spicecloud-app"
  cname       = "us-west-2-prod-aws-data"
  description = "Managed by Terraform"
  visibility  = "private"

  spicepod = <<-YAML
    version: v1
    kind: Spicepod
    name: my-spicecloud-app
    datasets:
      - name: taxi_trips
        from: s3://spiceai-demo-datasets/taxi_trips/2024/
        params:
          file_format: parquet
  YAML

  image_tag             = "latest"
  replicas              = 2
  region                = "us-east-2"
  production_branch     = "main"
  storage_claim_size_gb = 10.0
}

Required arguments:

ArgumentTypeDescription
namestringProject name: 4–38 characters, letters, numbers, and hyphens only. Forces replacement on change.
cnamestringRegion identifier. Get values from the spiceai_regions data source. Forces replacement on change.

Optional arguments:

ArgumentTypeDescription
descriptionstringProject description
visibilitystringpublic or private (default: private)
spicepodstringSpicepod configuration (YAML or JSON string)
image_tagstringSpice runtime image tag (e.g., latest, v0.18.0)
imagestringImage name for the spiced container
registrystringRegistry for the spiced image
replicasnumberNumber of replicas (0–10)
regionstringDeployment region
node_groupstringNode group for deployment
storage_claim_size_gbnumberPersistent volume size in GB
production_branchstringGit branch for production deployments
update_channelstringUpdate channel: stable, nightly, internal, internal-sandbox
tagsmap(string)Key-value tags for the project

Read-only attributes:

AttributeDescription
idProject ID
api_keyPrimary API key (sensitive)
cluster_idCluster identifier
created_atTimestamp when the project was created

The spicepod can also be provided as JSON or loaded from a template file:

# Using JSON
resource "spiceai_app" "json_config" {
  name  = "my-json-app"
  cname = "us-west-2-prod-aws-data"

  spicepod = jsonencode({
    version = "v1"
    kind    = "Spicepod"
    name    = "my-json-app"
    datasets = [{
      name = "my_dataset"
      from = "postgres://mydb/table"
    }]
  })
}

# Using a template file
resource "spiceai_app" "templated" {
  name  = "my-app"
  cname = "us-west-2-prod-aws-data"

  spicepod = templatefile("${path.module}/spicepod.yaml.tftpl", {
    app_name = "my-app"
  })
}

spiceai_deployment

Creates a deployment for a Spice.ai project. Deployments are immutable — any changes create a new deployment.

# Basic deployment using project defaults
resource "spiceai_deployment" "deploy" {
  app_id = spiceai_app.app.id
}

# Deployment with overrides
resource "spiceai_deployment" "production" {
  app_id = spiceai_app.app.id

  image_tag      = "v0.18.0"
  replicas       = 3
  debug          = false
  branch         = "main"
  commit_sha     = "abc123def456789"
  commit_message = "Production release"
}

Required arguments:

ArgumentTypeDescription
app_idstringThe project ID to deploy

Optional arguments:

ArgumentTypeDescription
image_tagstringOverride the Spice runtime image tag
replicasnumberOverride the number of replicas (0–10)
debugbooleanEnable debug mode
branchstringGit branch name
commit_shastringGit commit SHA
commit_messagestringGit commit message
triggersmap(string)Map of values that force a new deployment when changed

Read-only attributes:

AttributeDescription
idDeployment ID
statusStatus: queued, in_progress, succeeded, failed, created
created_atTimestamp when the deployment was created
started_atTimestamp when the deployment started running
finished_atTimestamp when the deployment finished
error_messageCatalog text when the deployment failed, or when status is in_progress and Cloud is retrying a scheduling block

Use triggers to automatically redeploy when project configuration changes:

resource "spiceai_deployment" "auto" {
  app_id = spiceai_app.app.id

  triggers = {
    spicepod  = spiceai_app.app.spicepod
    image_tag = spiceai_app.app.image_tag
    replicas  = spiceai_app.app.replicas
  }
}

spiceai_secret

Manages secrets for a Spice.ai project. Secret values are encrypted at rest.

resource "spiceai_secret" "database_password" {
  app_id = spiceai_app.app.id
  name   = "DATABASE_PASSWORD"
  value  = var.database_password
}

Required arguments:

ArgumentTypeDescription
app_idnumberThe project ID
namestringSecret name. Forces replacement on change.
valuestringSecret value (sensitive)

Read-only attributes:

AttributeDescription
idSecret ID
created_atTimestamp when the secret was created
updated_atTimestamp when the secret was updated

spiceai_member

Manages organization members.

resource "spiceai_member" "developer" {
  username = "johndoe"
  roles    = ["member"]
}

resource "spiceai_member" "admin" {
  username = "janedoe"
  roles    = ["admin", "member"]
}

Required arguments:

ArgumentTypeDescription
usernamestringUsername of the user. Forces replacement on change.

Optional arguments:

ArgumentTypeDescription
roleslist(string)Roles to assign (admin, member)

Read-only attributes:

AttributeDescription
idMember ID (same as user_id)
user_idUser ID
is_ownerWhether the member is the organization owner
created_atTimestamp when the member was added

Data Sources

spiceai_regions

Lists available deployment regions.

data "spiceai_regions" "available" {}

# Use in project resource
resource "spiceai_app" "app" {
  name  = "my-spicecloud-app"
  cname = data.spiceai_regions.available.regions[0].cname
}
ArgumentTypeDescription
envstringOptional. Filter by environment: prod or dev
AttributeDescription
defaultDefault region identifier
regionsList of region objects with cname, region, name, provider, provider_name, is_default, disabled

spiceai_container_images

Lists available Spice runtime container images.

data "spiceai_container_images" "stable" {
  channel = "stable"
}

resource "spiceai_app" "app" {
  name      = "my-spicecloud-app"
  cname     = "us-west-2-prod-aws-data"
  image_tag = data.spiceai_container_images.stable.default
}
ArgumentTypeDescription
channelstringOptional. Release channel: stable or enterprise (default: stable)
AttributeDescription
defaultDefault image tag
imagesList of image objects with tag, name, channel

spiceai_api_keys

Retrieves the API keys for a project. Each project has two API keys to support key rotation.

data "spiceai_api_keys" "keys" {
  app_id = spiceai_app.app.id
}

output "primary_api_key" {
  value     = data.spiceai_api_keys.keys.api_key
  sensitive = true
}
ArgumentTypeDescription
app_idstringRequired. The project ID
AttributeDescription
api_keyPrimary API key (sensitive)
api_key_2Secondary API key (sensitive)

spiceai_app

Gets details about an existing project by ID.

data "spiceai_app" "existing" {
  id = "12345"
}

output "app_name" {
  value = data.spiceai_app.existing.name
}

spiceai_apps

Lists all projects in the organization.

data "spiceai_apps" "all" {}

output "app_names" {
  value = [for app in data.spiceai_apps.all.apps : app.name]
}

spiceai_members

Lists all organization members.

data "spiceai_members" "all" {}

output "member_usernames" {
  value = [for m in data.spiceai_members.all.members : m.username]
}

spiceai_secrets

Lists secrets for a project (values are masked).

data "spiceai_secrets" "app_secrets" {
  app_id = spiceai_app.app.id
}

output "secret_names" {
  value = [for s in data.spiceai_secrets.app_secrets.secrets : s.name]
}

Import

Import existing resources into Terraform state:

# Import a project by ID
terraform import spiceai_app.example 12345

# Import a deployment (app_id/deployment_id)
terraform import spiceai_deployment.example 12345/67890

# Import a secret (app_id/secret_name)
terraform import spiceai_secret.example 123/DATABASE_PASSWORD

# Import a member by user ID
terraform import spiceai_member.example 123

Complete Example

terraform {
  required_providers {
    spiceai = {
      source  = "spiceai/spiceai"
      version = "~> 0.1"
    }
  }
}

provider "spiceai" {}

# Look up available regions and images
data "spiceai_regions" "available" {}

data "spiceai_container_images" "stable" {
  channel = "stable"
}

# Create the project
resource "spiceai_app" "production" {
  name        = "my-production-app"
  description = "Production analytics project"
  visibility  = "private"
  cname       = data.spiceai_regions.available.regions[0].cname

  spicepod = <<-YAML
    version: v1
    kind: Spicepod
    name: my-production-app
    datasets:
      - name: taxi_trips
        from: s3://spiceai-demo-datasets/taxi_trips/2024/
        params:
          file_format: parquet
  YAML

  image_tag         = data.spiceai_container_images.stable.default
  replicas          = 2
  production_branch = "main"
}

# Configure secrets
resource "spiceai_secret" "database_password" {
  app_id = spiceai_app.production.id
  name   = "DATABASE_PASSWORD"
  value  = var.database_password
}

# Deploy with auto-trigger on config changes
resource "spiceai_deployment" "production" {
  app_id = spiceai_app.production.id

  triggers = {
    spicepod  = spiceai_app.production.spicepod
    image_tag = spiceai_app.production.image_tag
    replicas  = spiceai_app.production.replicas
  }
}

# Add team members
resource "spiceai_member" "team" {
  for_each = toset(["alice", "bob", "charlie"])

  username = each.key
  roles    = ["member"]
}

# Variables
variable "database_password" {
  type      = string
  sensitive = true
}

# Outputs
output "app_id" {
  value = spiceai_app.production.id
}

output "app_api_key" {
  value     = spiceai_app.production.api_key
  sensitive = true
}

output "deployment_status" {
  value = spiceai_deployment.production.status
}

See also: