spice-terraform

Manage Spice.ai Cloud infrastructure with Terraform or OpenTofu. Use when writing Terraform configs for Spice apps, deployments, secrets, members, or when importing existing Spice.ai resources into Terraform state.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "spice-terraform" with this command: npx skills add spiceai/skills/spiceai-skills-spice-terraform

Spice.ai Terraform Provider

Manage Spice.ai Cloud resources as infrastructure-as-code using the spiceai/spiceai Terraform provider. Supports apps, deployments, secrets, org members, and data sources for regions and runtime images.

Provider Setup

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

provider "spiceai" {
  client_id     = var.spiceai_client_id
  client_secret = var.spiceai_client_secret
  # api_endpoint = "https://api.spice.ai"  # optional, this is the default
}

Requirements: Terraform >= 1.0 or OpenTofu >= 1.0

Authentication

The provider uses OAuth 2.0 Client Credentials. Set credentials via:

  1. Provider blockclient_id and client_secret arguments
  2. Environment variablesSPICEAI_CLIENT_ID and SPICEAI_CLIENT_SECRET

Create OAuth clients in the Spice.ai Portal: Settings > OAuth Clients.

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

Resources

spiceai_app

Manages a Spice.ai application.

data "spiceai_regions" "available" {}

resource "spiceai_app" "analytics" {
  name        = "analytics-app"
  cname       = data.spiceai_regions.available.default
  description = "Production analytics"
  visibility  = "private"
  replicas    = 2
  image_tag   = "1.5.0-models"

  spicepod = yamlencode({
    version = "v1"
    kind    = "Spicepod"
    name    = "analytics-app"
    datasets = [{
      from = "postgres:public.events"
      name = "events"
      params = {
        pg_host = "db.example.com"
        pg_user = "$${secrets:PG_USER}"
        pg_pass = "$${secrets:PG_PASS}"
      }
      acceleration = {
        enabled              = true
        engine               = "duckdb"
        refresh_check_interval = "5m"
      }
    }]
  })
}

Arguments:

ArgumentTypeRequiredNotes
namestringYesMin 4 chars, alphanumeric + hyphens
cnamestringYesRegion identifier (from spiceai_regions)
descriptionstringNo
visibilitystringNopublic or private (default: private)
spicepodstringNoYAML or JSON spicepod configuration
image_tagstringNoSpice runtime version tag
replicasnumberNo1-10
regionstringNoAWS region code
production_branchstringNoGit branch for production deployments
node_groupstringNoKubernetes node group
storage_claim_size_gbnumberNoPersistent volume size in GB

Read-only attributes: id, api_key (sensitive), created_at

spiceai_deployment

Creates an immutable deployment for an app. Use triggers to auto-redeploy on config changes.

resource "spiceai_deployment" "current" {
  app_id         = spiceai_app.analytics.id
  commit_message = "Deploy analytics app"

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

Arguments:

ArgumentTypeRequiredNotes
app_idstringYesApp ID to deploy
image_tagstringNoOverride runtime image
replicasnumberNoOverride replicas (1-10)
debugbooleanNoEnable debug mode (default: false)
branchstringNoGit branch for tracking
commit_shastringNoGit commit SHA
commit_messagestringNoDeployment description

Read-only attributes: id, status (queued | in_progress | succeeded | failed), error_message, created_at

The triggers map causes Terraform to replace the deployment whenever any tracked value changes.

spiceai_secret

Manages app secrets. Values are AES-256 encrypted at rest.

variable "secrets" {
  type      = map(string)
  sensitive = true
  default = {
    PG_USER       = "analytics"
    PG_PASS       = "secret123"
    OPENAI_API_KEY = "sk-..."
  }
}

resource "spiceai_secret" "app_secrets" {
  for_each = var.secrets

  app_id = spiceai_app.analytics.id
  name   = each.key
  value  = each.value
}

Arguments:

ArgumentTypeRequiredNotes
app_idstringYes
namestringYesStart with letter/underscore, alphanumeric + _
valuestringYesSensitive. Must set manually after import (API masks values)

Read-only attributes: id

spiceai_member

Manages organization members by GitHub username.

variable "team" {
  type    = set(string)
  default = ["alice", "bob"]
}

resource "spiceai_member" "team" {
  for_each = var.team

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

Arguments:

ArgumentTypeRequiredNotes
usernamestringYesGitHub username
rolesstring[]NoDefault: ["member"]. Options: owner, member, billing

Read-only attributes: user_id

Organization owners cannot be managed via Terraform.

Data Sources

spiceai_regions

data "spiceai_regions" "available" {}

# Use: data.spiceai_regions.available.default
# Use: data.spiceai_regions.available.regions[*].cname

Returns regions list (each with name, region, cname, provider, providerName) and default region.

spiceai_container_images

data "spiceai_container_images" "stable" {
  channel = "stable"  # or "enterprise"
}

# Use: data.spiceai_container_images.stable.default
# Use: data.spiceai_container_images.stable.images[*].tag

Returns images list (each with name, tag, channel) and default tag.

spiceai_app (data)

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

Retrieves details of an existing app by ID.

spiceai_apps

data "spiceai_apps" "all" {}

# Filter in Terraform
locals {
  prod_apps = [for app in data.spiceai_apps.all.apps : app if app.visibility == "private"]
}

Lists all apps in the organization.

spiceai_members

data "spiceai_members" "all" {}

Lists all organization members with username, roles, is_owner, and user_id.

spiceai_secrets

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

Lists secrets for an app. Values are always masked.

Import

Import existing resources into Terraform state:

# App (by app ID)
terraform import spiceai_app.analytics 12345

# Deployment (appId/deploymentId)
terraform import spiceai_deployment.current 12345/67890

# Secret (appId/SECRET_NAME)
terraform import spiceai_secret.db_password 12345/DB_PASSWORD

# Member (by user ID)
terraform import spiceai_member.alice 789

# Member with for_each
terraform import 'spiceai_member.team["alice"]' 789

After importing spiceai_secret, manually set the value in your config — the API never returns plain-text values.

Complete Example

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

provider "spiceai" {}

# Look up available regions and runtime versions
data "spiceai_regions" "available" {}
data "spiceai_container_images" "stable" {
  channel = "stable"
}

# Create the app
resource "spiceai_app" "myapp" {
  name      = "my-analytics"
  cname     = data.spiceai_regions.available.default
  image_tag = data.spiceai_container_images.stable.default

  spicepod = yamlencode({
    version  = "v1"
    kind     = "Spicepod"
    name     = "my-analytics"
    datasets = [{
      from = "postgres:public.events"
      name = "events"
      params = {
        pg_host = "db.example.com"
        pg_user = "$${secrets:PG_USER}"
        pg_pass = "$${secrets:PG_PASS}"
      }
    }]
    models = [{
      from = "openai:gpt-4o"
      name = "assistant"
      params = {
        openai_api_key = "$${secrets:OPENAI_API_KEY}"
      }
    }]
  })
}

# Manage secrets
variable "app_secrets" {
  type      = map(string)
  sensitive = true
}

resource "spiceai_secret" "secrets" {
  for_each = var.app_secrets
  app_id   = spiceai_app.myapp.id
  name     = each.key
  value    = each.value
}

# Deploy (redeploys when app config changes)
resource "spiceai_deployment" "current" {
  app_id         = spiceai_app.myapp.id
  commit_message = "Managed by Terraform"

  triggers = {
    spicepod  = spiceai_app.myapp.spicepod
    image_tag = spiceai_app.myapp.image_tag
  }

  depends_on = [spiceai_secret.secrets]
}

# Team access
resource "spiceai_member" "team" {
  for_each = toset(["alice", "bob"])
  username = each.key
}

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

output "api_key" {
  value     = spiceai_app.myapp.api_key
  sensitive = true
}

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

Present Results to User

When generating Terraform configurations:

  • Use data.spiceai_regions for region lookup instead of hardcoding
  • Use data.spiceai_container_images for image tags instead of hardcoding
  • Always use for_each for multiple secrets or members
  • Mark secret values and API keys as sensitive
  • Include triggers on deployments to auto-redeploy on changes
  • Use depends_on to ensure secrets exist before deploying
  • Reference secrets in spicepod with $${secrets:NAME} (double $ for Terraform escaping)

Troubleshooting

IssueSolution
401 or auth errorsVerify SPICEAI_CLIENT_ID / SPICEAI_CLIENT_SECRET; check OAuth client scope
Import loses secret valueExpected — set value in config after import; API always masks values
Deployment stuck in queuedCheck app has a valid spicepod; verify image_tag exists
409 on deploymentPrevious deployment still in progress; wait or check status
Spicepod YAML syntax errorsUse yamlencode() for type safety; validate YAML before applying
$$ showing in spicepodUse $${secrets:NAME} in Terraform — the double $ escapes to single $
Cannot delete org ownerOrganization owners cannot be managed by Terraform; remove manually
Provider not foundCheck source = "spiceai/spiceai" and run terraform init

Documentation

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

spice-data-connector

No summary provided by upstream source.

Repository SourceNeeds Review
General

spice-models

No summary provided by upstream source.

Repository SourceNeeds Review
General

spicepod-config

No summary provided by upstream source.

Repository SourceNeeds Review
General

spice-accelerators

No summary provided by upstream source.

Repository SourceNeeds Review